无法使用 Spring Webflux 和 Thymeleaf 对静态资源进行版本控制

Not able to version static resources with Spring Webflux and Thymeleaf

我尝试使用 Spring Webflux 在我的应用程序上实现静态内容版本控制,如文档中所述:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web-reactive.html#webflux-config-static-resources

我有以下使用版本资源解析器的资源处理程序:

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {

        final VersionResourceResolver versionResolver = new VersionResourceResolver();

        versionResolver.addFixedVersionStrategy("test_version", "/**");

        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .resourceChain(false)
                .addResolver(versionResolver);
    }

使用此代码,我希望将所有静态信息存储到浏览器请求的类路径中的“/static/”中,并在 URL 中添加 "test_version"。

但是,在请求的所有资源中,只有少数请求是使用 URL 中的版本完成的。

例如,这是请求的图像示例:

这是唯一请求的图像,在 URL 中添加了版本:

我的 JS 或 CSS 资源没有两个版本。从 ETag header.

看,版本化资源和未版本化资源之间在 HTTP 级别(headers 等)没有任何差异

这是我的项目配置:Spring Boot 2.1.0.RELEASE with Spring Webflux, Thymeleaf 3.0.11.RELEASE, Netty server.

为什么我的一些资源由 VersionResourceResolver 处理,而另一些不是?

更新

我在示例应用程序上重现了该问题:https://github.com/adsanche/statics-versioning-problem

此应用程序仅包含以 Spring Boot 2.1.0.RELEASE.

启动的 Webflux 和 Thymeleaf

这是我在 application.yml 中的版本控制配置:

spring:
  resources:
    chain:
      strategy:
        fixed:
          enabled: true
          version: test_version

请注意,通过模板请求的图像没有版本控制:

<!-- Can't version this image -->
<img th:src="@{/images/not_versioned.png}">

但是,通过 CSS 请求的图像版本控制良好:

.test {
    background: url('/images/versioned.png');
}

我的 CSS 通过 Thymeleaf 请求的文件也没有版本化:

<head>
    <link rel="stylesheet" th:href="@{/css/main.css}"/>
</head>

这与Sring Boot 2.1相同。1.RELEASE。

我的 Spring Boot/Thymeleaf 配置中是否遗漏了什么,或者这可能是个问题?

更新 2

我可能对这种行为有一些解释。

通过 CSS 文件调用的资源似乎是版本化的,因为有一个 CssLinkResourceTransformer 调用 ResourceUrlProvider 中的 getForUriString 方法,它使用 VersionResourceResolver解析资源路径。

然而,当从模板调用时,SpringWebFluxTemplateEngine 似乎通过 SpringWebFluxLinkBuilder 解析资源路径,其 processLink 方法似乎只是 return 资源提供的路径,没有转换或调用 ResourceUrlProvider.

我目前找到的唯一解决方法是通过这种方式覆盖 SpringWebFluxLinkBuilder 并将其设置为 SpringWebFluxTemplateEngine:

@Override
public String processLink(IExpressionContext context, String link) {

    return super.processLink(context, "test_version" + link);
}

使用该代码,我可以对模板中调用的所有 CSS/JS/images 进行版本控制。但是,我想知道这是否有必要,或者我是否只是缺少一些配置点来自动触发此逻辑。

首先,您应该放弃自定义配置并依赖配置属性,因为您没有做 Spring Boot 已经实现的任何事情:

spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.version=test_version

资源 URL 由 Spring 框架中的资源处理支持在服务器端重写。

首先,您应该确保它已启用:

  1. 在 Spring 启动的情况下,你不应该有 @EnableWebMvc@EnableWebFlux,因为它们会禁用网络自动配置
  2. 资源链接应位于模板文件中或由 Spring 重写的文件中(例如 CSS 文件)
  3. 资源 URL 应由模板引擎编码,如 <link rel="stylesheet" th:href="@{/static/css/spring.css}">

在 Spring WebFlux 的情况下,CSS 文件等资源在提供服务时被资源处理程序重写。 Spring MVC 支持重写模板引擎中的资源链接,但目前 Spring WebFlux 不支持。

您可以看到 ResourceUrlProvider 合约是异步的,因为它 returns 是 Mono 类型。另一方面,SpringWebFluxLinkBuilder 需要一个阻塞合约。我并不是说 Thymeleaf 在这里有问题,因为 Thymeleaf 已经以反应方式呈现模板。但是为了呈现这些链接,我们需要潜在地读取整个资源并计算它的哈希值——我们不能在呈现模板的过程中这样做。

您可以在 SPR-15012 中阅读更多相关信息,其中解释了当前的状态。请随意对该票发表评论 - 也许事情在此期间发生了变化,或者 Spring 框架/Spring 引导参考文档中可能缺少某些内容。