Spring Web Reactive | 1. Spring WebFlux | 1.10. HTTPキャッシュ
HTTPキャッシュは、Webアプリケーションの性能を大幅に向上させます。中心となるのはCache-Controlレスポンスヘッダーと、Last-ModifiedやETagなどの条件付きリクエストヘッダーです。Cache-Controlは、ブラウザーなどのプライベートキャッシュや、プロキシーなどのパブリックキャッシュに、レスポンスのキャッシュと再利用方法を指示します。ETagヘッダーを使用すると、コンテンツが変更されていない場合にボディなしの304(NOT_MODIFIED)を返す条件付きリクエストを作成できます。ETagはLast-Modifiedより精密な代替手段と考えられます。
このセクションでは、Spring WebFluxで利用できるHTTPキャッシュの選択肢を説明します。
1.10.1. CacheControl
CacheControlは、Cache-Controlヘッダーの設定を支援し、複数の場所で引数として使用できます。
RFC 7234は、Cache-Controlレスポンスヘッダーで使用できるすべてのディレクティブを説明しています。CacheControlは一般的なシナリオに焦点を当てたユースケース指向の方法を提供します。
Java
// 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();
Kotlin
// 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()
1.10.2. コントローラー
コントローラーはHTTPキャッシュを明示的にサポートできます。条件付きリクエストヘッダーと比較する前に、リソースのlastModifiedまたはETag値を計算する必要があるため、この方法を推奨します。ResponseEntityへETagとCache-Controlの設定を追加できます。
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)
}
条件付きリクエストヘッダーと比較してコンテンツが変更されていない場合、この例は空のボディを持つ304(NOT_MODIFIED)レスポンスを送信します。それ以外の場合は、ETagとCache-Controlヘッダーをレスポンスへ追加します。
コントローラーで条件付きリクエストヘッダーを確認することもできます。
Java
@RequestMapping
public String myHandleMethod(ServerWebExchange exchange, Model model) {
long eTag = ... // (1)
if (exchange.checkNotModified(eTag)) {
return null; // (2)
}
model.addAttribute(...); // (3)
return "myViewName";
}
Kotlin
@RequestMapping
fun myHandleMethod(exchange: ServerWebExchange, model: Model): String? {
val eTag: Long = ... // (1)
if (exchange.checkNotModified(eTag)) {
return null // (2)
}
model.addAttribute(...) // (3)
return "myViewName"
}
- (1) アプリケーション固有の方法で計算します。
- (2) レスポンスを304(NOT_MODIFIED)に設定し、処理を終了します。
- (3) リクエスト処理を続行します。
条件付きリクエストをeTag、lastModified、または両方で確認する3つの形式があります。条件付きGETとHEADリクエストでは304(NOT_MODIFIED)を返せます。条件付きPOST、PUT、DELETEでは、同時変更を防ぐために412(PRECONDITION_FAILED)を返せます。
1.10.3. 静的リソース
最適な性能を得るには、Cache-Controlと条件付きレスポンスヘッダーを使用して静的リソースを提供します。静的リソースの設定セクションを参照してください。