DelegatingHandler解压缩WebApi中的传入请求 [英] DelegatingHandler to decompress incoming requests in WebApi

查看:89
本文介绍了DelegatingHandler解压缩WebApi中的传入请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用中,我们正在从客户端发送大小合适的数据包,并收到大小合适的响应,因此我想在向上和向后执行一些压缩。

In my app we are sending decent size packets up from a client and receiving a decent sized response, so I want to implement some compression on the way up and the way back.

回来的时候很好,因为我可以依靠IIS的动态压缩为我做这件事,但是在上升的过程中,我发现了以下问题。

On the way back it is fine because I can lean on IIS's dynamic compression to do that for me, but on the way up I am finding the following issue.

我有一个委托处理程序,用于解压缩传入的请求:
(大多数代码基于Fabrik.Common的各个部分( https://github.com/benfoster/Fabrik.Common )))

I have a delegating handler that sits there to decompress the incoming requests: (Most of this code is based on parts of Fabrik.Common (https://github.com/benfoster/Fabrik.Common))

public class DecompressionHandler : DelegatingHandler
{
    public Collection<ICompressor> Compressors;

    public DecompressionHandler()
    {
        Compressors = new Collection<ICompressor> {new GZipCompressor(), new DeflateCompressor()};
    }

    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        if (request.Content.Headers.ContentEncoding.IsntNullOrEmpty() && request.Content != null)
        {
            var encoding = request.Content.Headers.ContentEncoding.First();

            var compressor = Compressors.FirstOrDefault(c => c.EncodingType.Equals(encoding, StringComparison.InvariantCultureIgnoreCase));

            if (compressor != null)
            {
                request.Content = await DecompressContentAsync(request.Content, compressor).ConfigureAwait(true);
            }
        }

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

        return response;
    }

    private static async Task<HttpContent> DecompressContentAsync(HttpContent compressedContent, ICompressor compressor)
    {
        using (compressedContent)
        {
            var decompressed = new MemoryStream();
            await compressor.Decompress(await compressedContent.ReadAsStreamAsync(), decompressed).ConfigureAwait(true);

            // set position back to 0 so it can be read again
            decompressed.Position = 0;

            var newContent = new StreamContent(decompressed);
            // copy content type so we know how to load correct formatter
            newContent.Headers.ContentType = compressedContent.Headers.ContentType;
            return newContent;
        }
    }
}

public class DeflateCompressor : Compressor
{
    private const string DeflateEncoding = "deflate";

    public override string EncodingType
    {
        get { return DeflateEncoding; }
    }

    public override Stream CreateCompressionStream(Stream output)
    {
        return new DeflateStream(output, CompressionMode.Compress, leaveOpen: true);
    }

    public override Stream CreateDecompressionStream(Stream input)
    {
        return new DeflateStream(input, CompressionMode.Decompress, leaveOpen: true);
    }
}
public abstract class Compressor : ICompressor
{
    public abstract string EncodingType { get; }
    public abstract Stream CreateCompressionStream(Stream output);
    public abstract Stream CreateDecompressionStream(Stream input);

    public virtual Task Compress(Stream source, Stream destination)
    {
        var compressed = CreateCompressionStream(destination);

        return Pump(source, compressed)
            .ContinueWith(task => compressed.Dispose());
    }

    public virtual Task Decompress(Stream source, Stream destination)
    {
        var decompressed = CreateDecompressionStream(source);

        return Pump(decompressed, destination)
            .ContinueWith(task => decompressed.Dispose());
    }

    protected virtual Task Pump(Stream input, Stream output)
    {
        return input.CopyToAsync(output);
    }
}

public interface ICompressor
{
    string EncodingType { get; }
    Task Compress(Stream source, Stream destination);
    Task Decompress(Stream source, Stream destination);
}

public class GZipCompressor : Compressor
{
    private const string GZipEncoding = "gzip";

    public override string EncodingType
    {
        get { return GZipEncoding; }
    }

    public override Stream CreateCompressionStream(Stream output)
    {
        return new GZipStream(output, CompressionMode.Compress, leaveOpen: true);
    }

    public override Stream CreateDecompressionStream(Stream input)
    {
        return new GZipStream(input, CompressionMode.Decompress, leaveOpen: true);
    }
}

减压工作正常,我有我的要求。

The decompression works fine and I have my request.Content populated with a result that is my decompressed JSON.

当我将其传递给base.SendAsync并到达我的控制器方法时,该模型为null,而在实现之前

When I pass that off to base.SendAsync and it hits my controller method, the model is null, whereas before I implemented compression it was all working great.

我已经读到,当您阅读进来的内容流时,这是一次性的事情,但是我认为设置request.content

I have read that when you Read the content stream coming in that it is a once off thing, but I thought setting the request.content to the decompressed result should let it be read again?

推荐答案

我解决了这个问题。

这是我的HTTPClient实现中的一个错误,我从使用PostAsJsonAsync移到PostAsync来执行压缩客户端,但是没有添加Content-Type标头来指定application / json。

It was an error in my HTTPClient implementation where I had moved from using PostAsJsonAsync to PostAsync to do the compression client side, but had not added the Content-Type header to specify application/json.

在客户端执行完此操作后,一切都按计划进行。

After doing that in the client, all is working as planned.

这篇关于DelegatingHandler解压缩WebApi中的传入请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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