zoukankan      html  css  js  c++  java
  • HttpClient与APS.NET Web API:请求内容的压缩与解压

    HttpClient与APS.NET Web API:请求内容的压缩与解压

     

    首先说明一下,这里的压缩与解压不是通常所说的http compression——那是响应内容在服务端压缩、在客户端解压,而这里是请求内容在客户端压缩、在服务端解压。

    对于响应内容的压缩,一般Web服务器(比如IIS)都提供了内置支持,只需在请求头中包含 Accept-Encoding: gzip, deflate ,客户端浏览器与HttpClient都提供了内置的解压支持。HttpClient中启用这个压缩的代码如下:

    var httpClient = new HttpClient(new HttpClientHandler { AutomaticDecompression = 
        System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate });

    对于请求内容的压缩,.NET中的HttpClient并没有提供内置支持,IIS也没有提供对解压的内置支持,需要自己写代码实现,本文也是由此而生。

    为什么要对请求内容进行压缩呢?目前我们在2种应用场景下遇到:1)用HttpClient调用第三方Web API;2)或者iOS App调用自己的Web API时需要提交大文本数据。

    对于压缩与解压,System.IO.Compression中提供了对应的类库——GZipStream与DeflateStream,我们只需要在HttpClient与Web API中应用它们即可。

    先来看看客户端HttpClient的实现。我们需要实现一个支持压缩的HttpContent——CompressedContent,实现代码如下:

    复制代码
    public enum CompressionMethod
    {
        GZip = 1,
        Deflate = 2
    }
    
    public class CompressedContent : HttpContent
    {
        private readonly HttpContent _originalContent;
        private readonly CompressionMethod _compressionMethod;
    
        public CompressedContent(HttpContent content, CompressionMethod compressionMethod)
        {
            if (content == null)
            {
                throw new ArgumentNullException("content");
            }
    
            _originalContent = content;
            _compressionMethod = compressionMethod;
    
            foreach (KeyValuePair<string, IEnumerable<string>> header in _originalContent.Headers)
            {
                Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
    
            Headers.ContentEncoding.Add(_compressionMethod.ToString().ToLowerInvariant());
        }
    
        protected override bool TryComputeLength(out long length)
        {
            length = -1;
            return false;
        }
    
        protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            if (_compressionMethod == CompressionMethod.GZip)
            {
                using (var gzipStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true))
                {
                    await _originalContent.CopyToAsync(gzipStream);
                }
            }
            else if (_compressionMethod == CompressionMethod.Deflate)
            {
                using (var deflateStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true))
                {
                    await _originalContent.CopyToAsync(deflateStream);
                }
            }
        }
    }
    复制代码

    主要就是重载HttpContent.SerializeToStreamAsync()方法,在其中使用相应的压缩算法进行压缩。

    HttpClient使用这个CompressedContent的方法如下:

    var json = JsonConvert.SerializeObject(bookmark);
    var content = new CompressedContent(
        new StringContent(json, Encoding.UTF8, "application/json"), 
        CompressionMethod.GZip);
    var response = await _httpClient.PostAsync("/api/bookmarks", content);

    再来看看服务端ASP.NET Web API中的实现,需要实现一个DelegatingHandler——DecompressionHandler:

    复制代码
    public class DecompressionHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
            CancellationToken cancellationToken)
        {
            if (request.Method == HttpMethod.Post)
            {
                bool isGzip = request.Content.Headers.ContentEncoding.Contains("gzip");
                bool isDeflate = !isGzip && request.Content.Headers.ContentEncoding.Contains("deflate");
    
                if (isGzip || isDeflate)
                {
                    Stream decompressedStream = new MemoryStream();
    
                    if (isGzip)
                    {
                        using (var gzipStream = new GZipStream(await request.Content.ReadAsStreamAsync(),
                            CompressionMode.Decompress))
                        {
                            await gzipStream.CopyToAsync(decompressedStream);
                        }
                    }
                    else if (isDeflate)
                    {
    
                        using (var gzipStream = new DeflateStream(await request.Content.ReadAsStreamAsync(),
                            CompressionMode.Decompress))
                        {
                            await gzipStream.CopyToAsync(decompressedStream);
                        }
                    }
    
                    decompressedStream.Seek(0, SeekOrigin.Begin);
    
                    var originContent = request.Content;
                    request.Content = new StreamContent(decompressedStream);
    
                    foreach (var header in originContent.Headers)
                    {
                        request.Content.Headers.Add(header.Key, header.Value);
                    }
                }
            }
    
            return await base.SendAsync(request, cancellationToken);
        }
    }
    复制代码

    重载DelegatingHandler.SendAsync()方法,在其中用GZipStream或DeflateStream完成解压操作。

    然后在WebApiConfig中应用这个DecompressionHandler,代码如下:

    复制代码
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MessageHandlers.Add(new DecompressionHandler());
        }
    }
    复制代码

    最后用这个支持请求内容压缩的HttpClient调用一下这个支持请求内容解压的Web API测试一下,用WireShark抓包看一下压缩是否生效。

    测试成功!

  • 相关阅读:
    蓝桥杯基础练习 高精度加法
    int和string的相互装换 (c++)
    蓝桥杯基础练习 十六进制转八进制
    markdown笔记
    hdu1384Intervals(差分约束)
    【Android】Android Studio 设置
    【调试】pthread.c:(.text+0xa2):对‘pthread_create’未定义的引用
    【STM32】开拓者MiniSTM32RBT6开发板原理图
    [小技巧]解决Word转PDF时,图片变模糊的问题
    Java十六进制字符串转换说明
  • 原文地址:https://www.cnblogs.com/zxtceq/p/5872877.html
Copyright © 2011-2022 走看看