В чем разница между HTTP методами PUT() и PATCH()?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между HTTP методами PUT() и PATCH()
Оба метода используются для обновления ресурсов, но они работают по-разному. Это критически важное различие при проектировании REST API.
PUT (Replace / Full Update)
Определение: PUT заменяет весь ресурс на сервере на отправленные данные. Это полное обновление.
Семантика:
- Отправляешь полное представление ресурса
- Сервер заменяет весь ресурс на отправленные данные
- Если какие-то поля отсутствуют в запросе, они либо удаляются, либо устанавливаются в default значения
- Идемпотентный: несколько одинаковых PUT запросов дают тот же результат
Пример:
Путь 1: Исходное состояние заказа
GET /api/v1/orders/123
{
"id": "123",
"status": "pending",
"customer_name": "Ivan",
"amount": 1000,
"address": "Moscow",
"notes": "Leave at door"
}
Путь 2: Отправляем PUT с ПОЛНЫМ заказом
PUT /api/v1/orders/123
Content-Type: application/json
{
"id": "123",
"status": "confirmed",
"customer_name": "Ivan",
"amount": 1000,
"address": "Moscow",
"notes": "Leave at door"
}
Результат: Заказ ПОЛНОСТЬЮ обновлен со всеми полями
Важный момент: Если в PUT запросе отсутствует поле, оно может быть удалено или установлено в default:
Путь 3: Отправляем PUT БЕЗ notes
PUT /api/v1/orders/123
Content-Type: application/json
{
"id": "123",
"status": "confirmed",
"customer_name": "Ivan",
"amount": 1000,
"address": "Moscow"
// notes отсутствует!
}
Результат: Может быть
- notes удалено (notes: null)
- ИЛИ notes установлено в default значение
- Зависит от реализации API
PATCH (Partial Update)
Определение: PATCH обновляет только указанные поля ресурса, остальные остаются неизменными. Это частичное обновление.
Семантика:
- Отправляешь только изменённые поля (дельта)
- Сервер обновляет только эти поля
- Остальные поля остаются как были
- Может быть идемпотентным (зависит от операции)
Пример:
Исходное состояние:
{
"id": "123",
"status": "pending",
"customer_name": "Ivan",
"amount": 1000,
"address": "Moscow",
"notes": "Leave at door"
}
Отправляем PATCH с ОДНИМ изменением
PATCH /api/v1/orders/123
Content-Type: application/json
{
"status": "confirmed"
}
Результат: ТОЛЬКО status обновлён
{
"id": "123",
"status": "confirmed", // обновлено
"customer_name": "Ivan", // без изменений
"amount": 1000, // без изменений
"address": "Moscow", // без изменений
"notes": "Leave at door" // без изменений
}
Таблица сравнения
| Параметр | PUT | PATCH |
|---|---|---|
| Тип обновления | Полное (replace) | Частичное (merge) |
| Что отправляешь | Весь ресурс | Только изменённые поля |
| Неупомянутые поля | Удаляются / default | Остаются как были |
| Идемпотентность | Всегда идемпотентный | Может быть, может нет |
| Размер payload | Больше | Меньше |
| Сложность реализации | Проще | Сложнее |
| Когда использовать | Замена всего ресурса | Небольшие изменения |
Практические примеры
Сценарий 1: Обновление профиля пользователя
Исходные данные:
{
"user_id": "f47ac10b",
"first_name": "Ivan",
"last_name": "Petrov",
"email": "ivan@example.com",
"phone": "+7-999-123-45-67",
"bio": "Software Engineer",
"avatar_url": "https://example.com/avatar.jpg"
}
Если использовать PUT для изменения только имени:
ПЫ, ОЙ! Забыли avatar_url, bio, phone
PUT /api/v1/users/f47ac10b
{
"user_id": "f47ac10b",
"first_name": "Ivan NEW",
"last_name": "Petrov",
"email": "ivan@example.com",
"phone": "+7-999-123-45-67",
"bio": "Software Engineer",
"avatar_url": "https://example.com/avatar.jpg"
}
Результат: Корректно (если отправили весь объект)
НО если забыли avatar_url:
avatar_url может быть удалён! ❌
Если использовать PATCH (правильно):
PATCH /api/v1/users/f47ac10b
{
"first_name": "Ivan NEW"
}
Результат:
{
"user_id": "f47ac10b",
"first_name": "Ivan NEW", // обновлено
"last_name": "Petrov", // без изменений
"email": "ivan@example.com", // без изменений
"phone": "+7-999-123-45-67", // без изменений
"bio": "Software Engineer", // без изменений
"avatar_url": "...", // без изменений ✓
}
Сценарий 2: Обновление статуса заказа
Используем PUT (полное обновление):
Если мы хотим изменить status, priority и notes:
Полный заказ с изменениями
PUT /api/v1/orders/123
{
"id": "123",
"customer_id": "cust-456",
"items": [{...}],
"status": "shipped", // изменено
"priority": "high", // изменено
"notes": "Ready", // изменено
"created_at": "2025-03-28T10:00:00Z",
"updated_at": "2025-03-28T15:30:00Z"
}
Рисковано: если забыть items, created_at - они могут быть удалены!
Используем PATCH (правильно):
Только изменения
PATCH /api/v1/orders/123
{
"status": "shipped",
"priority": "high",
"notes": "Ready"
}
Результат: Только эти 3 поля изменены, остальное как было
Сценарий 3: Замена конфигурации (PUT уместен)
Когда PUT имеет смысл:
Если ты заменяешь всю конфигурацию системы (например, настройки приложения):
PUT /api/v1/config
{
"app_name": "MyApp",
"theme": "dark",
"language": "ru",
"timeout": 30,
"debug_mode": false,
"features": {
"feature_a": true,
"feature_b": false
}
}
Всё содержимое конфига заменяется ✓
Это правильное использование PUT
Идемпотентность
PUT всегда идемпотентный:
1-й раз: PUT /orders/123 -> успех
2-й раз: PUT /orders/123 (точный же запрос) -> тот же результат
3-й раз: PUT /orders/123 -> тот же результат
Результат одинаковый независимо от количества вызовов ✓
PATCH может не быть идемпотентным:
1-й раз: PATCH /orders/123 {"amount": 1000} -> успех
2-й раз: PATCH /orders/123 {"amount": 1000} -> тот же результат
НО если это операция типа {"amount": +100} (прибавить 100)
1-й раз: amount = 1000 + 100 = 1100
2-й раз: amount = 1100 + 100 = 1200
Неидемпотентный! ✗
JSON Merge Patch (RFC 7386) vs JSON Patch (RFC 6902)
Есть два способа описать PATCH операции:
JSON Merge Patch (простой):
Content-Type: application/merge-patch+json
{
"status": "shipped",
"notes": null // явно удалить поле
}
Ресурс MERGE'ится с отправленными данными
JSON Patch (более мощный):
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/status", "value": "shipped" },
{ "op": "add", "path": "/tags/-", "value": "urgent" },
{ "op": "remove", "path": "/notes" },
{ "op": "copy", "path": "/notes_backup", "from": "/notes" }
]
Больше control, но сложнее
Best Practices при проектировании API
1. Используй PATCH для частичных обновлений (по умолчанию)
// Правильно ✓
PATCH /api/v1/users/:id
Content-Type: application/json
{
"email": "newemail@example.com"
}
// Не требуешь отправлять все поля
2. Используй PUT когда заменяешь весь ресурс
// Правильно ✓
PUT /api/v1/documents/:id
Content-Type: application/json
{
// весь документ целиком
"title": "...",
"content": "...",
"author": "...",
// если поле отсутствует, оно удаляется
}
3. Требуй полный объект в PUT запросе
// PUT: требуй весь объект
PUT /api/v1/users/123 -> 400 Bad Request если отсутствуют поля
// PATCH: принимай любые поля
PATCH /api/v1/users/123 -> 200 OK для partial update
4. Документируй в API спецификации
PUT /users/{id}:
description: Replace entire user resource
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/User'
example:
id: 123
name: "John"
email: "john@example.com"
# all fields required
PATCH /users/{id}:
description: Partial update of user
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
email:
type: string
# no required fields
Мой подход в проектировании API
В проекте логистики я использовал:
-
PATCH как основной метод для большинства обновлений
- Меньше payload
- Безопаснее (не забудешь поле)
- Более гибкий
-
PUT только для специальных случаев:
- Замена конфигурации целиком
- Замена документа/контракта целиком
- Операции, которые требуют полного overwrite
-
Всегда документирую разницу в API spec
-
Использую JSON Merge Patch для простоты (он по умолчанию поддерживается браузерами)
Это дало нам более надёжный и user-friendly API.