自定义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

 

转载于:https://www.cnblogs.com/Code-life/p/7657999.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值