如何让 WRO 回答与 http 304 未修改

  • 本文关键字:http 未修改 WRO spring wro4j
  • 更新时间 :
  • 英文 :


我们通过web应用程序中的wro提供javascript资源(和其他资源)。例如,在 PROD 环境中,浏览器获取 app.js angular Web 应用程序的内容,其中包含未来一年的"过期"标头。

这意味着对于后续请求,浏览器会从缓存中获取它,而无需向服务器发出请求。如果我们部署新版本的 webapp,浏览器不会获得新版本,因为它从本地缓存中获取它。

目标是配置 wro 或/和 spring,以便正确设置标头,让浏览器每次都执行请求,并且服务器返回未修改的 304。因此,我们将让客户端自动"更新"新部署。有人已经做到了这一点吗?

我们使用 Spring 的 Java 配置:

@Configuration
public class Wro4jConfiguration {   
@Value("${app.webapp.web.minimize}")
private String minimize;
@Value("${app.webapp.web.disableCache}")
private String disableCache;
@Autowired
private Environment env;
@Bean(name = "wroFilter")
public WroFilter wroFilter() {
    ConfigurableWroFilter filter = new ConfigurableWroFilter();
    filter.setWroManagerFactory(new Wro4jManagerFactory());
    filter.setWroConfigurationFactory(createProperties());
    return filter;
}
private PropertyWroConfigurationFactory createProperties() {
    Properties props = new Properties();
    props.setProperty("jmxEnabled", "false");
    props.setProperty("debug", String.valueOf(!env.acceptsProfiles(EnvConstants.PROD)));
    props.setProperty("gzipResources", "false");
    props.setProperty("ignoreMissingResources", "true");
    props.setProperty("minimizeEnabled", minimize);
    props.setProperty("resourceWatcherUpdatePeriod", "0");
    props.setProperty("modelUpdatePeriod", "0");
    props.setProperty("cacheGzippedContent", "false");
    // let's see if server-side cache is disabled (DEV only)
    if (Boolean.valueOf(disableCache)) {
        props.setProperty("resourceWatcherUpdatePeriod", "1");
        props.setProperty("modelUpdatePeriod", "5");
    }
    return new PropertyWroConfigurationFactory(props);
}
}

默认情况下,WroFilter 设置以下标头:ETag(资源的 md5 校验和)、缓存控制(公共,max-age=315360000)、过期(自资源创建以来 1 年)。

关于这些标头的重要性有很多细节。简短的解释是这样的:

当服务器从客户端请求中读取 ETag 时,服务器可以确定是发送文件 (HTTP 200) 还是告诉客户端仅使用其本地副本 (HTTP 304)。ETag 基本上只是文件的校验和,当文件内容更改时,它在语义上会发生变化。如果只发送 ETag,则客户端将始终必须发出请求。

过期和缓存

控制标头非常相似,客户端(和代理/缓存)使用它来确定它是否需要向服务器发出请求。

因此,您真正要做的是同时使用两个标头 - 根据内容更改的频率将 Expires 标头设置为合理的值。然后配置要发送的 ETag,以便当客户端向服务器发送请求时,它可以更轻松地确定是否将文件发回。

如果希望客户端始终检查最新的资源版本,则不应发送 expires 和 cache-control 标头。

或者,还有一种更积极的缓存技术:将资源的校验和编码到其路径中。因此,每次更改资源时,该资源的路径都会更改。此方法保证客户端始终请求最新版本。对于此方法,理论上资源不应过期,因为每次更改资源时校验和都会更改。

根据 Alex 的信息和文档参考,我最终覆盖了 WroFilter.setResponseHeaders 以放置适当的过期值。这工作正常。Wro已经负责设置ETag,Date和其他,所以我只覆盖到期延迟和日期。

@Configuration
public class Wro4jConfiguration {
    @Value("${app.webapp.web.browserCache.maxAgeInHours}")
    private String maxAgeInHours;
    @Bean(name = "wroFilter")
    public WroFilter wroFilter() {
        ConfigurableWroFilter filter = createFilter();
        filter.setWroManagerFactory(new Wro4jManagerFactory());
        filter.setWroConfigurationFactory(createProperties());
        return filter;
    }
    private ConfigurableWroFilter createFilter() {
        return new ConfigurableWroFilter() {
            private final int BROWSER_CACHE_HOURS = Integer.parseInt(maxAgeInHours);
            private final int BROWSER_CACHE_SECONDS = BROWSER_CACHE_HOURS * 60 * 60;
            @Override
            protected void setResponseHeaders(final HttpServletResponse response){
                super.setResponseHeaders(response);
                if (!getConfiguration().isDebug()) {
                    ZonedDateTime cacheExpires = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("GMT")).plusHours(BROWSER_CACHE_HOURS);
                    String cacheExpiresStr = cacheExpires.format(DateTimeFormatter.RFC_1123_DATE_TIME);
                    response.setHeader(HttpHeader.EXPIRES.toString(), cacheExpiresStr);
                    response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "public,  max-age=" + BROWSER_CACHE_SECONDS);
                }
            }
        };
    }
    // Other config methods
}

相关内容

  • 没有找到相关文章

最新更新