2014-01-25 2 views
23

У меня есть атрибут Log, который пытается записывать содержимое запроса и ответа в текстовый файл. Я поставил над своим контроллером все действия. В LogAttribute я читаю содержимое как строку (ReadAsStringAsync), поэтому я не теряю тело запроса.Web Api Request Содержимое пусто в действии фильтра

public class LogAttribute : ActionFilterAttribute 
{ 
    // .. 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     // stuff goes here 
     var content = actionContext.Request.Content.ReadAsStringAsync().Result; 
     // content is always empty because request body is cleared 
    } 

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 
    { 
     // other stuff goes here 
     var content = actionContext.Request.Content.ReadAsStringAsync().Result; 
     // content is always empty because request body is cleared 
    } 

    // .. 
} 

С другой стороны, я применил атрибут FromBody перед классом параметров действия, чтобы воспользоваться его преимуществами.

[Log] 
public class SomethingController 
{ 
    public HttpResponseMessage Foo([FromBody] myModel) 
    { 
     // something 
    } 
} 

Проблема заключается в том, что содержимое всегда пустое в ActionExecuting или ActionExecuted.

Я думаю, что это потому, что FromBody работает до моего атрибута Log в отличие от их порядка в коде. И снова я думаю, что это из-за поиска лучшего соответствия action/controller для запроса в соответствии с параметрами действия (Route Processing). После этого тело запроса очищается, так как тело запроса не забуферировано в WebApi.

Я хочу знать, есть ли способ изменить время выполнения атрибута FromBody и атрибут Log? или что-то еще, что решает проблему! Я должен упомянуть, что я не хочу удалять FromBody и использовать HttpRequestMessage вместо моей модели или что-то в этом роде.

+1

рядом это ваше обращение асинхронного неправильно. Использование .Result опасно, вам следует избегать этого, если возможно. Скорее переопределите метод OnActionExecutingAsync, который позволяет вам ждать. –

+0

@ LukasK на самом деле это было написано, чтобы объяснить мой вопрос. но ты прав. –

ответ

25

Тело запроса представляет собой поток без перемотки; его можно читать только один раз. Форматтер уже прочитал поток и заполнил модель. Мы не можем читать поток снова в фильтре действий.

Вы можете попробовать:

public class LogAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     var myModel = actionContext.ActionArguments["myModel"]; 

    } 

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 
    { 
     var myModel = actionContext.ActionArguments["myModel"]; 
    } 
} 

На самом деле, ActionArguments это просто словарь, мы можем цикл, хотя, если нам нужно, чтобы избежать жёстко прописанного имени параметра ("myModel"). Когда мы создаем общий фильтр действий, который должен работать над классом похожих объектов для некоторых конкретных требований, мы могли бы реализовать наши модели интерфейса => знать, какой аргумент является моделью, над которой нам нужно работать, и мы можем вызывать методы, хотя интерфейс.

Пример кода:

public class LogAttribute : ActionFilterAttribute 
    { 
     public override void OnActionExecuting(HttpActionContext actionContext) 
     { 
      foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is ILogable))) 
      { 
       ILogable model = argument as ILogable;//assume that only objects implementing this interface are logable 
       //do something with it. Maybe call model.log 
      } 
     } 

     public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 
     { 
      foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is ILogable))) 
      { 
       ILogable model = argument as ILogable;//assume that only objects implementing this interface are logable 
       //do something with it. Maybe call model.log 
      } 
     } 
    } 
+0

Спасибо, брат. Что делать, если у меня есть несколько параметров, и я не знаю их имени? В этом решении я должен установить контракт, что «всегда мое имя параметра - myModel»? да? –

+0

@Reza Ahmadi: «ActionArguments» - это просто словарь. Вы можете зациклиться. –

+0

@Reza Ahmadi: обычно, если вы хотите применить этот фильтр для определенного действия против класса похожих объектов. Вы могли бы использовать свои модели для реализации интерфейса, когда вы зацикливаете словарь, у вас есть ключ, чтобы указать, что это модель, над которой вам нужно работать. (возможно, методы вызова, хотя интерфейс) –

9

Этот подход работает для меня:

using (var stream = new MemoryStream()) 
{ 
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"]; 
    context.Request.InputStream.Seek(0, SeekOrigin.Begin); 
    context.Request.InputStream.CopyTo(stream); 
    string requestBody = Encoding.UTF8.GetString(stream.ToArray()); 
} 

Возвращается для меня JSon представление моего объекта параметра действия запускающего журналирования или исключение случая.

Найден, как принято отвечать here

Смежные вопросы