El almacenamiento en caché HTTP puede mejorar significativamente el rendimiento de una aplicación web. El almacenamiento en caché HTTP se basa en el encabezado de respuesta Cache-Control y posteriormente en los encabezados de solicitud condicionales, como Last-Modified y ETag. Cache-Control le dice a las cachés privadas (por ejemplo, el navegador) y públicas (por ejemplo, el proxy) cómo almacenar en caché y reutilizar las respuestas. El encabezado ETag se utiliza para una solicitud condicional, que puede resultar en una respuesta 304 (NOT_MODIFIED) sin cuerpo si el contenido no ha cambiado. ETag puede considerarse un sucesor más avanzado del encabezado Last-Modified.

Esta sección describe las opciones relacionadas con el almacenamiento en caché HTTP disponibles en Spring WebFlux.

Control de caché

CacheControl proporciona soporte para configurar opciones asociadas con el encabezado Cache-Control y se acepta como argumento en varios lugares:

  • Controladores

  • Recursos estáticos

Aunque RFC 7234 describe todas las directivas posibles para Cache-Control tipo de encabezado de respuesta CacheControl adopta un enfoque basado en casos de uso que se centra en escenarios comunes:

Java

// Almacenamiento en caché durante una hora  - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);
/// Impedir el almacenamiento en caché - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();
// Almacenamiento en caché durante diez días en cachés públicos y privados
// el caché público no debería convertir la respuesta
// "Control de caché: max-age=864000, público, sin transformación"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();
Kotlin
// Almacenamiento en caché durante una hora - "Cache-Control: max-age=3600"
val ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS)
/// Impedir el almacenamiento en caché - "Cache-Control: no-store"
val ccNoStore = CacheControl.noStore()
// Almacenamiento en caché durante diez días en cachés públicos y privados
// el caché público no debería convertir la respuesta
// "Control de caché: max-age=864000, público, sin transformación"
val ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic()

Controladores

Los controladores pueden agregar soporte explícito para el almacenamiento en caché HTTP. Recomendamos hacer esto porque el valor lastModified o ETag para un recurso debe calcularse antes de poder compararlo con los encabezados de solicitud condicionales. El controlador puede agregar el encabezado ETag y los parámetros Cache-Control a ResponseEntity, como se muestra en el siguiente ejemplo:

Java
@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {
Book book = findBook(id);
String version = book.getVersion();
return ResponseEntity
    .ok()
    .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
    .eTag(version) // lastModified is also available
    .body(book);
}
Kotlin
@GetMapping("/book/{id}")
fun showBook (@PathVariable id: Long): ResponseEntity<Book> {
val book = findBook(id)
val version = book.getVersion()
return ResponseEntity
    .ok()
    .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
    .eTag(version) // lastModified is also available
    .body(book)
}

En el ejemplo anterior, se envía una respuesta 304 (NOT_MODIFIED) con un cuerpo vacío si una comparación con los encabezados de solicitud condicional indica que el contenido no ha cambiado. De lo contrario, los encabezados ETag y Cache-Control se agregan a la respuesta.

También puede verificar los encabezados de solicitud condicionales en el controlador, como se muestra en el siguiente ejemplo:

Java
@RequestMapping
public Cadena myHandleMethod(Intercambio ServerWebExchange, modelo modelo) {
long eTag = ... 
if (exchange.checkNotModified(eTag)) {
retorno nulo; 
}
modelo.addAttribute(...); 
return "myViewName";
}
  1. Cálculo específico de la aplicación.
  2. La respuesta se estableció en 304 (NOT_MODIFIED). No se produce ningún procesamiento adicional.
  3. Continuar procesando la solicitud.
Kotlin
@RequestMapping
diversión myHandleMethod (intercambio: ServerWebExchange, modelo: Modelo): ¿Cadena? {
val Etiqueta electrónica: Largo = ... 
if (exchange.checkNotModified(eTag)) {
retorno nulo
}
model.addAttribute(...) 
retorno "myViewName"
}
  1. Cálculo específico de la aplicación.
  2. La respuesta se estableció en 304 (NOT_MODIFIED). No se produce ningún procesamiento adicional.
  3. Continuar procesando la solicitud.

Hay tres opciones para verificar las solicitudes condicionales con los valores de eTag, los valores de lastModified o ambos. Para solicitudes condicionales GET y HEAD, puede establecer la respuesta en 304 (NOT_MODIFIED). Para POST, PUT y DELETE condicionales, puede establecer la respuesta en 412 (PRECONDITION_FAILED) para evitar modificaciones simultáneas.

Recursos estáticos

Para un rendimiento óptimo, los recursos estáticos deben manejarse mediante Cache-Control y encabezados de respuesta condicionales. Consulte la sección sobre configuración de recursos estáticos.