Dans un contrôleur Spring MVC basé sur des annotations, quelle est la méthode préférée pour définir les en-têtes de cache pour un chemin spécifique?
org.springframework.web.servlet.support.WebContentGenerator , qui est la classe de base de tous les contrôleurs Spring, comporte de nombreuses méthodes traitant des en-têtes de cache:
/* 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);
Ils peuvent être appelés dans votre contrôleur avant la génération de contenu ou spécifiés en tant que propriétés de bean dans un contexte Spring.
Je viens de rencontrer le même problème et j'ai trouvé une bonne solution déjà fournie par le framework. La classe org.springframework.web.servlet.mvc.WebContentInterceptor
vous permet de définir un comportement de mise en cache par défaut, ainsi que des remplacements spécifiques au chemin (avec le même comportement de matrices de chemins que celui utilisé ailleurs). Les étapes pour moi étaient:
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
.Ajoutez une instance de 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>
Après ces modifications, les réponses sous/foo incluaient des en-têtes pour décourager la mise en cache, celles dans/cache/me incluaient des en-têtes pour encourager la mise en cache et les réponses sous/cache/agnostic ne comprenaient aucun en-tête lié au cache.
Si vous utilisez une configuration Java pure:
@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);
}
}
Voir aussi: http://docs.spring.io/spring-security/site/docs/current/reference/html/headers.html
La réponse est assez 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);
}
}
Le code ci-dessus montre exactement ce que vous voulez atteindre. Vous devez faire deux choses. Ajoutez "réponse finale HttpServletResponse" comme paramètre. Et ensuite, définissez l'en-tête Cache-Control sur no-cache. À partir de Spring 4.2 vous pouvez le faire:
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 est un générateur avec de nombreuses options de configuration, voir JavaDoc
Vous pouvez utiliser un intercepteur de gestionnaire et utiliser la méthode postHandle fournie par celui-ci:
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
puis ajoutez simplement un en-tête comme suit dans la méthode:
response.setHeader("Cache-Control", "no-cache");
vous pouvez définir une annotation pour cela: @CacheControl(isPublic = true, maxAge = 300, sMaxAge = 300)
, puis la rendre en en-tête HTTP avec l'intercepteur Spring MVC. ou le faire dynamique:
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);
}
Les implications peuvent être trouvées ici: Bu 的 Builder 模式
BTW: Je viens de constater que Spring MVC dispose d’un support intégré pour le contrôle du cache:.
Je sais que c'est très ancien, mais ceux qui googuent pourraient aider:
@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);
}
Vous pouvez étendre AnnotationMethodHandlerAdapter pour rechercher une annotation de contrôle de cache personnalisée et définir les en-têtes http en conséquence.
Dans votre contrôleur, vous pouvez définir directement les en-têtes de réponse.
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
J'ai trouvé que WebContentInterceptor
était le moyen le plus facile d'aller.
@Override
public void addInterceptors(InterceptorRegistry registry)
{
WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.addCacheMapping(CacheControl.noCache(), "/users", "admin");
registry.addInterceptor(interceptor);
}