Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между PUT и PATCH: полное обновление vs частичное
При проектировании API часто встаёт вопрос: как обновить существующий ресурс? И здесь есть два методов — PUT и PATCH — которые решают одну задачу по-разному. Это важное различие для правильного дизайна API.
PUT: полная замена ресурса
PUT — это HTTP метод для полной замены существующего ресурса новыми данными. Семантика: "возьми этот ресурс и замени его полностью на то, что я отправляю".
Пример: обновление профиля пользователя
Исходное состояние:
GET /api/v1/users/user_123
Response: {
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"country": "USA",
"age": 30
}
PUT запрос (полная замена):
PUT /api/v1/users/user_123
Content-Type: application/json
Body: {
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+0987654321",
"country": "UK",
"age": 28
}
Response: 200 OK
{
"id": "user_123",
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+0987654321",
"country": "UK",
"age": 28
}
Важное правило PUT: Если в Body не отправить какое-то поле, оно удалится или вернёться к значению по умолчанию. Это полная замена!
Перед: {"name": "John", "email": "john@ex.com", "phone": "+123"}
PUT с: {"name": "Jane", "email": "jane@ex.com"}
После: {"name": "Jane", "email": "jane@ex.com", "phone": null}
PATCH: частичное обновление
PATCH — это HTTP метод для частичного обновления ресурса. Семантика: "обнови только те поля, которые я отправляю, остальное оставь как было".
Пример: обновление профиля пользователя
Исходное состояние (то же):
{
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"country": "USA",
"age": 30
}
PATCH запрос (только изменение):
PATCH /api/v1/users/user_123
Content-Type: application/json
Body: {
"phone": "+0987654321",
"country": "UK"
}
Response: 200 OK
{
"id": "user_123",
"name": "John Doe", ← НЕ изменилось
"email": "john@example.com", ← НЕ изменилось
"phone": "+0987654321", ← ИЗМЕНИЛОСЬ
"country": "UK", ← ИЗМЕНИЛОСЬ
"age": 30 ← НЕ изменилось
}
По сравнению с PUT, мы отправляем только то, что хотим изменить!
Таблица сравнения
| Аспект | PUT | PATCH |
|---|---|---|
| Семантика | Полная замена | Частичное обновление |
| Обязательные поля | Все поля требуются | Только изменяемые поля |
| Отсутствующие поля | Удаляются/сбрасываются | Остаются как были |
| Idempotent | Да (100%) | Да (обычно) |
| Body | Полный ресурс | Только дельта |
| Когда использовать | Редактирование формы целиком | Быстрое обновление одного поля |
| Производительность | Отправляем больше данных | Отправляем минимум данных |
| Сложность реализации | Проще | Сложнее (нужна merge логика) |
Когда использовать PUT
1. Обновление целой формы
Когда пользователь редактирует весь профиль целиком:
<form>
<input value="John Doe" name="name">
<input value="john@ex.com" name="email">
<input value="+123456" name="phone">
<input value="USA" name="country">
<button>Save All</button> ← PUT
</form>
2. Замена конфигурации
Когда обновляешь всю конфигурацию приложения сразу:
Прежде:
{"theme": "dark", "lang": "en", "notifications": "all"}
PUT /api/v1/settings
{"theme": "light", "lang": "ru", "notifications": "none"}
После:
{"theme": "light", "lang": "ru", "notifications": "none"}
3. Синхронизация состояния
Когда мобильное приложение отправляет весь актуальный state:
PUT /api/v1/sync/user_state
Body: [полное состояние приложения]
Когда использовать PATCH
1. Быстрые изменения одного поля
Пользователь изменил только email:
PATCH /api/v1/users/user_123
{"email": "newemail@example.com"}
Не нужно отправлять все остальные поля!
2. Обновления из разных источников
Администратор и пользователь одновременно могут обновлять разные поля:
Админ:
PATCH /api/v1/users/user_123
{"status": "suspended"}
Пользователь:
PATCH /api/v1/users/user_123
{"phone": "+9999999"}
Результат: оба изменения сохранены
3. Условные обновления
Обновить только если выполнено условие:
PATCH /api/v1/orders/order_123
If-Match: "etag_v2"
Body: {"status": "shipped"}
Если etag совпадает → обновляем
Если нет (был изменён другим процессом) → 412 Precondition Failed
4. API для частичных обновлений
Внутренние сервисы часто используют PATCH для микро-обновлений:
PATCH /api/v1/users/user_123
Body: [{"op": "replace", "path": "/phone", "value": "+123"}]
Это RFC 6902 (JSON Patch) стандарт.
Реальные примеры из моей работы
Сценарий 1: Обновление профиля пользователя
Пользователь нажимает "Edit Profile". Какой метод использовать?
Ответ: зависит от интерфейса
- Если форма показывает все поля → PUT (пользователь редактирует весь профиль)
- Если форма показывает только изменённые поля → PATCH (быстрое обновление)
Сценарий 2: API для мобильного приложения
Мобильное приложение может отправлять только изменённые поля:
ПОШУЛся:
GET /api/v1/users/me
Response: {"name": "John", "email": "john@ex.com", "age": 30}
Пользователь изменил возраст на 31
PATCH /api/v1/users/me
Body: {"age": 31} ← только это
Делает запрос меньше, батарея дольше держит.
Сценарий 3: Конфликты в системе
Когда несколько процессов обновляют разные поля:
Процесс A: Процесс B:
PATCH /users/123 PATCH /users/123
{"name": "Alice"} {"email": "alice@ex.com"}
Результат:
{"name": "Alice", "email": "alice@ex.com"}
Оба изменения сохранены! (благодаря PATCH)
С PUT было бы конфликт:
Процесс A:
Прочитал: {"name": "John", "email": "john@ex.com"}
Изменил: {"name": "Alice", "email": "john@ex.com"}
Отправил PUT
Процесс B (в то же время):
Прочитал: {"name": "John", "email": "john@ex.com"}
Изменил: {"name": "John", "email": "alice@ex.com"}
Отправил PUT
Результат: потеря данных (overwrite)
Best Practice для API Design
1. Используй PUT для:
- Полное обновление ресурса
- Когда client отправляет полное состояние
- Когда отсутствующее поле = удалить значение
2. Используй PATCH для:
- Частичное обновление
- Когда client отправляет только дельту
- Когда отсутствующее поле = оставить как было
3. Реакция на отсутствие поля:
# PUT реализация
def update_user_put(user_id, data):
user = User.get(user_id)
user.name = data.get('name') # Если нет → None
user.email = data.get('email')
user.phone = data.get('phone', None) # Явно None
user.save()
# PATCH реализация
def update_user_patch(user_id, data):
user = User.get(user_id)
for key, value in data.items():
setattr(user, key, value) # Обновляем только отправленные
user.save()
Идемпотентность обоих методов
Оба PUT и PATCH должны быть идемпотентными — повторный вызов с теми же данными даёт тот же результат:
Первый раз: PATCH /users/123 {"age": 31} → age = 31
Второй раз: PATCH /users/123 {"age": 31} → age = 31 (не меняется)
Это критично для надежности (если запрос отправился дважды — no problem).
Мой выбор в реальных проектах
В большинстве современных API я предпочитаю PATCH:
✓ Меньше данных в сети ✓ Меньше risk конфликтов ✓ Проще для микросервисной архитектуры ✓ Лучше для мобильных приложений
Но PUT остаётся полезным для: ✓ Редиса кэши (Redis часто использует full replace) ✓ Configurations (всегда замена конфига полностью) ✓ Legacy систем
Выбор между PUT и PATCH — это часть стратегического дизайна API, и я всегда обсуждаю это с архитектором при планировании.