Spring 使用 ContentVersionStrategy 的启动缓存禁止使用 gzip 进行资源压缩

Spring Boot caching with ContentVersionStrategy prohibits resource compression with gzip

我们有一个 Spring 使用 Thymeleaf 启动 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

当使用 header Accept-Encoding='gzip' 请求(版本化)资源时,我们没有收到 Content-Encoding='gzip' 的响应。因此,资源压缩似乎无法与内容版本控制结合使用。

如果我们禁用内容版本控制,资源压缩工作正常:Content-Encoding='gzip' header 设置为(现在 non-versioned)资源。

所以我们深入 Spring 的内部并发现以下内容:

  1. org.springframework.web.servlet.resource.VersionResourceResolver#getResponseHeaders 始终设置(强)ETag header:
public HttpHeaders getResponseHeaders() {
    HttpHeaders headers = (this.original instanceof HttpResource ?
            ((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
    headers.setETag("\"" + this.version + "\"");
    return headers;
}
  1. 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,其中包含三个通过的测试和一个失败的测试,显示了我们的期望未达到的地方...

你知道如何解决这个矛盾吗?我们是不是做错了什么?

该问题现已在 Spring's Github repository 中进行跟踪。目前的想法是在 Spring 的 VersionResourceResolver.

中从强 ETag 切换到弱 ETag

更新:问题已通过 Spring Boot 2.4 解决,Spring 5.3 附带。包含修复程序。

贡献者在问题中强调当前行为与 Tomcat 中解决的 here 中的修复相关联。在那里,得出的结论是响应压缩违反了强 ETag 的含义,因此是一个错误。因此,Tomcat 仅在不存在或仅存在弱 ETag header 时应用压缩(这与我在 SO 问题中描述的观察结果一致)。