public virtual ICompressionProvider GetCompressionProvider(HttpContext context) { var accept = context.Request.Headers[HeaderNames.AcceptEncoding]; //判断请求头是否包含Accept-Encoding信心 if (StringValues.IsNullOrEmpty(accept)) { Debug.Assert(false, "Duplicate check failed."); return null; } //获取Accept-Encoding里的值,判断是否包含gzip、br、identity等,并返回匹配信息 if (!StringWithQualityHeaderValue.TryParseList(accept, out var encodings) || !encodings.Any()) { return null; } //根据请求信息和设置信息计算匹配优先级 var candidates = new HashSet<ProviderCandidate>(); foreach (var encoding in encodings) { var encodingName = encoding.Value; //Quality涉及到一个非常复杂的算法,有兴趣的可以自行查阅 var quality = encoding.Quality.GetValueOrDefault(1); //quality需大于0 if (quality < double.Epsilon) { continue; } //匹配请求头里encodingName和设置的providers压缩算法里EncodingName一致的算法 //从这里可以看出匹配的优先级和注册providers里的顺序也有关系 for (int i = 0; i < _providers.Length; i++) { var provider = _providers[i]; if (StringSegment.Equals(provider.EncodingName, encodingName, StringComparison.OrdinalIgnoreCase)) { candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider)); } } //如果请求头里EncodingName是*的情况则在所有注册的providers里进行匹配 if (StringSegment.Equals("*", encodingName, StringComparison.Ordinal)) { for (int i = 0; i < _providers.Length; i++) { var provider = _providers[i]; candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider)); } break; } //如果请求头里EncodingName是identity的情况,则不对响应进行编码 if (StringSegment.Equals("identity", encodingName, StringComparison.OrdinalIgnoreCase)) { candidates.Add(new ProviderCandidate(encodingName.Value, quality, priority: int.MaxValue, provider: null)); } } ICompressionProvider selectedProvider = null; //如果匹配的只有一个则直接返回 if (candidates.Count <= 1) { selectedProvider = candidates.FirstOrDefault().Provider; } else { //如果匹配到多个则按照Quality倒序和Priority正序的负责匹配第一个 selectedProvider = candidates .OrderByDescending(x => x.Quality) .ThenBy(x => x.Priority) .First().Provider; } //如果没有匹配到selectedProvider或是identity的情况直接返回null if (selectedProvider == null) { return null; } return selectedProvider; }
通过以上的介绍我们可以大致了解到响应压缩的大致工作方式,简单总结一下
首先设置压缩相关的算法类型或是压缩目标的MimeType
其次我们可以设置压缩级别,这将决定压缩的质量和压缩性能
通过响应压缩中间件,我们可以获取到一个优先级最高的压缩算法进行压缩,这种情况主要是针对多种压缩类型的情况。这个压缩算法与内部机制和注册压缩算法的顺序都有一定的关系,最终会选择权重最大的返回。
响应压缩中间件的核心工作类ResponseCompressionBody通过实现IHttpResponseBodyFeature,重写输出相关的方法实现对响应的压缩,不需要我们手动进行调用相关方法,而是替换掉默认的输出方式。只要设置了响应压缩,并且请求满足响应压缩,那么有调用输出的地方默认都是执行ResponseCompressionBody里压缩相关的方法,而不是拦截具体的输出进行统一处理。至于为什么这么做,目前我还没有理解到设计者真正的考虑。
总结#
在查看相关代码之前,本来以为关于响应压缩相关的逻辑会非常的简单,看过了源码才知道是自己想的太简单了。其中和自己想法出入最大的莫过于在ResponseCompressionMiddleware中间件里,本以为是通过统一拦截输出流来进行压缩操作,没想到是对整体输出操作进行重写。因为在之前我们使用Asp.Net相关框架的时候是统一写Filter或者HttpModule进行处理的,所以存在思维定式。可能是Asp.Net Core设计者有更深层次的理解,可能是我理解的还不够彻底,不能够体会这样做的好处究竟是什么,如果你有更好的理解或则答案欢迎在评论区里留言解惑。