使用ContentVersionStrategy的java Spring引导缓存禁止使用gzip进行资源压缩
我们有一个带有Thymeleaf的Spring Boot web应用程序。
在HTML模板中,我们引用了一些静态资源,例如/src/main/resources/static/js/main.js
通过<script defer th:src="@{/js/main.js}"></script>
为了允许浏览器缓存静态资源,以便多次访问我们的网站,我们启用了内容版本控制:
spring.resources:
chain:
strategy.content:
enabled: true
paths: /**
cache.cachecontrol.max-age: 365d
这一切都很好,我们得到的资源将其MD5哈希附加到文件名(例如/main-d9f17fd70ee583fef4acf26dd331b8ab.js
)
为了进一步减少通信量,我们现在希望使用gzip启用资源压缩:
server:
compression:
enabled: true
mime-types: application/javascript,and-some-others
min-response-size: 1024
当请求头为Accept-Encoding='gzip'
的(版本化)资源时,我们不会得到Content-Encoding='gzip'
的响应。因此,资源压缩似乎不能与内容版本控制一起工作
如果我们禁用内容版本控制,资源压缩就可以正常工作:为(现在没有版本控制的)资源设置Content-Encoding='gzip'
头
因此,我们深入研究了Spring的内部,发现了以下内容:
org.springframework.web.servlet.resource.VersionResourceResolver#getResponseHeaders
始终设置(强)ETag头:
public HttpHeaders getResponseHeaders() {
HttpHeaders headers = (this.original instanceof HttpResource ?
((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
headers.setETag("\"" + this.version + "\"");
return headers;
}
org.apache.coyote.CompressionConfig#useCompression
如果存在强ETag,则禁用压缩:
public boolean useCompression(Request request, Response response) {
...
if (noCompressionStrongETag) {
String eTag = responseHeaders.getHeader("ETag");
if (eTag != null && !eTag.trim().startsWith("W/")) {
// Has an ETag that doesn't start with "W/..." so it must be a
// strong ETag
return false;
}
}
...
}
您可以将noCompressionStrongETag
设置为false,但这已被弃用,并将使用Tomcat 10删除
为了证明这个问题,我创建了一个example project in Github测试,其中有三个通过测试,一个失败测试,显示我们的期望没有得到满足
你有办法解决这个矛盾吗?我们做错什么了吗
# 1 楼答案
该问题现在在Spring's Github repository中跟踪。目前的想法是在Spring的
VersionResourceResolver
中从强etag切换到弱etag更新:该问题通过Spring 5.3附带的Spring Boot 2.4解决。包含修复
一位贡献者在本期文章中强调,当前行为与Tomcat addressedhere中的修复程序相关联。在这里,我们得出结论,响应压缩违反了强ETag的含义,因此是一个bug。因此,Tomcat仅在不存在或仅存在弱ETag头的情况下应用压缩(这与我在SO问题中描述的观察结果一致)