Как включить кэширование HTTP-ответов в Spring Boot
Я реализовал REST-сервер, используя Spring Boot 1.0.2. У меня проблемы с тем, что Spring не может установить заголовки HTTP, которые отключают HTTP-кэширование.
Мой контроллер выглядит следующим образом:
@Controller
public class MyRestController {
@RequestMapping(value = "/someUrl", method = RequestMethod.GET)
public @ResponseBody ResponseEntity<String> myMethod(
HttpServletResponse httpResponse) throws SQLException {
return new ResponseEntity<String>("{}", HttpStatus.OK);
}
}
Все HTTP-ответы содержат следующие заголовки:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: 0
Pragma: no-cache
Я пробовал следующее, чтобы удалить или изменить эти заголовки:
- Вызов
setCacheSeconds(-1)
в контроллере. - Вызов
httpResponse.setHeader("Cache-Control", "max-age=123")
в контроллере. - определять
@Bean
это возвращаетWebContentInterceptor
за что я позвонилsetCacheSeconds(-1)
, - Установить свойство
spring.resources.cache-period
до -1 или положительное значение вapplication.properties
,
Ничто из вышеперечисленного не имело никакого эффекта. Как отключить или изменить эти заголовки для всех или отдельных запросов в Spring Boot?
10 ответов
Оказывается, HTTP-заголовки без кеша устанавливаются Spring Security. Это обсуждается в http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/.
Следующее отключает заголовок ответа HTTP Pragma: no-cache
, но в остальном не решает проблему:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// Prevent the HTTP response header of "Pragma: no-cache".
http.headers().cacheControl().disable();
}
}
В итоге я полностью отключил Spring Security для общедоступных статических ресурсов следующим образом (в том же классе, что и выше):
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/public/**");
}
Это требует настройки двух обработчиков ресурсов, чтобы правильно получить заголовки элемента управления кэшем:
@Configuration
public class MvcConfigurer extends WebMvcConfigurerAdapter
implements EmbeddedServletContainerCustomizer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Resources without Spring Security. No cache control response headers.
registry.addResourceHandler("/static/public/**")
.addResourceLocations("classpath:/static/public/");
// Resources controlled by Spring Security, which
// adds "Cache-Control: must-revalidate".
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCachePeriod(3600*24);
}
}
См. Также Обслуживание статических веб-ресурсов в приложении Spring Boot & Spring Security.
В весенней загрузке есть много способов для http-кэширования. Использование пружинного башмака 2.1.1 и дополнительно пружинной безопасности 5.1.1.
1. Для ресурсов, использующих обработчик ресурсов в коде:
Таким способом вы можете добавлять индивидуальные расширения ресурсов.
registry.addResourceHandler
Для добавления пути URI, где получить ресурс
.addResourceLocations
Предназначен для установки местоположения в файловой системе, где расположены ресурсы (данное значение является относительным с classpath, но также возможен абсолютный путь с file:://.)
.setCacheControl
Предназначен для настройки заголовков кэша (не требует пояснений.)
Resourcechain и resolver не являются обязательными (в данном случае они соответствуют значениям по умолчанию).
@Configuration
public class CustomWebMVCConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/*.js", "/*.css", "/*.ttf", "/*.woff", "/*.woff2", "/*.eot",
"/*.svg")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)
.cachePrivate()
.mustRevalidate())
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
}
2. Для ресурсов, использующих файл конфигурации свойств приложения
То же самое, что и выше, за исключением конкретных шаблонов, но теперь как config.Эта конфигурация применяется ко всем ресурсам в перечисленных статических местоположениях.
spring.resources.cache.cachecontrol.cache-private=true
spring.resources.cache.cachecontrol.must-revalidate=true
spring.resources.cache.cachecontrol.max-age=31536000
spring.resources.static-locations=classpath:/static/
3. На уровне контроллера
Ответом здесь является HttpServletResponse, введенный в метод контроллера в качестве параметра.
no-cache, must-revalidate, private
getHeaderValue выведет параметры кэша в виде строки. например
response.setHeader(HttpHeaders.CACHE_CONTROL,
CacheControl.noCache()
.cachePrivate()
.mustRevalidate()
.getHeaderValue());
Переопределение поведения кэширования по умолчанию для конкретного метода можно выполнить следующим образом:
@Controller
public class MyRestController {
@RequestMapping(value = "/someUrl", method = RequestMethod.GET)
public @ResponseBody ResponseEntity<String> myMethod(
HttpServletResponse httpResponse) throws SQLException {
return new ResponseEntity.ok().cacheControl(CacheControl.maxAge(100, TimeUnit.SECONDS)).body(T)
}
}
Я нашел это расширение Spring: https://github.com/foo4u/spring-mvc-cache-control.
Вам просто нужно сделать три шага.
Шаг 1 (pom.xml):
<dependency>
<groupId>net.rossillo.mvc.cache</groupId>
<artifactId>spring-mvc-cache-control</artifactId>
<version>1.1.1-RELEASE</version>
<scope>compile</scope>
</dependency>
Шаг 2 (WebMvcConfiguration.java):
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CacheControlHandlerInterceptor());
}
}
Шаг 3 (Контроллер):
@Controller
public class MyRestController {
@CacheControl(maxAge=31556926)
@RequestMapping(value = "/someUrl", method = RequestMethod.GET)
public @ResponseBody ResponseEntity<String> myMethod(
HttpServletResponse httpResponse) throws SQLException {
return new ResponseEntity<String>("{}", HttpStatus.OK);
}
}
Класс CacheControl - это гибкий конструктор, который упрощает создание различных типов кеширования:
@GetMapping("/users/{name}")
public ResponseEntity<UserDto> getUser(@PathVariable String name) {
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS))
.body(new UserDto(name));
}
Давайте поразим эту конечную точку в нашем тесте и подтвердим, что мы изменили заголовки:
given()
.when()
.get(getBaseUrl() + "/users/Michael")
.then()
.header("Cache-Control", "max-age=60");
Если вам не нужна аутентификация ваших статических ресурсов, вы можете сделать это:
import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toStaticResources;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.requestMatchers(toStaticResources().atCommonLocations());
}
...
}
и в вашем application.properties
:
spring.resources.cache.cachecontrol.max-age=43200
Смотрите ResourceProperties.java для дополнительных свойств, которые могут быть установлены.
@Configuration
@EnableAutoConfiguration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
}
Я сталкиваюсь с подобной проблемой. Я хотел получить только некоторые из динамических ресурсов (изображений), которые кэшируются в браузере. Если изображение меняется (не очень часто), я изменяю часть URI... Это мое решение
http.headers().cacheControl().disable();
http.headers().addHeaderWriter(new HeaderWriter() {
CacheControlHeadersWriter originalWriter = new CacheControlHeadersWriter();
@Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
Collection<String> headerNames = response.getHeaderNames();
String requestUri = request.getRequestURI();
if(!requestUri.startsWith("/web/eventImage")) {
originalWriter.writeHeaders(request, response);
} else {
//write header here or do nothing if it was set in the code
}
}
});
Я использовал следующие строки в своем контроллере.
ResponseEntity.ok().cacheControl(CacheControl.maxAge(secondWeWantTobeCached, TimeUnit.SECONDS)).body(objToReturnInResponse);
Обратите внимание, что Response будет иметь заголовок Cache-Control со значением secondWeWantTobeCached. Однако, если мы вводим URL-адрес в адресной строке и нажимаем Enter, запрос всегда будет отправляться из Chrome на сервер. Однако если мы перейдем по ссылке по какой-либо ссылке, браузер не отправит новый запрос и он будет взят из кеша.
Spring Security отключает ваши заголовки кэширования. Если у вас есть динамические ответы, которые не нужно защищать, и вы хотите использовать заголовки http для их кеширования - просто это путь к вашему application.property:
# Comma-separated list of paths to exclude from the default secured
security.ignored= /someUrl1
Увидеть больше в документах