Разница POST и PUT в HTTP: когда использовать для создания
Разница между HTTP методами POST и PUT по RFC 2616: семантика, идемпотентность, статусы ответов. Когда использовать POST для коллекций, PUT для известного URI в REST API. Лучшие практики дизайна с поддержкой обоих методов.
В чем разница между методами HTTP POST и PUT, и когда каждый из них следует использовать для создания ресурса?
Фон:
- Согласно RFC 2616, § 9.5, POST используется для создания нового ресурса как подчиненного ресурсу, идентифицированному Request‑URI
- Согласно RFC 2616, § 9.6, PUT используется для создания или замены ресурса по указанному URI
Вопрос:
Какой метод HTTP следует использовать для создания ресурса, или оба должны поддерживаться в проектировании REST API?
Разница между методами HTTP POST и PUT заключается в их семантике по RFC 2616: POST предназначен для создания нового подчиненного ресурса в коллекции (сервер сам генерирует URI, метод не идемпотентен), а PUT — для создания или полной замены ресурса по точно известному клиенту URI (идемпотентен). Для создания ресурса в REST API стандартно используйте POST, но в хорошем дизайне поддерживайте оба метода — это дает гибкость: POST для типичных случаев, PUT для сценариев, где клиент заранее знает идентификатор. Так вы избегаете дубликатов и упрощаете интеграции.
Содержание
- Разница между методами HTTP POST и PUT по RFC 2616
- Семантика POST: создание подчиненного ресурса
- Семантика PUT: создание или замена по известному URI
- Идемпотентность и статусы ответов: POST vs PUT
- Когда использовать POST или PUT для создания ресурса в REST API
- Лучшие практики проектирования REST API с поддержкой обоих методов
- Источники
- Заключение
Разница между методами HTTP POST и PUT по RFC 2616
Представьте: вы строите REST API для пользователей. Клиент шлет данные — какой метод выбрать? Всё упирается в спецификацию RFC 2616, где §9.5 описывает POST, а §9.6 — PUT. POST не просто “отправка формы”, это запрос на обработку: сервер может создать ресурс как подчиненный указанному URI коллекции. PUT же — прямая команда: “положи или замени именно здесь”.
Вот таблица ключевых отличий из документации Microsoft Azure и Baeldung:
| Аспект | POST (§9.5 RFC) | PUT (§9.6 RFC) |
|---|---|---|
| Семантика | Запрос на обработку (создание в коллекции) | Замена/создание по точному URI |
| URI ресурса | Сервер генерирует (Location в ответе) | Клиент указывает заранее |
| Идемпотентность | Нет (повторы могут дублировать) | Да (повторы дают то же состояние) |
| Тело запроса | Любое, сервер интерпретирует | Полное представление ресурса |
Почему это важно? Без понимания разницы POST PUT ваш API превратится в хаос — клиенты будут путаться, а сервер тонуть в дублях.
А теперь подумаем: зачем вообще два метода, если оба создают ресурсы?
Семантика POST: создание подчиненного ресурса
POST — король создания в REST. По §9.5 RFC 2616: “The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI”. Проще: шлете на /users — сервер создаст /users/123 и вернет 201 Created с Location: /users/123.
Пример cURL:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Иван", "email": "ivan@example.com"}'
Ответ:
HTTP/1.1 201 Created
Location: https://api.example.com/users/123
{"id": 123, "name": "Иван"}
Сервер решает ID — удобно для UUID или автоинкремента. Но повтор запроса? Может создать второго Ивана. Не идемпотентно, вот почему клиенты добавляют If-None-Match или idempotency keys (как в Stripe).
В Stack Overflow Blog подчеркивают: POST для коллекций вроде /posts или /orders. Идеально для новых пользователей, статей, заказов.
Семантика PUT: создание или замена по известному URI
PUT строже. §9.6 RFC: “The PUT method requests that the enclosed entity be stored under the supplied Request-URI”. Если ресурса нет — создайте. Есть — замените целиком. Клиент должен знать URI заранее, например /users/123.
cURL-пример:
curl -X PUT https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-d '{"name": "Иван Иванов", "email": "ivan@new.com"}'
Ответ при создании:
HTTP/1.1 201 Created
{"id": 123, "name": "Иван Иванов"}
При замене:
HTTP/1.1 200 OK
{"id": 123, ...}
Повторите 10 раз — ничего не изменится. Идемпотентно! Полезно для “upsert” (update or insert), когда клиент генерирует ID (например, из внешней системы).
Но осторожно: PUT требует полного тела. Частичные обновления — на PATCH.
Идемпотентность и статусы ответов: POST vs PUT
Идемпотентность — святое в HTTP. PUT: повтор = то же состояние (200/204/201). POST: может создать дубли (202 Accepted или 409 Conflict с проверкой).
Таблица статусов из Baeldung и Azure:
| Сценарий | POST статусы | PUT статусы |
|---|---|---|
| Успешное создание | 201 Created | 201 Created |
| Уже существует | 200 OK / 409 Conflict | 200 OK / 204 No Content |
| Ошибка валидации | 400 Bad Request | 400 Bad Request |
| Повтор | Новый ресурс / 409 | То же (идемпотентно) |
В реальном коде (Spring Boot):
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
User saved = userService.save(user);
return ResponseEntity.created(URI.create("/users/" + saved.getId())).body(saved);
}
@PutMapping("/users/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
User updated = userService.save(user); // upsert
return ResponseEntity.ok(updated);
}
POST рискует дублями при сетевых сбоях. PUT — спасение для надежности.
Когда использовать POST или PUT для создания ресурса в REST API
Стандарт: POST для создания — 90% случаев. Клиент не знает ID, сервер генерирует. /users, /products — POST.
PUT, когда клиент знает URI: импорт данных, миграции, внешние ID (например, PUT /invoices/inv_abc123). Или для “атомарного upsert”.
Из Microsoft Azure: “Use POST to create new resources when the server assigns the URI. Use PUT when the client provides it”.
А если оба? Да! Гибкость: мобильные apps любят POST, enterprise-системы — PUT для скриптов.
Вопрос: а если ресурс уже есть? POST верните 409, PUT — 200.
Лучшие практики проектирования REST API с поддержкой обоих методов
Поддерживайте оба в REST API. Почему? Клиенты разные: браузеры — POST, CLI-инструменты — PUT.
- POST /users → 201, Location.
- PUT /users/{id} → 201/200/409 (если существует и не PUT).
Добавьте idempotency: хедер Idempotency-Key для POST.
В Node.js/Express:
app.post('/users', (req, res) => {
// Проверка на дубль по ключу
const user = userService.create(req.body);
res.status(201).location(`/users/${user.id}`).json(user);
});
app.put('/users/:id', (req, res) => {
const user = userService.upsert(req.params.id, req.body);
res.status(200).json(user);
});
Из Stack Overflow Blog: документируйте в OpenAPI, тестируйте повторами. Избегайте PUT для больших payload — лимит 1MB.
В 2026 году (на сегодня) API вроде GitHub, Stripe сочетают оба. Ваш API тоже сможет.
Источники
- API Design Best Practices — Рекомендации по методам HTTP в REST, таблицы сравнения и статусы: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
- REST HTTP PUT vs POST — Детальный разбор семантики RFC 2616 с примерами кода Spring: https://www.baeldung.com/rest-http-put-vs-post
- Best practices for REST API design — Практики поддержки POST и PUT в API, сценарии использования: https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/
Заключение
Разница POST и PUT проста, но мощна: POST для серверного создания в коллекции (не идемпотентен), PUT для клиентского URI (идемпотентен). В REST API используйте POST как основной для новых ресурсов, но обязательно добавьте PUT для гибкости — это сделает ваш дизайн надежным и удобным. Начните с таблиц статусов и примеров кода, протестируйте повторами, и клиенты скажут спасибо.