zoukankan      html  css  js  c++  java
  • 自定义DelegatingHandler为ASP.NET Web Api添加压缩与解压的功能

    HTTP协议中的压缩

    Http协议中使用Accept-Encoding和Content-Encoding头来表示期望Response内容的编码和当前Request的内容编码。而Http内容的压缩其实是内容编码的子集。所以也通过这两个头来描述Http Request和Response内容的压缩方式。

    常用的压缩算法有gzip(采用GNU zip压缩)和deflate(采用zlib的格式压缩),对应的Http头中的值也为:gzip或者deflate.

    内容压缩与解压

    Web服务器可以配置进行全局压缩和解压。当压缩或者解压需要基于某些逻辑进行判断,则要自行实现。

    浏览器(或者部分移动端网络库,包括iOS系统网络库)会对Content-Encoding头的值设为gzip或deflate的Response自动解压。而压缩则需要自己实现。

    实现

    这里采用DotNetZip库进行压缩和解压。

    首先创建两个压缩类来分别对gzip和deflate进行处理:

    public abstract class Compressor
    {
            public abstract byte[] Compress(byte[] originalData);
    
            public abstract byte[] Decompress(byte[] compressedData);
    }
    
    public class DeflateCompressor : Compressor
        {
            public static string Name => "Deflate";
    
            public override byte[] Compress(byte[] originalData)
            {
                using (var outputStream = new MemoryStream())
                {
                    using (var gzipStream = new DeflateStream(outputStream, CompressionMode.Compress, CompressionLevel.BestSpeed))
                    {
                        gzipStream.Write(originalData, 0, originalData.Length);
                    }
    
                    return outputStream.ToArray();
                }
            }
    
            public override byte[] Decompress(byte[] compressedData)
            {
                using (var inputStream = new MemoryStream(compressedData))
                {
                    var outputStream = new MemoryStream();
                    var gzipStream = new DeflateStream(inputStream, CompressionMode.Decompress, CompressionLevel.BestSpeed);
                    int blockSize = 10 * 1024;
                    int readSize = 1;
                    while (readSize > 0)
                    {
                        byte[] buffer = new byte[blockSize];
                        readSize = gzipStream.Read(buffer, 0, blockSize);
                        if (readSize > 0)
                        {
                            outputStream.Write(buffer, 0, readSize);
                        }
                    }
    
                    outputStream.Flush();
                    return outputStream.ToArray();
                }
            }
        }
    View Code
    public class GZipCompressor : Compressor
        {
            public static string Name => "GZip";
    
            public override byte[] Compress(byte[] originalData)
            {
                using (var outputStream = new MemoryStream())
                {
                    using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress, CompressionLevel.BestSpeed))
                    {
                        gzipStream.Write(originalData, 0, originalData.Length);
                    }
    
                    return outputStream.ToArray();
                }
            }
    
            public override byte[] Decompress(byte[] compressedData)
            {
                using (var inputStream = new MemoryStream(compressedData))
                {
                    var outputStream = new MemoryStream();
                    var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress, CompressionLevel.BestSpeed);
                    int blockSize = 10 * 1024;
                    int readSize = 1;
                    while (readSize > 0)
                    {
                        byte[] buffer = new byte[blockSize];
                        readSize = gzipStream.Read(buffer, 0, blockSize);
                        if (readSize > 0)
                        {
                            outputStream.Write(buffer, 0, readSize);
                        }
                    }
    
                    outputStream.Flush();
                    return outputStream.ToArray();
                }
            }
        }
    View Code

    创建一个DelegatingHandler: CompressHandler

    public class CompressHandler : DelegatingHandler
        {
            private Dictionary<string, Compressor> _supportCompressors = new Dictionary<string, Compressor>();
    
            public void RegisterCompressor(string compressorName, Compressor compressor)
            {
                if (string.IsNullOrEmpty(compressorName) || compressor == null)
                {
                    throw new InvalidOperationException("parameter is invalid.");
                }
    
                _supportCompressors[compressorName.ToLower()] = compressor;
            }
    
            public CompressHandler()
            {
    
            }
    
            protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
    //拦截请求,对请求进行解压
                await DecompressRequestIfNeed(request);
                var response = await base.SendAsync(request, cancellationToken);
    //对响应进行压缩
                await CompressResponseIfNeed(request, response);
    
                return response;
            }
    
            private async Task DecompressRequestIfNeed(HttpRequestMessage request)
            {
                if (request.Content != null)
                {
                    var originalContentType = request.Content.Headers.ContentType;
                    foreach (var encoding in request.Content.Headers.ContentEncoding)
                    {
                        if (_supportCompressors.ContainsKey(encoding))
                        {
                            var compressor = _supportCompressors[encoding];
                            var compressedBytes = await request.Content.ReadAsByteArrayAsync();
                            if (compressedBytes == null || compressedBytes.Length == 0)
                            {
                                // don't need to decompress, since the content is empty.
                                break;
                            }
    
    //基于Content-Encoding进行解压
                            var decompressedBytes = compressor.Decompress(compressedBytes);
                            var byteContent = new ByteArrayContent(decompressedBytes);
                            request.Content = byteContent;
    //恢复原始的ContentTypeHeader到新的RequestContent中
                            request.Content.Headers.ContentType = originalContentType;
    
                            break;
                        }
                    }
                }
            }
    
            private async Task CompressResponseIfNeed(HttpRequestMessage request, HttpResponseMessage response)
            {
                if (response.Content != null)
                {
                    foreach (var acceptEncoding in request.Headers.AcceptEncoding)
                    {
                        if (_supportCompressors.ContainsKey(acceptEncoding.Value.ToLower()))
                        {
                            var originalBytes = await response.Content.ReadAsByteArrayAsync();
                            if (originalBytes == null || originalBytes.Length == 0)
                            {
                                // don't need to compress, since the content is empty.
                                break;
                            }
                            //基于客户端能接受的压缩算法进行压缩
                            var compressor = _supportCompressors[acceptEncoding.Value];
                            var compresedBytes = compressor.Compress(originalBytes);
    //重新设置新的Response Content和Header
                            response.Content = new ByteArrayContent(compresedBytes);
                            response.Content.Headers.ContentEncoding.Add(acceptEncoding.Value);
                            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    
                            break;
                        }
                    }
                }
            }
        }
    View Code

    将CompressHandler添加到WebApi的MessageHandler列表中来拦截Http请求和响应, 来执行压缩和解压。

    public static void Register(HttpConfiguration config)
            {
               
                var compressHandler = new CompressHandler();
    //将GZip和Deflate压缩器注册到CompressHandler
                compressHandler.RegisterCompressor(GZipCompressor.Name, new GZipCompressor());
                compressHandler.RegisterCompressor(DeflateCompressor.Name, new DeflateCompressor());
                config.MessageHandlers.Add(compressHandler);
                config.MapHttpAttributeRoutes();
            }
    View Code
  • 相关阅读:
    第一册:lesson forty five。
    第一册:lesson forty three。
    马化腾2015港大演讲。
    Swing实现文件选择(目录选择)附导出
    SVN强制注释
    Websphere内存溢出的日志
    sql server2008 搭建链接服务器成功后查询时报Cannot obtain the schema rowset "DBSCHEMA_TABLES_INFO" for OLE DB provider "SQLNCLI10" for linked server "XXXXX". 的解决方法
    UML图例
    jSP的3种方式实现radio ,checkBox,select的默认选择值。
    通过js子页面回写父页面,改变父页面控件的值
  • 原文地址:https://www.cnblogs.com/Code-life/p/7657999.html
Copyright © 2011-2022 走看看