How do you set cache headers in Spring MVC?

JavaSpringCachingSpring Mvc

Java Problem Overview


In an annotation-based Spring MVC controller, what is the preferred way to set cache headers for a specific path?

Java Solutions


Solution 1 - Java

I just encountered the same problem, and found a good solution already provided by the framework. The org.springframework.web.servlet.mvc.WebContentInterceptor class allows you to define default caching behaviour, plus path-specific overrides (with the same path-matcher behaviour used elsewhere). The steps for me were:

  1. Ensure my instance of org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter does not have the "cacheSeconds" property set.

  2. Add an instance of WebContentInterceptor:

    <mvc:interceptors>
    ...
    <bean class="org.springframework.web.servlet.mvc.WebContentInterceptor" p:cacheSeconds="0" p:alwaysUseFullPath="true" >
        <property name="cacheMappings">
            <props>
                <!-- cache for one month -->
                <prop key="/cache/me/**">2592000</prop>
                <!-- don't set cache headers -->
                <prop key="/cache/agnostic/**">-1</prop>
            </props>
        </property>
    </bean>
    ...
    </mvc:interceptors>
    

After these changes, responses under /foo included headers to discourage caching, responses under /cache/me included headers to encourage caching, and responses under /cache/agnostic included no cache-related headers.


If using a pure Java configuration:

@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
  /* Time, in seconds, to have the browser cache static resources (one week). */
  private static final int BROWSER_CACHE_CONTROL = 604800;

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
     .addResourceHandler("/images/**")
     .addResourceLocations("/images/")
     .setCachePeriod(BROWSER_CACHE_CONTROL);
  }
}

See also: http://docs.spring.io/spring-security/site/docs/current/reference/html/headers.html

Solution 2 - Java

The answer is quite simple:

@Controller
public class EmployeeController {

@RequestMapping(value = "/find/employer/{employerId}", method = RequestMethod.GET)
public List getEmployees(@PathVariable("employerId") Long employerId, final HttpServletResponse response) {
response.setHeader("Cache-Control", "no-cache");
return employeeService.findEmployeesForEmployer(employerId);
}

}
Code above shows exactly what you want to achive. You have to do two things. Add "final HttpServletResponse response" as your parameter. And then set header Cache-Control to no-cache.

Solution 3 - Java

http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/servlet/support/WebContentGenerator.html">org.springframework.web.servlet.support.WebContentGenerator</a>;, which is the base class for all Spring controllers has quite a few methods dealing with cache headers:

/* Set whether to use the HTTP 1.1 cache-control header. Default is "true".
 * <p>Note: Cache headers will only get applied if caching is enabled
 * (or explicitly prevented) for the current request. */
public final void setUseCacheControlHeader();

/* Return whether the HTTP 1.1 cache-control header is used. */
public final boolean isUseCacheControlHeader();

/* Set whether to use the HTTP 1.1 cache-control header value "no-store"
 * when preventing caching. Default is "true". */
public final void setUseCacheControlNoStore(boolean useCacheControlNoStore);

/* Cache content for the given number of seconds. Default is -1,
 * indicating no generation of cache-related headers.
 * Only if this is set to 0 (no cache) or a positive value (cache for
 * this many seconds) will this class generate cache headers.
 * The headers can be overwritten by subclasses, before content is generated. */
public final void setCacheSeconds(int seconds);

They can either be invoked within your controller prior to content generation or specified as bean properties in Spring context.

Solution 4 - Java

Starting with Spring 4.2 you can do this:

import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class CachingController {
    @RequestMapping(method = RequestMethod.GET, path = "/cachedapi")
    public ResponseEntity<MyDto> getPermissions() {

        MyDto body = new MyDto();

        return ResponseEntity.ok()
            .cacheControl(CacheControl.maxAge(20, TimeUnit.SECONDS))
            .body(body);
    }
}

CacheControl object is a builder with many configuration options, see JavaDoc

Solution 5 - Java

You could use a Handler Interceptor and use the postHandle method provided by it:

http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/web/servlet/HandlerInterceptor.html

postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 

then just add a header as follows in the method:

response.setHeader("Cache-Control", "no-cache");

Solution 6 - Java

you can define a anotation for this: @CacheControl(isPublic = true, maxAge = 300, sMaxAge = 300), then render this anotation to HTTP Header with Spring MVC interceptor. or do it dynamic:

int age = calculateLeftTiming();
String cacheControlValue = CacheControlHeader.newBuilder()
      .setCacheType(CacheType.PUBLIC)
      .setMaxAge(age)
      .setsMaxAge(age).build().stringValue();
if (StringUtils.isNotBlank(cacheControlValue)) {
    response.addHeader("Cache-Control", cacheControlValue);
}

Implication can be found here: 优雅的Builder模式

BTW: I just found that Spring MVC has build-in support for cache control: Google WebContentInterceptor or CacheControlHandlerInterceptor or CacheControl, you will find it.

Solution 7 - Java

I found WebContentInterceptor to be the easiest way to go.

@Override
public void addInterceptors(InterceptorRegistry registry)
{
    WebContentInterceptor interceptor = new WebContentInterceptor();
    interceptor.addCacheMapping(CacheControl.noCache(), "/users", "admin");
    registry.addInterceptor(interceptor);
}

Solution 8 - Java

I know this is a really old one, but those who are googling, this might help:

@Override
protected void addInterceptors(InterceptorRegistry registry) {

    WebContentInterceptor interceptor = new WebContentInterceptor();

    Properties mappings = new Properties();
    mappings.put("/", "2592000");
    mappings.put("/admin", "-1");
    interceptor.setCacheMappings(mappings);

    registry.addInterceptor(interceptor);
}

Solution 9 - Java

In your controller, you can set response headers directly.

response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);

Solution 10 - Java

You could extend AnnotationMethodHandlerAdapter to look for a custom cache control annotation and set the http headers accordingly.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionD. WroblewskiView Question on Stackoverflow
Solution 1 - JavaEric R. RathView Answer on Stackoverflow
Solution 2 - JavagoroncyView Answer on Stackoverflow
Solution 3 - JavaChssPly76View Answer on Stackoverflow
Solution 4 - JavaGian MarcoView Answer on Stackoverflow
Solution 5 - JavaJonView Answer on Stackoverflow
Solution 6 - JavaarganzhengView Answer on Stackoverflow
Solution 7 - JavaLiang ZhouView Answer on Stackoverflow
Solution 8 - JavaVitali CarbivniciiView Answer on Stackoverflow
Solution 9 - JavahakunamiView Answer on Stackoverflow
Solution 10 - Javauser281581View Answer on Stackoverflow