HTTP Caching

HTTP 缓存可以显著提高 Web 应用程序的性能。HTTP 缓存围绕 Cache-Control 响应头以及随后的条件请求头(例如 Last-ModifiedETag)。Cache-Control 为私有(例如浏览器)和公有(例如代理)缓存如何缓存和重新使用响应提供建议。如果内容没有更改,则 ETag 头用于进行条件请求,这可能会导致没有正文的 304(未修改)。ETag 可以看作是 Last-Modified 头的一个更精细的后继。

HTTP caching can significantly improve the performance of a web application. HTTP caching revolves around the Cache-Control response header and, subsequently, conditional request headers (such as Last-Modified and ETag). Cache-Control advises private (for example, browser) and public (for example, proxy) caches on how to cache and re-use responses. An ETag header is used to make a conditional request that may result in a 304 (NOT_MODIFIED) without a body, if the content has not changed. ETag can be seen as a more sophisticated successor to the Last-Modified header.

本节介绍 Spring Web MVC 中提供的与 HTTP 缓存相关的选项。

This section describes the HTTP caching-related options that are available in Spring Web MVC.

CacheControl

CacheControl 提供对与 Cache-Control 头部相关的设置的支持,并且被当作参数接受在多个位置:

CacheControl provides support for configuring settings related to the Cache-Control header and is accepted as an argument in a number of places:

尽管https://datatracker.ietf.org/doc/html/rfc7234#section-5.2.2[RFC 7234] 描述了 Cache-Control 响应头部的所有可能指令,但 CacheControl 类型采取以用例为导向的方法,专注于常见场景:

While RFC 7234 describes all possible directives for the Cache-Control response header, the CacheControl type takes a use case-oriented approach that focuses on the common scenarios:

  • Java

  • Kotlin

// Cache for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);

// Prevent caching - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();

// Cache for ten days in public and private caches,
// public caches should not transform the response
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();
// Cache for an hour - "Cache-Control: max-age=3600"
val ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS)

// Prevent caching - "Cache-Control: no-store"
val ccNoStore = CacheControl.noStore()

// Cache for ten days in public and private caches,
// public caches should not transform the response
// "Cache-Control: max-age=864000, public, no-transform"
val ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic()

WebContentGenerator 还接受一个更简单的 cachePeriod 属性(以秒为单位定义),其工作原理如下:

WebContentGenerator also accepts a simpler cachePeriod property (defined in seconds) that works as follows:

  • A -1 value does not generate a Cache-Control response header.

  • A 0 value prevents caching by using the ’Cache-Control: no-store'` directive.

  • An n > 0 value caches the given response for n seconds by using the ’Cache-Control: max-age=n'` directive.

Controllers

控制器可以添加对 HTTP 缓存的显式支持。我们建议这样做,因为必须在将资源的 lastModifiedETag 值与条件请求标头进行比较之前计算出该值。控制器可以将 ETag 标头和 Cache-Control 设置添加到 ResponseEntity 中,如下例所示:

Controllers can add explicit support for HTTP caching. We recommended doing so, since the lastModified or ETag value for a resource needs to be calculated before it can be compared against conditional request headers. A controller can add an ETag header and Cache-Control settings to a ResponseEntity, as the following example shows:

  • Java

  • Kotlin

@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);
}
@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)
}

前一个示例在比较与条件请求标头的情况下表明内容未更改时,发送一个正文为空的 304 (NOT_MODIFIED) 响应。否则,将 ETagCache-Control 标头添加到响应中。

The preceding example sends a 304 (NOT_MODIFIED) response with an empty body if the comparison to the conditional request headers indicates that the content has not changed. Otherwise, the ETag and Cache-Control headers are added to the response.

也可以在控制器中对条件请求标头进行检查,如下例所示:

You can also make the check against conditional request headers in the controller, as the following example shows:

Java
@RequestMapping
public String myHandleMethod(WebRequest request, Model model) {

	long eTag = ... (1)

	if (request.checkNotModified(eTag)) {
		return null; (2)
	}

	model.addAttribute(...); (3)
	return "myViewName";
}
1 Application-specific calculation.
2 The response has been set to 304 (NOT_MODIFIED) — no further processing.
3 Continue with the request processing.
Kotlin
@RequestMapping
fun myHandleMethod(request: WebRequest, model: Model): String? {

	val eTag: Long = ... (1)

	if (request.checkNotModified(eTag)) {
		return null (2)
	}

	model[...] = ... (3)
	return "myViewName"
}
4 Application-specific calculation.
5 The response has been set to 304 (NOT_MODIFIED) — no further processing.
6 Continue with the request processing.

有三个变体用于针对 eTag 值、lastModified 值或同时针对两者检查条件请求。对于条件 GETHEAD 请求,可以将响应设置为 304 (NOT_MODIFIED)。对于条件 POSTPUTDELETE,可以将响应设置为 412 (PRECONDITION_FAILED),以防止并发修改。

There are three variants for checking conditional requests against eTag values, lastModified values, or both. For conditional GET and HEAD requests, you can set the response to 304 (NOT_MODIFIED). For conditional POST, PUT, and DELETE, you can instead set the response to 412 (PRECONDITION_FAILED), to prevent concurrent modification.

Static Resources

你应当利用 Cache-Control 和条件响应头部提供静态资源以实现最佳性能。查阅配置 Static Resources 这一部分。

You should serve static resources with a Cache-Control and conditional response headers for optimal performance. See the section on configuring Static Resources.

ETag Filter

你可以使用 ShallowEtagHeaderFilter 添加 “shallow” eTag 值,这些值是从响应内容中计算得出的,从而能够节省带宽,但不能节省 CPU 时间。请参阅 Shallow ETag

You can use the ShallowEtagHeaderFilter to add “shallow” eTag values that are computed from the response content and, thus, save bandwidth but not CPU time. See Shallow ETag.