Разница между HTTP POST и PUT: когда использовать для создания
Разница между методами HTTP POST и PUT по RFC 7231: семантика, идемпотентность, статусы. Когда использовать POST для создания ресурса в REST API (сервер генерирует URI) и PUT (клиент указывает URI). Лучшие практики и примеры.
В чем разница между методами HTTP POST и PUT, и когда каждый из них следует использовать для создания ресурса?
Фон:
- Согласно RFC 2616, § 9.5, POST используется для создания нового ресурса как подчиненного ресурсу, идентифицированному Request‑URI
- Согласно RFC 2616, § 9.6, PUT используется для создания или замены ресурса по указанному URI
Вопрос:
Какой метод HTTP следует использовать для создания ресурса, или оба должны поддерживаться в проектировании REST API?
Разница между методами HTTP POST и PUT заключается в их семантике: POST предназначен для обработки запроса сервером с генерацией URI для нового ресурса (не идемпотентен), а PUT — для создания или полной замены ресурса по точно указанному клиентом URI (идемпотентен). Для создания ресурса в REST API используйте POST, если URI неизвестен заранее (сервер присваивает ID), и PUT — когда клиент заранее знает путь, но оба метода стоит поддерживать для гибкости. Согласно RFC 7231, POST предпочтительнее для типичного создания в коллекциях, как подчеркивают рекомендации Microsoft и MDN.
Содержание
- Разница между POST и PUT по RFC 7231
- Семантика POST: когда использовать для создания ресурса
- Семантика PUT: создание по известному URI
- POST vs PUT в REST API: лучшие практики
- Идемпотентность, статусы ответов и безопасность
- Должны ли API поддерживать оба метода для создания?
- Источники
- Заключение
Разница между POST и PUT по RFC 7231
Представьте: вы строите REST API, и нужно создать пользователя. Какой метод выбрать — POST или PUT? Ответ кроется в стандарте RFC 7231, который обновил устаревший RFC 2616. Там четко разделены роли.
POST (§4.3.3) — это “общая обработка”. Сервер сам решает, что делать: создать ресурс, запустить процесс или даже отправить email. URI запроса обычно указывает коллекцию (/users), а новый ресурс получает свой путь (/users/123), который сервер возвращает в Location. Не идемпотентен: повторный POST может создать дубликат.
PUT (§4.3.4) — точечная операция. Клиент говорит: “Создай или замени именно по этому URI (/users/123)”. Если ресурса нет — создай. Повтор? Ничего не меняется, полная идемпотентность. Тело запроса должно полностью описывать ресурс.
| Аспект | POST | PUT |
|---|---|---|
| URI ресурса | Коллекция (сервер генерирует) | Конкретный (клиент указывает) |
| Идемпотентность | Нет (дубликаты возможны) | Да (повторы безопасны) |
| Статус при создании | 201 Created + Location | 201 Created (Location опционально) |
| Тело | Частичное/полное (сервер решает) | Полное (замена) |
Почему это важно? Неправильный выбор ломает клиенты. Браузеры кэшируют GET, но POST/PUT требуют осторожности. В MDN подчеркивают: путайте на свой страх.
А фон из вашего вопроса? RFC 2616 (§9.5/9.6) похож, но 7231 точнее: POST для “подчиненных” ресурсов коллекций, PUT — прямая загрузка по URI.
Семантика POST: когда использовать для создания ресурса
POST — король создания в большинстве API. Почему? Потому что клиент редко знает ID заранее. Отправьте POST /users с JSON {name: “Иван”}, сервер создаст /users/456 и вернет 201 Created, Location: /users/456.
Пример curl:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Иван", "email": "ivan@example.com"}'
Ответ: 201, тело с данными, Location в заголовке. Идеально для коллекций: /posts, /orders.
Но не идемпотентен. Повторите запрос — может родиться второй Иван. Решение? Используйте idempotency-key в заголовке (Stripe так делает). MDN POST рекомендует для форм, коллекций, неатомарных операций.
Когда POST? Всегда для “создай новый, сам придумай ID”. В 90% случаев — ваш выбор.
Семантика PUT: создание по известному URI
PUT реже для чистого создания, но крут, когда URI предопределен. Клиент знает: “Загрузи аватар по /users/123/avatar”. Если нет — создай.
Пример:
curl -X PUT https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-d '{"name": "Иван Петров", "age": 30}'
Если /users/123 пуст — 201 Created. Уже есть? 200 OK, полная замена. Идемпотентно: жмите сколько угодно.
MDN PUT предупреждает: тело должно быть полным, иначе данные сотрутся. Не для частичных обновлений (там PATCH).
Когда PUT для создания? Редко: если ID из внешнего источника (UUID от клиента) или загрузка файлов по фиксированному пути. В Baeldung примеры показывают: PUT /articles/my-known-slug.
Но осторожно: клиенты не всегда знают URI. Поэтому не основной метод.
POST vs PUT в REST API: лучшие практики
В REST нет строгого “только POST”. Roy Fielding (отец REST) в StackOverflow советует POST для создания. Microsoft в API design то же: POST /customers, сервер дает ID.
Практика:
- POST /users → создай, получи /users/{id}.
- PUT /users/{id} → замени/создай по ID.
Из Postman блога: правило большого пальца — POST, если сервер решает URI. Restfulapi.net добавляет: POST для детей коллекций.
Таблица best practices:
| Сценарий | POST | PUT |
|---|---|---|
| Новый пользователь в /users | Да | Нет (ID неизвестен) |
| Обновить/создать /users/known-uuid | Нет | Да |
| Загрузка файла /files/fixed-name | Нет | Да |
GitHub, Twitter API — POST для твитов/репозиториев. Почему? Масштаб, безопасность.
Идемпотентность, статусы ответов и безопасность
Идемпотентность — ключ. PUT: повторите — то же. POST: может удвоить заказы. Решение: ключи вроде X-Idempotency-Key.
Статусы:
- Создание: 201 Created (оба).
- Уже есть (PUT): 200/204 OK.
- Конфликт: 409 (дубликат email).
Безопасность: PUT раскрывает URI, уязвим к угадыванию (/users/1). POST прячет. TheServerSide отмечает: PUT для trusted клиентов.
Тестируйте в Postman: повторите PUT — мирно, POST — хаос без ключей.
Что если трафик? Идемпотентные PUT проще для retry в сетях.
Должны ли API поддерживать оба метода для создания?
Да, поддерживайте оба. Зачем? Гибкость. Большинство — POST /resource. Но для миграций или клиентов с UUID — PUT /{id}.
StackOverflow Blog: POST для append, PUT для set. Stripe, AWS — оба есть.
Минусы двойной поддержки? Документация. Swagger: опишите четко.
В итоге: базовый API — POST. Продвинутый — +PUT. Клиенты спасибо скажут.
Источники
- RFC 7231 — Стандарт семантики HTTP/1.1, разница POST/PUT: https://datatracker.ietf.org/doc/html/rfc7231
- 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
- Microsoft API Design Best Practices — Рекомендации POST для создания в REST: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
- Baeldung: PUT vs POST — Сравнение с примерами кода и статусами: https://www.baeldung.com/rest-http-put-vs-post
- Stack Overflow: POST vs PUT — Обсуждение от Roy Fielding, практика REST: https://stackoverflow.com/questions/630453/what-is-the-difference-between-post-and-put-in-http
- Postman Blog: PUT vs POST — Правила выбора для API-дизайна: https://blog.postman.com/put-vs-post/
Заключение
Разница POST и PUT проста, но критична: POST для сервер-генерируемого URI при создании ресурса (основной в REST API), PUT — для клиентского URI с идемпотентностью. Поддерживайте оба для полного покрытия сценариев, следуя RFC 7231 и best practices от Microsoft/MDN. Начните с POST — и ваш API станет надежным, масштабируемым. Экспериментируйте в sandbox, увидите разницу на деле.