DelegatingHandler用于解压缩WebApi中的传入请求



在我的应用程序中,我们从客户端发送大小适中的数据包,并收到大小适中的响应,所以我想在向上和返回的过程中实现一些压缩。

在返回的过程中,这很好,因为我可以依靠IIS的动态压缩来为我做到这一点,但在返回的途中,我发现了以下问题。

我有一个委派处理程序,它坐在那里对传入的请求进行解压缩:(此代码大部分基于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);
    }
}

减压效果很好,我有我的要求。用一个结果填充的内容是我的解压缩JSON。

当我把它传给基地的时候。SendAsync,它击中了我的控制器方法,模型为null,而在我实现压缩之前,它运行得很好。

我读到,当你读取即将到来的内容流时,这是一件一次性的事情,但我认为将request.content设置为解压缩的结果应该让它再次被读取吗?

我解决了这个问题。

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

在客户端中完成这些操作后,一切都按计划进行。

最新更新