【问题标题】:How to add Cache-Control header to static resource in Spring Boot?如何在 Spring Boot 中将 Cache-Control 标头添加到静态资源?
【发布时间】:2016-01-17 19:19:23
【问题描述】:

如何在 Spring Boot 中为静态资源添加 Cache-Control HTTP 标头?

尝试在应用程序中使用过滤器组件,该组件可以正确写入标头,但 Cache-Control 标头会被覆盖。

@Component
public class CacheBustingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
                                              throws IOException, ServletException {

        HttpServletResponse httpResp = (HttpServletResponse) resp;
        httpResp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        httpResp.setHeader("This-Header-Is-Set", "no-cache, no-store, must-revalidate");
        httpResp.setHeader("Expires", "0");

        chain.doFilter(req, resp);
    }

我在浏览器中得到的是:

Cache-Control:no-store
This-Header-Is-Set:no-cache, no-store, must-revalidate
Expires:0

我想要的是:

Cache-Control:no-cache, no-store, must-revalidate
This-Header-Is-Set:no-cache, no-store, must-revalidate
Expires:0

【问题讨论】:

标签: java spring-boot cache-control


【解决方案1】:

我想在使用@Override public void addResourceHandlers(ResourceHandlerRegistry registry) {} 的给定答案中添加一些有用的 cmets,因为我遇到了一些问题。它们也可能对其他人有用。

假设使用 Spring Web MVC 的默认 Spring Boot 2.4 应用程序的目录结构如下。

src/main/resources/
|- static/
  |- res/
    |- css/
    |- js/
  |- images/
  |- favicon.ico

我们想为 css、js、图像和 favicon 添加缓存。然后配置将如下所示:

import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.concurrent.TimeUnit;

@Configuration
public class CacheStaticResourcesConfiguration implements WebMvcConfigurer {

    /**
     * We provide a custom configuration which resolves URL-Requests to static files in the
     * classpath (src/main/resources directory).
     *
     * This overloads a default configuration retrieved at least partly from
     * {@link WebProperties.Resources#getStaticLocations()}.
     * @param registry ResourceHandlerRegistry
     */
    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        /*
         * BE AWARE HERE:
         *
         * .addResourceHandler(): URL Paths
         * .addResourceLocations(): Paths in Classpath to look for file
         *   root "/" refers to src/main/resources
         *   For configuration example, see:
         *     org.springframework.boot.autoconfigure.web.WebProperties.Resources().getStaticLocations()
         *
         * .addResourceLocations("classpath:/static/")
         *   =>
         *      addResourceHandler("/**")
         *      => GET /res/css/main.css
         *         => resolved as: "classpath:/static/res/css/main.css"
         *      BUT
         *      addResourceHandler("/res/**")
         *      => GET /res/css/main.css
         *            (spring only appends the ** to the value from
         *             addResourceLocations())
         *         => resolved as: "classpath:/static/css/main.css"
         */

        registry
                .addResourceHandler("/favicon.ico")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS)
                        .noTransform()
                        .mustRevalidate());

        registry
                .addResourceHandler("/res/**")
                // trailing slash is important!
                .addResourceLocations("classpath:/static/res/")
                .setCacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)
                        .noTransform()
                        .mustRevalidate());

        registry
                .addResourceHandler("/images/**")
                // trailing slash is important!
                .addResourceLocations("classpath:/static/images/")
                .setCacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)
                        .noTransform()
                        .mustRevalidate());
    }

}

【讨论】:

    【解决方案2】:

    我们也可以在拦截器中添加Cache-Control header:

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        WebContentInterceptor interceptor = new WebContentInterceptor();
        interceptor.addCacheMapping(CacheControl.maxAge(60, TimeUnit.SECONDS)
          .noTransform()
          .mustRevalidate(), "/static/**");
        registry.addInterceptor(interceptor);
    }
    

    那么我们就不用指定资源位置了。

    https://www.baeldung.com/spring-mvc-cache-headers#cache-interceptors

    【讨论】:

    • 这似乎不适用于我的静态文件,仅适用于我的非弹簧安全保护的动态内容。
    【解决方案3】:

    spring boot中有很多缓存http资源的方法。使用 spring boot 2.1.1 和另外的 spring security 5.1.1。

    1.对于在代码中使用资源处理程序的资源(未测试):

    您可以通过这种方式添加自定义的资源扩展。

    registry.addResourceHandler
    

    用于添加获取资源的uri路径

    .addResourceLocations
    

    用于设置资源所在文件系统中的位置( given 是类路径的相对路径,但 file::// 的绝对路径也是可能的。)

    .setCacheControl
    

    用于设置缓存头(不言自明。)

    资源链和解析器是可选的(在这种情况下与默认值完全相同。)

    @Configuration
    public class CustomWebMVCConfig implements WebMvcConfigurer {
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.noStore()
                        .mustRevalidate())
                .setCacheControl(CacheControl.noCache())
                .resourceChain(true)
                .addResolver(new PathResourceResolver());
        }
    }
    

    2。对于使用应用程序属性配置文件的资源

    与上面相同,减去特定模式,但现在作为配置。 此配置适用于列出的静态位置中的所有资源。

    spring.resources.cache.cachecontrol.no-store=true
    spring.resources.cache.cachecontrol.must-revalidate=true
    spring.resources.cache.cachecontrol.no-cache=true
    

    3。在控制器级别

    这里的Response是作为参数注入到控制器方法中的HttpServletResponse。

    response.setHeader(HttpHeaders.CACHE_CONTROL,
                "no-cache, must-revalidate, no-store");
    response.setHeader("Expires", "0");
    

    【讨论】:

      【解决方案4】:

      这些属性控制资源的默认缓存标头:

      spring.resources.cache.cachecontrol.max-age: 3600
      

      https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

      【讨论】:

      • 这适用于包括 index.html 在内的静态资源。当请求 index.html 时(但在对所有其他静态资源的请求中设置它),我将如何防止服务器设置标头?
      • @dagerber 你还需要这个要求吗?我想通了。
      【解决方案5】:

      使用 spring boot 1.3.3,我得到了一个 404 使用 malenc 答案的答案。 我可以通过添加资源位置来纠正它:

      @Configuration
      public class HttpClientConfiguration extends WebMvcConfigurerAdapter {
          @Override
          public void addResourceHandlers(ResourceHandlerRegistry registry) {
              registry.addResourceHandler("/**").setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS))
                      .addResourceLocations("/");
          }
      }
      

      【讨论】:

        【解决方案6】:

        Maleenc 的,答案是正确的。但是,此实现存在一个问题。

        以下代码将在第一次请求时提供正确的缓存控制标头,但未来返回 304(未修改)的请求将返回 spring security 设置的默认缓存控制标头。 {代码}

        public void addResourceHandlers(ResourceHandlerRegistry registry) {         
            registry.addResourceHandler("/resources/**").setCacheControl(CacheControl.maxAge(10, TimeUnit.SECONDS));
        }
        

        我已经向 Spring 团队提出了这个问题,请参阅 https://jira.spring.io/browse/SPR-15133。这里有响应:“现在您确实不应该为整个应用程序禁用安全缓存控制标头;在该问题评论中解释了为特定路径(资源处理,此处)禁用这些标头的正确方法,请参阅@987654322 @

        【讨论】:

          【解决方案7】:

          这是因为 Spring Security:它重写了所有缓存头以完全禁用缓存。 所以我们需要做两件事:

          1. 为静态资源禁用 spring 安全性
          2. 启用静态资源缓存处理

          在当前版本的 Spring Boot 中,我们可以在 application.properties 配置中更改此行为。

          对某些资源禁用 spring 安全性:

          # Comma-separated list of paths to exclude from the default secured 
          security.ignored=/myAssets/**
          

          为静态资源启用发送缓存头:

          # Enable HTML5 application cache manifest rewriting.
          spring.resources.chain.html-application-cache=true
          
          # Enable the Spring Resource Handling chain. Disabled by default unless at least one strategy has been enabled.
          spring.resources.chain.enabled=true
          # Enable the content Version Strategy.
          spring.resources.chain.strategy.content.enabled=true 
          # Comma-separated list of patterns to apply to the Version Strategy.
          spring.resources.chain.strategy.content.paths=/** 
          
          # Locations of static resources.
          spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
          

          就是这样。现在 Spring 将检查您的静态文件是否已更改,并可以发送更智能的响应(If-Modiffied-Since 等)并重写您的应用缓存。

          此外,如果有理由不为某些资源使用基于内容的版本 - 您可以使用备用 FixedVersion 策略并在配置中明确设置版本:

          #Enable the fixed Version Strategy.
          spring.resources.chain.strategy.fixed.enabled=false 
          # Comma-separated list of patterns to apply to the Version Strategy.
          spring.resources.chain.strategy.fixed.paths= 
          # Version string to use for the Version Strategy.
          spring.resources.chain.strategy.fixed.version= 
          

          See more in docs

          【讨论】:

          【解决方案8】:

          根据ResourceHandlerRegistrydocumentation。这很容易。 (我现在没有与之相关的代码。)

          在你配置静态资源的地方添加addResourceHandler方法,它会返回ResourceHandlerRegistration对象。

          在那里你可以使用setCacheControl 方法。你要做的就是配置和设置一个CacheControl obejct。

          这是从 spring 4.2 开始,否则你必须像下面那样做。

          @Configuration
          @EnableWebMvc
          @ComponentScan("my.packages.here")
          public class WebConfig extends WebMvcConfigurerAdapter {
          
          
             @Override
              public void addResourceHandlers(ResourceHandlerRegistry registry) {
                  registry.addResourceHandler("/resources/**").setCachePeriod(0);
              }
          
          }
          

          【讨论】:

          • 使用 setCacheControl() 设置 CacheControl 对象使服务器发送正确的标头,通过 curl -I 验证
          • 我找不到任何 CacheControl 对象和 setCacheControl 的代码示例,您能否分享一下您是如何做到的?谢谢!
          • 其实你不应该使用@EnableWebMvc。这让我有很多时间来弄清楚会发生什么,因为有些页面没有提供。见stackoverflow.com/a/27383522/4252764
          • 如果您使用 Spring Boot,只需使用 spring.resources.cache-period 并确保将 Spring Security 配置为忽略静态资源 stackoverflow.com/questions/36835420/…
          猜你喜欢
          • 2015-03-07
          • 2014-11-10
          • 2014-08-20
          • 1970-01-01
          • 2016-10-01
          • 2011-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多