Gymterview
middle

Как работает кэширование в REST?

Кэширование — одно из шести ограничений REST, реализуемое через HTTP-заголовки Cache-Control, ETag и Last-Modified для снижения нагрузки на сервер и ускорения ответов клиенту.

1. Cache-Control

Заголовок, управляющий политикой кэширования:

Пример
Cache-Control: max-age=3600          — кэшировать на 1 час
Cache-Control: no-cache              — проверять актуальность при каждом запросе
Cache-Control: no-store              — не кэшировать вообще
Cache-Control: public, max-age=86400 — кэшировать публично на 24 часа
Cache-Control: private, max-age=600  — кэшировать только на клиенте

2. ETag (Entity Tag)

Сервер возвращает хеш содержимого ресурса. Клиент при повторном запросе отправляет его для проверки:

Пример
Первый запрос:
GET /api/users/42 → 200 OK, ETag: "33a64df..."

Повторный запрос:
GET /api/users/42, If-None-Match: "33a64df..."
→ 304 Not Modified (если ресурс не изменился)

3. Last-Modified / If-Modified-Since

Аналогичен ETag, но использует дату последнего изменения:

Пример
Ответ: Last-Modified: Fri, 10 Jan 2025 12:00:00 GMT
Повторный запрос: If-Modified-Since: Fri, 10 Jan 2025 12:00:00 GMT
→ 304 Not Modified (если не изменился)
Реализация в Spring

Shallow ETag (Spring автоматически считает ETag от тела ответа):

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
        FilterRegistrationBean<ShallowEtagHeaderFilter> filter =
            new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
        filter.addUrlPatterns("/api/*");
        return filter;
    }
}

Ручное управление кэшированием:

@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    User user = userService.findById(id);

    return ResponseEntity.ok()
        .cacheControl(CacheControl.maxAge(Duration.ofMinutes(30)))
        .eTag(String.valueOf(user.getVersion()))
        .lastModified(user.getUpdatedAt().toInstant())
        .body(user);
}

// Условный запрос с ETag
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id,
                                     WebRequest request) {
    User user = userService.findById(id);
    String etag = String.valueOf(user.getVersion());

    if (request.checkNotModified(etag)) {
        return null; // Spring вернёт 304 автоматически
    }

    return ResponseEntity.ok()
        .eTag(etag)
        .body(user);
}

На собеседовании: нужно знать три механизма (Cache-Control, ETag, Last-Modified) и код 304 Not Modified. Частая ошибка — путать no-cache (проверять актуальность) и no-store (не кэшировать вообще).