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



我有一个自托管的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 });
    }

我想参数绑定可能已经在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头来向处理程序指示您正在接收的内容已被压缩;与发送压缩响应时发生的情况大致相同(由系统管理,而不是手动管理)。

最新更新