Подтвердить что ты не робот

Содержимое веб-приложения Api Content пусто в действии

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

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 вместо моей модели или что-то в этом роде.

4b9b3361

Ответ 1

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

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

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
            }
        }
    }

Ответ 2

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

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-представление моего объекта параметра действия, запускающего журнал или исключение.

Найдено как принятый ответ здесь