Разница POST и PUT: когда использовать для создания ресурса
Разница между HTTP методами POST и PUT по RFC 9110: семантика, идемпотентность, статус-коды. Когда использовать POST для коллекций и PUT для известных URI в REST API. Best practices проектирования с примерами curl.
В чем разница между методами HTTP POST и PUT, и когда каждый из них следует использовать для создания ресурса?
Фон:
- Согласно RFC 2616, § 9.5, POST используется для создания нового ресурса как подчиненного ресурсу, идентифицированному Request‑URI
- Согласно RFC 2616, § 9.6, PUT используется для создания или замены ресурса по указанному URI
Вопрос:
Какой метод HTTP следует использовать для создания ресурса, или оба должны поддерживаться в проектировании REST API?
Разница между методами HTTP POST и PUT проста, но критична для REST API: POST создаёт новый ресурс как подчинённый указанному URI (сервер генерирует финальный URI в Location), не идемпотентен и подходит для коллекций вроде /users. PUT же создаёт или полностью заменяет ресурс точно по клиентскому URI, строго идемпотентен и идеален, когда URI известен заранее, например /users/123. В проектировании REST API оба метода стоит поддерживать — POST для большинства случаев создания (стандарт ~90%), PUT для предсказуемых URI с повторной безопасностью.
Содержание
- Разница между POST и PUT по RFC
- Когда использовать POST для создания ресурса
- Когда использовать PUT для создания ресурса
- POST vs PUT в REST API: идемпотентность и статус-коды
- Best practices проектирования REST API
- Источники
- Заключение
Разница между POST и PUT по RFC
Представьте: клиент шлёт запрос на сервер. Что дальше? Всё зависит от семантики, закреплённой в RFC 9110 — это актуальный стандарт после RFC 2616. POST (§9.3.3) не просто “отправка данных”: он запрашивает обработку (часто создание подчинённого ресурса), где Request-URI — это коллекция или обработчик, а сервер сам решает URI для новичка и возвращает его в Location. Не идемпотентен: повтор вызовет дубли (новые ресурсы).
PUT (§9.3.4) — полная противоположность. Клиент точно знает URI ресурса и говорит: “Создай или замени именно здесь”. Сервер обязан выполнить атомарно: если ресурса нет — создай (201 Created), если есть — замени целиком (200 OK или 204 No Content). Идемпотентность на первом месте: сколько ни повторяй, результат один.
| Аспект | POST | PUT |
|---|---|---|
| URI в запросе | Коллекция/обработчик (сервер генерирует URI) | Точный ресурс (клиент задаёт URI) |
| Семантика | Запрос на обработку/создание подчинённого | Создание или замена по URI |
| Идемпотентность | Нет (дубли возможны) | Да (повторы безопасны) |
| Тело запроса | Любое (JSON, форма, файл) | Полное представление ресурса |
Почему это важно? Без понимания разницы POST PUT ваш API превратится в спагетти: клиенты будут путаться, а retry-логика сломается. RFC подчёркивает: POST для расширений (БД вставка), PUT — для чистого CRUD.
Когда использовать POST для создания ресурса
POST — король создания в 90% случаев. Почему? Клиент редко знает URI заранее. Представьте /api/users: шлёшь данные юзера, сервер создаёт /api/users/456 и кидает 201 Created с Location: /api/users/456. Просто, гибко.
Сценарии:
- Коллекции: POST /posts — новый пост в блоге.
- Подчинённые ресурсы: POST /orders/123/items — товар в заказ.
- Расширения: Загрузка файла, обработка формы (не чистый ресурс).
Пример curl:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Иван", "email": "ivan@example.com"}' \
--verbose
Ответ: 201 Created, Location: /users/789. Повторишь? Получишь второго Ивана — вот цена неидемпотентности. Но MDN по POST подтверждает: это норма для новых сущностей.
А если дубли не нужны? Добавьте Idempotency-Key в хедер — современный хак для безопасности.
Когда использовать PUT для создания ресурса
PUT сияет, когда URI предопределён. Клиент говорит: “Я знаю, куда класть — /users/uuid-123”. Сервер создаёт, если пусто, или перезаписывает. Идеально для:
- Клиент-генерируемых ID: UUID в URI (/documents/550e8400).
- Замены: Часто PUT = update, но RFC позволяет create.
Пример:
curl -X PUT https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-d '{"name": "Иван", "email": "ivan@example.com"}'
Ответ: 201 (новый) или 200/204 (существующий). Повтори — ничего не сломается! MDN по PUT отмечает: тело должно быть полным представлением, частичные изменения — PATCH.
Но осторожно: если /users/123 занято — 409 Conflict. PUT не для коллекций — зачем клиенту гадать свободный ID?
POST vs PUT в REST API: идемпотентность и статус-коды
Вот где разница POST PUT бьёт ключом. Идемпотентность: PUT — да (N запросов = 1 эффект), POST — нет (N = N ресурсов). Retry после таймаута? PUT безопасен, POST рискует дублями.
Статусы:
| Метод | Создание (новый) | Обновление (существующий) | Конфликт |
|---|---|---|---|
| POST | 201 Created (Location) | 200/201 (зависит от логики) | 409/422 |
| PUT | 201 Created | 200 OK / 204 No Content | 409 Conflict |
Из Baeldung: POST для серверного ID, PUT для клиентского. Postman блог добавляет: правило “POST — сервер URI, PUT — клиент URI”.
В реальности? Клиенты (Postman, curl) ожидают предсказуемости. Без идемпотентности микросервисы взорвутся на сетевых сбоях.
Best practices проектирования REST API
Поддерживайте оба — это консенсус. Microsoft API design советует: POST /orders для коллекций, PUT /articles/uuid для известных. Roy Fielding (REST автор) на StackOverflow: “Prefer POST, PUT только если URI known”.
Правила:
- POST /resources — стандарт для create.
- PUT /resources/{id} — create/update, если ID от клиента (UUID).
- Всегда Location в 201.
- Идемпотентность: Key для POST, native для PUT.
- Документируйте в OpenAPI.
Из restfulapi.net: в 95% API POST доминирует, PUT — ниша. Но гибкость окупается: клиенты счастливы, масштабируемость на уровне.
Ошибки новичков? PUT без 409 — перезапись чужого! POST без Location — слепой полёт.
Источники
- RFC 9110 — Официальный стандарт HTTP семантики POST и PUT: https://httpwg.org/specs/rfc9110.html
- MDN Web Docs: POST — Подробности использования POST для создания ресурсов: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/POST
- MDN Web Docs: PUT — Семантика PUT, идемпотентность и статус-коды: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/PUT
- RESTful API: PUT vs POST — Сравнение для REST, рекомендации по URI: https://restfulapi.net/rest-put-vs-post/
- Microsoft Azure API Design — Best practices для POST/PUT в коллекциях: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
- Stack Overflow: POST vs PUT — Обсуждение с ссылкой на Fielding: https://stackoverflow.com/questions/630453/what-is-the-difference-between-post-and-put-in-http
- Baeldung: REST PUT vs POST — Практические примеры и статусы: https://www.baeldung.com/rest-http-put-vs-post
Заключение
Разница POST vs PUT — в контроле URI и идемпотентности: POST для серверных ID и коллекций (основной выбор для создания), PUT для клиентских URI с безопасными повторами. В REST API проектируйте оба — это обеспечит гибкость, как рекомендуют RFC, MDN и Microsoft. Начните с POST, добавьте PUT для продвинутых сценариев, и ваши клиенты скажут спасибо. Вопросы? Тестируйте в Postman!