自托管WebApi服务中的请求解压缩

时间:2016-03-29 14:48:51

标签: c# .net rest asp.net-web-api compression

我有一个自托管的WebApi。 我的客户端正在发送一个GZip压缩流,我有一个属性,它在ActionFilterAttribute的OnActionExecuting覆盖中解压缩流。

但是我在控制器中的Details对象始终为null。

public class DatabaseAPIController : ApiController
{    
    [Decompression]
    public bool PostDetails([FromBody] IEnumerable<Detail> Details)
    {
         // The Details object is null.

         // This writes out the correct JSON which has been decompressed which proves my decompression method works.
         Console.WriteLine(Request.Content.ReadAsStringAsync().Result);  

         // Do other work. 

         return true;
    }
}   

这是解压缩属性。

public class DecompressionAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var content = actionContext.Request.Content;
        var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
        var decompressedContent = bytes == null ? new byte[0] : CompressionHelper.Decompress(bytes);
        actionContext.Request.Content.Headers.Clear();
        actionContext.Request.Content = new StringContent(Encoding.UTF8.GetString(decompressedContent), Encoding.UTF8, "application/json");
        base.OnActionExecuting(actionContext);
    }
}

如果我从客户端删除压缩和从服务器解压缩属性,那么一切正常。

我无法弄清楚为什么我的解压缩JSON不会填充PostDetails方法中的Details对象。

完整答案

public class DecompressionHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {

        var content = request.Content;
        var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
        var decompressedContent = bytes == null ? new byte[0] : CompressionHelper.Decompress(bytes);
        request.Content.Headers.Clear();
        request.Content = new StringContent(Encoding.UTF8.GetString(decompressedContent), Encoding.UTF8, "application/json");
        request.Content.Headers.Add("Content-Length", decompressedContent.Length.ToString());

        var response = await base.SendAsync(request, cancellationToken);

        return response;
    }
}

我在自托管设置的构造函数中注册了我的处理程序。

public partial class MainWebAPI : ServiceBase
{
    private HttpSelfHostServer _server;
    private readonly HttpSelfHostConfiguration _config;
    public const string ServiceAddress = "http://localhost:2345";

    public MainWebAPI()
    {
        InitializeComponent();

        _config = new HttpSelfHostConfiguration(ServiceAddress);
        _config.MessageHandlers.Add(new DecompressionHandler());
        _config.Routes.MapHttpRoute("DefaultApi",
            "api/{controller}/{id}",
            new { id = RouteParameter.Optional });

    }

1 个答案:

答案 0 :(得分:1)

我猜可能在OnActionExecuting期间已经发生了参数绑定,所以无论你在做什么,模型绑定器都不会看到它。

我认为你应该尝试使用MessageHandler

public class CompressedMessageHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Check for compressed message
        // Change the request
        // Call the inner handler.
        var response = await base.SendAsync(request, cancellationToken);

        return response;
    }
}
  

消息处理程序适用于在HTTP消息级别(而不是控制器操作)上运行的横切关注点。例如,消息处理程序可能会读取或修改请求标头。

这或多或少(内容)你在这里尝试做什么。

您不再需要[Decompression]属性(这很好);您将需要使用客户端的自定义http标头来指示您正在接收的内容被压缩的处理程序;或多或少与发送压缩响应时发生的情况相同(由系统管理,而不是由您手动管理)。