ModelBinder Request.Content.ReadAsStringAsync性能

时间:2014-05-12 21:01:50

标签: asp.net-web-api performance-testing ants

我有一个自定义的ModelBinder,当我加载测试我的应用程序并运行Ants profiler时,确定将Request.Content读为字符串作为热点:

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

有更有效的方法吗? 或者我是否错误地阅读了ANTS探查器?

enter image description here

1 个答案:

答案 0 :(得分:4)

内容有多大?请注意,您可能会看到很多时间,因为您正在同步而不是异步调用此网络呼叫。

您可以在之前的异步中读取字符串并将其存储在请求属性中。

或者你可以改写格式化程序,然后用[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;
    }
}

添加一个authroization过滤器(它们在模型绑定之前运行),您可以异步访问该操作。这也适用于委托处理程序:

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看了我一眼,并建议授权过滤器)

编辑:有一件事要记住,相对较大的字符串(消耗超过85KB所以大约40K字符)可以进入大对象堆,这将对您的网站性能造成严重破坏。如果你认为这很常见,那么将输入分解为字符串构建器/字符串数组或类似的东西,而不需要连续的内存。见Why Large Object Heap and why do we care?