ASP.NET Core中的响应压缩的实现(3)

这个中间件非常的简单,就是初始化了ResponseCompressionBody。看到这里你也许会好奇,并没有触发调用压缩相关的任何代码,ResponseCompressionBody也只是调用了FinishCompressionAsync都是和释放相关的,不要着急我们来看ResponseCompressionBody类的结构

internal class ResponseCompressionBody : Stream, IHttpResponseBodyFeature, IHttpsCompressionFeature { }

这个类实现了IHttpResponseBodyFeature,我们使用的Response.Body其实就是获取的HttpResponseBodyFeature.Stream属性。我们使用的Response.WriteAsync相关的方法,其实内部都是在调用PipeWriter进行写操作,而PipeWriter就是来自HttpResponseBodyFeature.Writer属性。可以大致概括为,输出相关的操作其核心都是在操作IHttpResponseBodyFeature。有兴趣的可以自行查阅HttpResponse相关的源码可以了解相关信息。所以我们的ResponseCompressionBody其实是重写了输出操作相关方法。也就是说,只要你调用了Response相关的Write或Body相关的,其实本质都是在操作IHttpResponseBodyFeature,由于我们开启了响应输出相关的中间件,所以会调用IHttpResponseBodyFeature的实现类ResponseCompressionBody相关的方法完成输出。和我们常规理解的还是有偏差的,一般情况下我们认为,其实只要针对输出的Stream做操作就可以了,但是响应压缩中间件竟然重写了输出相关的操作。

了解到这个之后,相信大家就没有太多疑问了。由于ResponseCompressionBody重写了输出相关的操作,代码相对也比较多,就不逐一粘贴出来了,我们只查看设计到响应压缩核心相关的代码,关于ResponseCompressionBody源码相关的细节有兴趣的可以自行查阅[点击查看源码👈],输出的本质其实都是在调用Write方法,我们就来查看一下Write方法相关的实现

public override void Write(byte[] buffer, int offset, int count) { //这是核心方法有关于压缩相关的输出都在这 OnWrite(); //_compressionStream初始化在OnWrite方法里 if (_compressionStream != null) { _compressionStream.Write(buffer, offset, count); if (_autoFlush) { _compressionStream.Flush(); } } else { _innerStream.Write(buffer, offset, count); } }

通过上面的代码我们看到OnWrite方法是核心操作,我们直接查看OnWrite方法实现

private void OnWrite() { if (!_compressionChecked) { _compressionChecked = true; //判断是否满足执行压缩相关的逻辑 if (_provider.ShouldCompressResponse(_context)) { //匹配Vary头信息对应的值 var varyValues = _context.Response.Headers.GetCommaSeparatedValues(HeaderNames.Vary); var varyByAcceptEncoding = false; //判断Vary的值是否为Accept-Encoding for (var i = 0; i < varyValues.Length; i++) { if (string.Equals(varyValues[i], HeaderNames.AcceptEncoding, StringComparison.OrdinalIgnoreCase)) { varyByAcceptEncoding = true; break; } } if (!varyByAcceptEncoding) { _context.Response.Headers.Append(HeaderNames.Vary, HeaderNames.AcceptEncoding); } //获取最佳的ICompressionProvider即最佳的压缩方式 var compressionProvider = ResolveCompressionProvider(); if (compressionProvider != null) { //设置选定的压缩算法,放入Content-Encoding头的值里 //客户端可以通过Content-Encoding头信息判断服务端采用的哪种压缩算法 _context.Response.Headers.Append(HeaderNames.ContentEncoding, compressionProvider.EncodingName); //进行压缩时,将 Content-MD5 删除该标头,因为正文内容已更改且哈希不再有效。 _context.Response.Headers.Remove(HeaderNames.ContentMD5); //进行压缩时,将 Content-Length 删除该标头,因为在对响应进行压缩时,正文内容会发生更改。 _context.Response.Headers.Remove(HeaderNames.ContentLength); //返回压缩相关输出流 _compressionStream = compressionProvider.CreateStream(_innerStream); } } } } private ICompressionProvider ResolveCompressionProvider() { if (!_providerCreated) { _providerCreated = true; //调用ResponseCompressionProvider的方法返回最合适的压缩算法 _compressionProvider = _provider.GetCompressionProvider(_context); } return _compressionProvider; }

从上面的逻辑我们可以看到,在执行压缩相关逻辑之前需要判断是否满足执行压缩相关的方法ShouldCompressResponse,这个方法是ResponseCompressionProvider里的方法,这里就不再粘贴代码了,本来就是判断逻辑我直接整理出来大致就是一下几种情况

如果请求是Https的情况下,是否设置了允许Https情况下压缩的设置,即ResponseCompressionOptions的EnableForHttps属性设置

Response.Head里不能包含Content-Range头信息

Response.Head里之前不能包含Content-Encoding头信息

Response.Head里之前必须要包含Content-Type头信息

返回的MimeType里不能包含配置的不需要压缩的类型,即ResponseCompressionOptions的ExcludedMimeTypes

返回的MimeType里需要包含配置的需要压缩的类型,即ResponseCompressionOptions的MimeTypes

如果不满足上面的两种情况,返回的MimeType里包含*/*也可以执行响应压缩

接下来我们查看ResponseCompressionProvider的GetCompressionProvider方法看它是如何确定返回哪一种压缩类型的

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/c75f3c89ab95a097d8904b5e9451d7f8.html