[英]Spring Boot caching with ContentVersionStrategy prohibits resource compression with gzip
我们有一个带有 Thymeleaf 的 Spring Boot Web 应用程序。 在 HTML 模板中,我们通过<script defer th:src="@{/js/main.js}"></script>
引用了一些静态资源,例如/src/main/resources/static/js/main.js
.
为了允许浏览器为多次访问我们的网站缓存静态资源,我们启用了内容版本控制:
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 一起删除...
为了演示这个问题,我在 Github 中创建了一个示例项目,其中三个通过的测试和一个失败的测试表明我们的期望没有得到满足......
你知道如何解决这个矛盾吗? 我们做错了什么吗?
该问题现已在Spring 的 Github 存储库中进行跟踪。 目前的想法是在 Spring 的VersionResourceResolver
从强 ETag 切换到弱 ETag。
更新:该问题已通过 Spring 5.3 随附的 Spring Boot 2.4 解决。 包含修复。
一位贡献者在问题中强调了当前行为与此处解决的 Tomcat 中的修复相关联。 在那里,得出的结论是响应压缩违反了强 ETag 的含义,因此是一个错误。 因此,Tomcat 仅在不存在或仅存在弱 ETag 标头时才应用压缩(这与我在 SO 问题中描述的观察结果一致)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.