How do you set cache headers in Spring MVC?
Solution 1
org.springframework.web.servlet.support.WebContentGenerator, 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 2
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:
- Ensure my instance of
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
does not have the "cacheSeconds" property set. -
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 3
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 4
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
You could use a Handler Interceptor and use the postHandle method provided by it:
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");
D. Wroblewski
Updated on July 05, 2022Comments
-
D. Wroblewski almost 2 years
In an annotation-based Spring MVC controller, what is the preferred way to set cache headers for a specific path?
-
D. Wroblewski over 14 yearsBut, as I wrote in my question, I use an annotation-based controller that doesn't subclass from any spring base class. How will this help me?
-
ChssPly76 over 14 yearsIf you want to alter cache settings depending on specific path, extending
AbstractController
is by far the easiest solution. If you want to apply your cache settings to all controllers, you can specify them onAnnotationMethodHandlerAdapter
instance in Spring context for annotation-based controllers. Here's an example: static.springsource.org/spring/docs/2.5.6/reference/… (disregard the init binder, you don't need it) -
D. Wroblewski over 14 yearsBecause my question concerned how to do this with an annotations-based controller. Your solution only applies to controllers that subclass WebContentGenerator. Isn't it so?
-
ChssPly76 over 14 yearsI've adressed that in my above comment. If you think you'll find a better solution I wish you luck.
-
D. Wroblewski over 14 yearsthat's why I up-voted your comment. Why didn't you edit your answer instead?
-
yincrash almost 14 yearsthis would have to be added to every request. not a good solution for adding the header to all requests across the board.
-
yincrash almost 14 yearsTo clarify, AnnotationMethodHandlerAdapter does extend WebContentGenerator which gives you all the cache header options. Remember, you need to set <property name="cacheSeconds" value="0" /> or a positive value for any of the cache headers to show up.
-
Czar over 13 yearsI tried to use this for images stored in the DB. I can easily retrieve them. They show up, but FireBug always keeps telling me status "200 Ok", so no caching. Any ideas?
-
Jesse Jashinsky over 11 yearsThis won't work because I don't have a prefix of mvc, or p. What are they supposed to be?
-
Eric R. Rath over 11 yearsJesse - check the root element of your context def file; you should have a namespace element like 'xmlns:mvc="springframework.org/schema/mvc"', and another attribute named 'xsi:schemaLocation' with a value that includes 'springframework.org/schema/mvc springframework.org/schema/mvc/spring-mvc-3.1.xsd'. See static.springsource.org/spring/docs/3.1.x/… for an example of a typical XML config.
-
BiAiB over 10 yearsCan you tell which xml namespace p: refers to ?
-
Eric R. Rath over 10 yearsYou should only need to add
xmlns:p="http://www.springframework.org/schema/p"
as long as you're using the 3.1 xsd. -
Ryan almost 9 years@JesseJ What part didn't work? I tried and it works for me.
-
user590849 over 8 yearsThis setup is not working for me. I did not do the <p...> thing I explicity mentioned a <property name="cacheSeconds">2345</property>
-
Klesun almost 4 years@yincrash, quoting OP: what is the preferred way to set cache headers for a specific path