В чём разница между PUT и PATCH?
Оба метода используются для обновления ресурса, но работают по-разному.
Junior Level
Оба метода используются для обновления ресурса, но работают по-разному.
PUT — Полное обновление
- Заменяет всё представление ресурса на то, что прислал клиент. Поля, которых нет в теле запроса, должны быть удалены или сброшены в значения по умолчанию.
- Нужно отправить все поля, даже те, что не изменились
- Если не отправить какое-то поле — оно будет удалено или установлено в null
PUT /users/1
{
"name": "Ivan",
"age": 30,
"email": "ivan@example.com"
}
PATCH — Частичное обновление
- Обновляет только те поля, которые были отправлены
- Экономит трафик: для объекта в 200 полей изменение одного поля через PATCH экономит ~95% тела запроса.
PATCH /users/1
{
"age": 31
}
// Остальные поля остаются без изменений
Сравнение:
| Критерий | PUT | PATCH |
|---|---|---|
| Объём данных | Полный объект | Только изменённые поля |
| Идемпотентность | Всегда идемпотентен | Зависит от реализации |
| Аналогия в SQL | UPDATE всех колонок | UPDATE конкретных колонок |
Когда PUT лучше PATCH
Используйте PUT для полных замен небольших объектов — проще в реализации и однозначно. Используйте PATCH для больших объектов или когда несколько клиентов могут параллельно менять разные поля.
Middle Level
Семантика методов
PUT (Replace)
- Идемпотентность: Всегда идемпотентен. Сколько бы раз ни отправить один и тот же запрос — результат одинаковый
- DB Analogy:
UPDATE users SET name=?, age=?, email=? ...(обновление всех колонок)
PATCH (Partial Update)
- Идемпотентность: Не гарантируется. Зависит от реализации
- DB Analogy:
UPDATE users SET age = age + 1(не идемпотентно) илиSET age = 30(идемпотентно)
Спецификации PATCH
- JSON Merge Patch (RFC 7386):
- Клиент шлёт JSON:
{"age": 31} - Сервер просто мержит его с текущим объектом
- Минус: Сложно удалить поле (нужно договариваться, что
null— это удаление)
Ключевое отличие: Merge Patch говорит «что изменить» (декларативно), а JSON Patch говорит «как изменить» (последовательность операций: add, remove, replace).
- Клиент шлёт JSON:
- JSON Patch (RFC 6902):
- Клиент шлёт массив инструкций:
[ { "op": "replace", "path": "/age", "value": 31 }, { "op": "remove", "path": "/middleName" } ] - Плюс: Атомарность и гибкость. Можно добавлять элементы в массивы, удалять конкретные поля
- Клиент шлёт массив инструкций:
Оптимистическая блокировка (ETag)
- Клиент получает ресурс с заголовком
ETag: "v1" - Клиент шлёт
PUT/PATCHс заголовкомIf-Match: "v1" - Если
ETagстал “v2” — сервер вернёт 412 Precondition Failed
Диагностика
- 422 Unprocessable Entity: Если PATCH логически невыполнима (например, удаление обязательного поля)
- Hibernate/JPA: При PATCH используйте
nullValuePropertyMappingStrategy = IGNOREв MapStruct, чтобы случайно не затереть поля
Senior Level
Проблема “Lost Updates” и Optimistic Locking
Что если два пользователя одновременно прочитали ресурс (age=20) и решили его обновить?
- PUT-проблема: Первый обновил до 21, второй — до 25. Изменения первого потеряны.
- PATCH-решение: Если PATCH делает
age = age + 1, итог будет 22 (оба изменения учтены).
Производительность и Highload
- Bandwidth: PATCH экономит трафик на больших объектах (профиль с сотнями полей)
- Validation: PUT проще валидировать (полная схема). PATCH требует сложной логики валидации “только присланных полей”
- Reflection в Java: Реализация PATCH часто требует Jackson
JsonNodeдля определения, было ли полеnullили отсутствовало
Реализация PATCH в Spring
Будьте осторожны с merge() в Hibernate. Если создать новый объект и вызвать merge(), можно случайно затереть поля, не пришедшие в PATCH, превратив его в кривой PUT.
Edge Cases
- Если PATCH содержит
{"op": "add", "path": "/tags", "value": "new"}— это не идемпотентно (при повторе добавится ещё один тег) - Для идемпотентности PATCH используйте детерминированные операции (replace, remove)
🎯 Шпаргалка для интервью
Обязательно знать:
- PUT заменяет ресурс целиком — поля, которых нет в запросе, удаляются или обнуляются
- PATCH обновляет только присланные поля — экономит трафик на больших объектах
- PUT всегда идемпотентен; PATCH идемпотентен только при детерминированных операциях
- JSON Merge Patch (RFC 7386) — декларативный подход: «что изменить»
- JSON Patch (RFC 6902) — императивный подход: массив операций (add, remove, replace)
- Optimistic Locking через ETag + If-Match предотвращает конфликт параллельных обновлений
- PUT проще валидировать (полная схема), PATCH требует сложной валидации частичных данных
Частые уточняющие вопросы:
- Что такое Lost Update Problem? — Два клиента читают ресурс и обновляют параллельно; изменения первого теряются
- Как реализовать PATCH в Hibernate? — Использовать nullValuePropertyMappingStrategy = IGNORE в MapStruct
- Что вернёт сервер при 412 Precondition Failed? — ETag изменился с момента чтения, ресурс устарел
- Когда PUT лучше PATCH? — Для небольших объектов и когда нужна однозначная полная замена
Красные флаги (НЕ говорить):
- «PATCH всегда идемпотентен» — add в коллекцию не идемпотентен
- «PUT обновляет только переданные поля» — PUT заменяет весь объект целиком
- «Разницы между PUT и PATCH нет» — PUT = полная замена, PATCH = частичное обновление
- «Merge Patch и JSON Patch — одно и то же» — Merge Patch декларативна, JSON Patch — последовательность операций
Связанные темы:
- [[Что такое идемпотентность (idempotency)]]
- [[Какие HTTP методы идемпотентны]]
- [[Какие основные HTTP методы используются в REST]]
- [[Как правильно именовать REST endpoints]]