🌐 Раздел 6 · Вопрос #3

Какие основные HTTP методы используются в REST

В архитектурном стиле REST методы сопоставляются с CRUD-операциями над данными:


🟢 Junior Level

HTTP-методы (HTTP Verbs / глаголы HTTP) — это стандартные команды протокола HTTP (спецификация RFC 9110), которые сообщают серверу, какое действие клиент хочет произвести над указанным ресурсом (URI).

В архитектурном стиле REST методы сопоставляются с CRUD-операциями над данными:

HTTP Метод CRUD-операция Назначение Пример URI Успешный статус-код
GET Read Получение представления ресурса (чтение) GET /api/v1/orders/42 200 OK
POST Create Создание нового дочернего ресурса или вызов действия POST /api/v1/orders 201 Created
PUT Update (Replace) Полная замена ресурса по указанному адресу PUT /api/v1/orders/42 200 OK / 204 No Content
PATCH Update (Partial) Частичное обновление отдельных полей ресурса PATCH /api/v1/orders/42 200 OK / 204 No Content
DELETE Delete Удаление указанного ресурса DELETE /api/v1/orders/42 204 No Content

Пример реализации REST-контроллера на Spring Boot 3

package com.example.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;
import java.util.List;

@RestController
@RequestMapping("/api/v1/products")
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    // 1. GET: чтение списка или одного элемента
    @GetMapping("/{id}")
    public ResponseEntity<ProductDto> getProduct(@PathVariable Long id) {
        return ResponseEntity.ok(productService.findById(id));
    }

    // 2. POST: создание ресурса с заголовком Location
    @PostMapping
    public ResponseEntity<ProductDto> createProduct(@RequestBody CreateProductRequest req) {
        ProductDto created = productService.create(req);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(created.id())
                .toUri();
        return ResponseEntity.created(location).body(created); // 201 Created + Location
    }

    // 3. PUT: полная замена ресурса
    @PutMapping("/{id}")
    public ResponseEntity<ProductDto> replaceProduct(@PathVariable Long id, 
                                                    @RequestBody ReplaceProductRequest req) {
        return ResponseEntity.ok(productService.replace(id, req));
    }

    // 4. PATCH: частичное обновление
    @PatchMapping("/{id}")
    public ResponseEntity<ProductDto> patchProduct(@PathVariable Long id, 
                                                  @RequestBody PatchProductRequest req) {
        return ResponseEntity.ok(productService.patch(id, req));
    }

    // 5. DELETE: удаление ресурса
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
        productService.delete(id);
        return ResponseEntity.noContent().build(); // 204 No Content
    }
}

🟡 Middle Level

Классификация методов: Безопасность (Safe) и Идемпотентность (Idempotent)

По спецификации RFC 9110 (HTTP Semantics) все методы разделяются по двум фундаментальным характеристикам:

                          ┌─── Safe (Безопасный: не меняет состояние на сервере): GET, HEAD, OPTIONS, TRACE
             ┌─── Свойства ┤
             │             └─── Idempotent (Идемпотентный: повтор N раз дает тот же итог): GET, HEAD, PUT, DELETE, OPTIONS
HTTP Методы ┤
             │             ┌─── PUT: Полная замена (все поля обязательны)
             └─── Мутации  ┼─── PATCH: Частичное обновление (RFC 6902 / RFC 7386)
                           └─── POST: Создание / Запуск бизнес-операции (НЕ идемпотентен!)

Сводная матрица свойств методов

Метод Наличие тела запроса Безопасный (Safe)? Идемпотентный (Idempotent)? Кэшируемый (Cacheable)?
GET ❌ Нет ✅ Да ✅ Да ✅ Да (по умолчанию)
HEAD ❌ Нет ✅ Да ✅ Да ✅ Да
OPTIONS ❌ Нет ✅ Да ✅ Да ❌ Нет
POST ✅ Да ❌ Нет ❌ Нет ⚠️ Только с явным Cache-Control
PUT ✅ Да ❌ Нет ✅ Да ❌ Нет
PATCH ✅ Да ❌ Нет ⚠️ Зависит от операции ❌ Нет
DELETE ⚠️ Не рекомендуется ❌ Нет ✅ Да ❌ Нет

Вспомогательные методы: HEAD и OPTIONS

  • HEAD: выполняет точно такой же запрос, как GET, но сервер возвращает только HTTP-заголовки без тела ответа. Применение: проверка размера тяжелого файла (Content-Length) или валидность кэша (ETag, Last-Modified) до начала реальной загрузки.
  • OPTIONS: опрос коммуникационных возможностей сервера. Применение: механизм CORS Preflight в браузерах. Перед отправкой нестандартного запроса браузер шлет OPTIONS, а сервер возвращает разрешенные методы (Access-Control-Allow-Methods: GET, POST, PUT, DELETE) и заголовки.

🔴 Senior Level

Тонкости семантики PUT vs PATCH

  1. Строгий контракт PUT (Полная замена):
    • Если отправлен PUT /users/42 с телом {"name": "Ivan"}, а у пользователя в БД было заполнено поле email: "ivan@test.com", бэкенд обязан затереть email в null, поскольку PUT замещает ресурс целиком. Если поле требуется сохранить, клиент обязан прислать его заново.
  2. Спецификации PATCH:
    • JSON Merge Patch (RFC 7386): клиент шлет только изменяемые поля: {"email": "new@mail.com"}. Проблема: как отличить «поле не передано (не менять)» от «поле передано как null (стереть значение)»? В Spring Boot это решается через модуль org.openapitools.jackson.nullable.JsonNullable<T>.
    • JSON Patch (RFC 6902): отправляется декларативный массив атомарных операций (add, remove, replace, copy, move, test):
      [
        { "op": "replace", "path": "/email", "value": "new@mail.com" },
        { "op": "remove", "path": "/phoneNumber" }
      ]
      

Автоматические повторы (Retries) в Service Mesh (Envoy / Istio)

В распределенных микросервисных системах знание идемпотентности методов критично при настройке сетевых политик:

  • Безопасные ретраи (Safe Retries): При получении ошибки сети (503 Service Unavailable, Connection Reset) прокси Envoy / Sidecar может автоматически повторить запросы GET, PUT, DELETE без риска испортить данные.
  • Опасность повторов для POST: Автоматический повтор запроса POST категорически запрещен (риск двойного списания денег со счета или дублирования заказа). Для безопасного повтора POST в архитектуру внедряют заголовок Idempotency-Key с сохранением уникальных ключей в Redis.

Туннелирование методов (X-HTTP-Method-Override)

Некоторые устаревшие корпоративные прокси-серверы, файрволы (WAF) и клиенты блокируют методы PUT, PATCH или DELETE.

  • Для обхода ограничений используют туннелирование через POST с заголовком:
    POST /api/v1/orders/42 HTTP/1.1
    X-HTTP-Method-Override: DELETE
    
  • В Spring Boot поддержка туннелирования включается через бин HiddenHttpMethodFilter.

🎯 Шпаргалка для интервью

30-секундный ответ

В REST используются 5 базовых методов HTTP (RFC 9110):

  • GET — чтение ресурса (Safe, Idempotent).
  • POST — создание подчиненного ресурса или триггер действия (Неидемпотентный).
  • PUT — полная замена ресурса по указанному URI (Idempotent).
  • PATCH — частичное обновление полей ресурса (обычно неидемпотентный).
  • DELETE — удаление ресурса (Idempotent).

Вспомогательные методы: HEAD (заголовки без тела для проверки кэша и размера) и OPTIONS (опрос возможностей сервера и CORS Preflight).

4 каверзных вопроса с ответами

  1. В чём принципиальное различие между свойствами Safe (Безопасный) и Idempotent (Идемпотентный)? Ответ: Метод является Safe, если он вообще не изменяет состояние данных на сервере (только чтение: GET, HEAD, OPTIONS). Метод является Idempotent, если его повторные идентичные вызовы приводят к тому же конечному состоянию данных на сервере, что и первый вызов. Например, DELETE не является безопасным (он модифицирует БД), но он идемпотентен (после 1-го или 100-го вызова сущность в БД удалена).

  2. Если второй вызов DELETE /users/1 возвращает 404 Not Found, нарушается ли идемпотентность? Ответ: Нет, не нарушается. Идемпотентность по RFC 9110 определяется конечным состоянием данных на сервере (Resource State), а не статус-кодом HTTP-ответа. После первого и после второго запроса пользователя с ID 1 в системе нет — значит, состояние сервера не изменилось, и метод идемпотентен.

  3. Зачем нужен метод HEAD и когда его используют на практике? Ответ: Метод HEAD возвращает абсолютно идентичные заголовки, что и GET, но с гарантированно пустым телом ответа. Он незаменим для:
    • Проверки доступности ресурса (200 vs 404) без передачи трафика.
    • Проверки размера файла перед скачиванием через заголовок Content-Length.
    • Проверки валидности клиентского кэша через проверку заголовков ETag или Last-Modified.
  4. Что произойдет, если в запросе PUT не передать одно из существующих полей сущности? Ответ: По контракту PUT производит полную замену ресурса. Сервер обязан затереть непереданное поле в null (или выставить дефолтное значение). Если клиент хочет обновить только часть полей, не трогая остальные, он обязан использовать метод PATCH.

Красные флаги (чего категорически нельзя говорить)

  • ❌ «PUT и PATCH делают одно и то же, это синонимы» — PUT полностью замещает ресурс, PATCH обновляет только указанные поля.
  • ❌ «DELETE неидемпотентен, потому что второй запрос возвращает ошибку 404» — статус ответа может меняться, важно состояние сервера.
  • ❌ «GET можно использовать для удаления или изменения записей в базе» — грубое нарушение спецификации Safe Methods.
  • ❌ «Метод POST идемпотентен из коробки» — POST принципиально неидемпотентен и без Idempotency-Key дублирует действия при повторах.

Связанные темы