如何启用 .Net web-API 以接受 g 压缩的帖子 [英] How do enable a .Net web-API to accept g-ziped posts

查看:21
本文介绍了如何启用 .Net web-API 以接受 g 压缩的帖子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相当标准的 .net MVC 4 Web API 应用程序.

I have a fairly bog standard .net MVC 4 Web API application.

 public class LogsController : ApiController
{

    public HttpResponseMessage PostLog(List<LogDto> logs)
    {
        if (logs != null && logs.Any())
        {
            var goodLogs = new List<Log>();
            var badLogs = new List<LogBad>();

            foreach (var logDto in logs)
            {
                if (logDto.IsValid())
                {
                    goodLogs.Add(logDto.ToLog());
                }
                else
                {
                    badLogs.Add(logDto.ToLogBad());
                }
            }

            if (goodLogs.Any())
            {
                _logsRepo.Save(goodLogs);
            }

            if(badLogs.Any())
            {
                _logsBadRepo.Save(badLogs);
            }


        }
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

这一切都很好,我的设备可以向我发送他们的日志并且运行良好.但是现在我们开始担心传输的数据的大小,我们想看看接受使用 GZIP 压缩的帖子吗?

This all work fine, I have devices that are able to send me their logs and it works well. However now we are starting to have concerns about the size of the data being transferred, and we want to have a look at accepting post that have been compressed using GZIP?

我该怎么做?是在 IIS 中设置还是我可以使用操作过滤器?

How would I go about do this? Is it setting in IIS or could I user Action Filters?

编辑 1

根据 Filip 的回答,我的想法是我需要在请求到达我的控制器之前拦截请求的处理.如果我可以在 Web api 框架尝试将请求正文解析为我的业务对象之前捕获请求,这将失败,因为请求正文仍然被压缩.然后我可以解压缩请求的主体,然后将请求传递回处理链,希望 Web Api 框架能够将(解压缩的)主体解析为我的业务对象.

Following up from Filip's answer my thinking is that I need to intercept the processing of the request before it gets to my controller. If i can catch the request before the Web api framework attempts to parse the body of the request into my business object, which fails because the body of the request is still compressed. Then I can decompress the body of the request and then pass the request back into the processing chain, and hopefully the Web Api framework will be able to parse the (decompressed) body into my business objects.

看起来像使用 DelagatingHandler 是要走的路.它允许我在处理过程中访问请求,但在我的控制器之前.所以我尝试了以下方法?

It looks Like using the DelagatingHandler is the way to go. It allows me access to the request during the processing, but before my controller. So I tried the following?

 public class gZipHandler : DelegatingHandler
{

    protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        string encodingType = request.Headers.AcceptEncoding.First().Value;

        request.Content = new DeCompressedContent(request.Content, encodingType);

        return base.SendAsync(request, cancellationToken);
    }
}

public class DeCompressedContent : HttpContent
{
    private HttpContent originalContent;
    private string encodingType;

    public DeCompressedContent(HttpContent content, string encodType)
    {
        originalContent = content;
        encodingType = encodType;
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;

        return false;
    }


    protected override Task<Stream> CreateContentReadStreamAsync()
    {
        return base.CreateContentReadStreamAsync();
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        Stream compressedStream = null;

        if (encodingType == "gzip")
        {
            compressedStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true);
        }

        return originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
        {
            if (compressedStream != null)
            {
                compressedStream.Dispose();
            }
        });
    }



}

}

这似乎工作正常.在我的控制器和 DecompressedContent 的构造函数被调用之前调用 SendAsync 方法.然而 SerializeToStreamAsync 从未被调用,所以我添加了 CreateContentReadStreamAsync 以查看解压缩是否应该发生,但也没有被调用.

This seems to be working ok. The SendAsync method is being called before my controller and the constructor for the DecompressedContent is being called. However the SerializeToStreamAsync is never being called so I added the CreateContentReadStreamAsync to see if that's where the decompressing should be happening, but that's not being called either.

我觉得我已经接近解决方案了,但只需要一点额外的东西就可以解决问题.

I fell like I am close to the solution, but just need a little bit extra to get it over the line.

推荐答案

我有同样的要求,将 gzipped 数据 POST 到 .NET web api 控制器.我想出了这个解决方案:

I had the same requirement to POST gzipped data to a .NET web api controller. I came up with this solution:

public class GZipToJsonHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                           CancellationToken cancellationToken)
    {
        // Handle only if content type is 'application/gzip'
        if (request.Content.Headers.ContentType == null ||
            request.Content.Headers.ContentType.MediaType != "application/gzip")
        {
            return base.SendAsync(request, cancellationToken);
        }

        // Read in the input stream, then decompress in to the outputstream.
        // Doing this asynronously, but not really required at this point
        // since we end up waiting on it right after this.
        Stream outputStream = new MemoryStream();
        Task task = request.Content.ReadAsStreamAsync().ContinueWith(t =>
            {
                Stream inputStream = t.Result;
                var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress);

                gzipStream.CopyTo(outputStream);
                gzipStream.Dispose();

                outputStream.Seek(0, SeekOrigin.Begin);
            });

        // Wait for inputstream and decompression to complete. Would be nice
        // to not block here and work async when ready instead, but I couldn't 
        // figure out how to do it in context of a DelegatingHandler.
        task.Wait();

        // This next section is the key...

        // Save the original content
        HttpContent origContent = request.Content;

        // Replace request content with the newly decompressed stream
        request.Content = new StreamContent(outputStream);

        // Copy all headers from original content in to new one
        foreach (var header in origContent.Headers)
        {
            request.Content.Headers.Add(header.Key, header.Value);
        }

        // Replace the original content-type with content type
        // of decompressed data. In our case, we can assume application/json. A
        // more generic and reuseable handler would need some other 
        // way to differentiate the decompressed content type.
        request.Content.Headers.Remove("Content-Type");
        request.Content.Headers.Add("Content-Type", "application/json");

        return base.SendAsync(request, cancellationToken);
    }
}

使用这种方法,通常使用 JSON 内容和自动模型绑定的现有控制器可以继续工作,无需任何更改.

Using this approach, the existing controller, which normally works with JSON content and automatic model binding, continued to work without any changes.

我不确定为什么接受了另一个答案.它提供了一种处理响应(常见)的解决方案,但不提供处理请求(不常见)的解决方案.Accept-Encoding 头用于指定可接受的响应编码,与请求编码无关.

I'm not sure why the other answer was accepted. It provides a solution for handling the responses (which is common), but not requests (which is uncommon). The Accept-Encoding header is used to specify acceptable response encodings, and is not related to request encodings.

这篇关于如何启用 .Net web-API 以接受 g 压缩的帖子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆