如何让WRO回答未修改的http 304?

时间:2015-06-14 09:48:26

标签: spring wro4j

我们正在通过我们的webapp中的wro服务javascript资源(和其他人)。 在PROD环境中,浏览器将在未来一年获得(例如)app.js angular webapp的内容和“过期”标题。

意味着对于后续请求,浏览器将其从缓存中取出而不向服务器发出请求。 如果我们部署新版本的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);
}
}

2 个答案:

答案 0 :(得分:2)

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

关于这些标头的重要性有plenty of details。简短的解释是:

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

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

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

如果您希望客户端始终检查最新资源版本,则不应发送过期和过期缓存控制头。

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

答案 1 :(得分:1)

根据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

}