HTTP Caching
HTTP 缓存可以显著提高 Web 应用程序的性能。HTTP 缓存围绕 Cache-Control
响应头以及随后的条件请求头(例如 Last-Modified
和 ETag
)。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 aCache-Control
response header. -
A
0
value prevents caching by using the ’Cache-Control: no-store'` directive. -
An
n > 0
value caches the given response forn
seconds by using the ’Cache-Control: max-age=n'` directive.
Controllers
控制器可以添加对 HTTP 缓存的显式支持。我们建议这样做,因为必须在将资源的 lastModified
或 ETag
值与条件请求标头进行比较之前计算出该值。控制器可以将 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) 响应。否则,将 ETag
和 Cache-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.
|
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
值或同时针对两者检查条件请求。对于条件 GET
和 HEAD
请求,可以将响应设置为 304 (NOT_MODIFIED)。对于条件 POST
、PUT
和 DELETE
,可以将响应设置为 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.