Spring Web Reactive | 1. Spring WebFlux | 1.10. HTTPキャッシュ

Web MVC

HTTPキャッシュは、Webアプリケーションの性能を大幅に向上させます。中心となるのはCache-Controlレスポンスヘッダーと、Last-ModifiedETagなどの条件付きリクエストヘッダーです。Cache-Controlは、ブラウザーなどのプライベートキャッシュや、プロキシーなどのパブリックキャッシュに、レスポンスのキャッシュと再利用方法を指示します。ETagヘッダーを使用すると、コンテンツが変更されていない場合にボディなしの304(NOT_MODIFIED)を返す条件付きリクエストを作成できます。ETagLast-Modifiedより精密な代替手段と考えられます。

このセクションでは、Spring WebFluxで利用できるHTTPキャッシュの選択肢を説明します。

1.10.1. CacheControl

Web MVC

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. コントローラー

Web MVC

コントローラーはHTTPキャッシュを明示的にサポートできます。条件付きリクエストヘッダーと比較する前に、リソースのlastModifiedまたはETag値を計算する必要があるため、この方法を推奨します。ResponseEntityETagCache-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)レスポンスを送信します。それ以外の場合は、ETagCache-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) リクエスト処理を続行します。

条件付きリクエストをeTaglastModified、または両方で確認する3つの形式があります。条件付きGETHEADリクエストでは304(NOT_MODIFIED)を返せます。条件付きPOSTPUTDELETEでは、同時変更を防ぐために412(PRECONDITION_FAILED)を返せます。

1.10.3. 静的リソース

Web MVC

最適な性能を得るには、Cache-Controlと条件付きレスポンスヘッダーを使用して静的リソースを提供します。静的リソースの設定セクションを参照してください。