ModelBinder Request.Content.ReadAsStringAsync производительность

У меня есть собственный ModelBinder, который, когда я загружаю тестирование своего приложения и запускаю Ants profiler, идентифицирует чтение Request.Content в виде строки как горячей точки:

 public class QueryModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            var body = actionContext.Request.Content.ReadAsStringAsync().Result;

Есть ли более эффективный способ сделать это? Или я неправильно читаю профилировщик ANTS?

1 ответ

Решение

Насколько большой контент? Обратите внимание, что вы могли бы видеть много времени, потому что вы вызываете этот сетевой вызов синхронно, а не асинхронно.

Вы можете прочитать строку ранее async и спрятать ее в свойстве запроса.

В качестве альтернативы вы можете вместо этого написать средство форматирования, а затем украсить свой параметр с помощью [FromBody].

Рекомендованный подход здесь заключается в использовании FromBody и форматера, поскольку он естественным образом соответствует архитектуре WebAPI:

Для этого вы должны написать средство форматирования медиа-типа:

public class StringFormatter : MediaTypeFormatter
{
    public StringFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/mystring"));
    }

    public override bool CanReadType(Type type)
    {
        return (type == typeof (string));
    }

    public override bool CanWriteType(Type type)
    {
        return false;
    }

    public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger,
        CancellationToken cancellationToken)
    {
        if (!CanReadType(type))
        {
            throw new InvalidOperationException();
        }

        return await content.ReadAsStringAsync();
    }
}

Зарегистрируйте это в webapiconfig.cs

config.Formatters.Add(new StringFormatter());

И потреблять в действии

public string Get([FromBody]string myString)
{
    return myString;
}

Другая конструкция (не так, как рекомендуется из-за связи между фильтром и связующим веществом):

Реализуем модель связующего (это супер наивно):

public class MyStringModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        // this is a Naive comparison of media type
        if (actionContext.Request.Content.Headers.ContentType.MediaType == "application/mystring")
        {
            bindingContext.Model = actionContext.Request.Properties["MyString"] as string;
            return true;
        }

        return false;
    }
}

Добавьте фильтр авторизации (они запускаются перед привязкой модели), чтобы вы могли асинхронно получать доступ к действию. Это также работает с делегирующим обработчиком:

public class MyStringFilter : AuthorizationFilterAttribute
{
    public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        if (actionContext.Request.Content.Headers.ContentType.MediaType == "application/mystring")
        {
            var myString = await actionContext.Request.Content.ReadAsStringAsync();
            actionContext.Request.Properties.Add("MyString", myString);
        }
    }
}

Зарегистрируйте его в WebApi.Config или примените к контроллеру:

WebApiConfig.cs

config.Filters.Add(new MyStringFilter());

ValuesController.cs

[MyStringFilter] // this is optional, you can register it globally as well
public class ValuesController : ApiController
{
    // specifying the type here is optional, but I'm using it because it avoids having to specify the prefix
    public string Get([ModelBinder(typeof(MyStringModelBinder))]string myString = null)
    {
        return myString;
    }
}

(Спасибо за @Kiran Challa за то, что я посмотрел через плечо и предложил фильтр авторизации)

РЕДАКТИРОВАТЬ: Одна вещь, которую всегда нужно помнить, с относительно большими строками (потребляющими более 85 КБ, то есть около 40 КБ символов) может попасть в кучу больших объектов, что приведет к снижению производительности вашего сайта. Если вы считаете, что это достаточно часто, разбейте ввод на что-то вроде строителя строк / массива строк или чего-то подобного без смежной памяти. Посмотрите, почему куча больших объектов и почему мы заботимся?

Другие вопросы по тегам