Веб

Разница POST и PUT в HTTP: когда использовать для создания

Разница между HTTP методами POST и PUT по RFC 2616: семантика, идемпотентность, статусы ответов. Когда использовать POST для коллекций, PUT для известного URI в REST API. Лучшие практики дизайна с поддержкой обоих методов.

1 ответ 76 просмотров

В чем разница между методами 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

Представьте: вы строите 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):

java
@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.

  1. POST /users → 201, Location.
  2. PUT /users/{id} → 201/200/409 (если существует и не PUT).

Добавьте idempotency: хедер Idempotency-Key для POST.

В Node.js/Express:

javascript
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 тоже сможет.


Источники

  1. API Design Best Practices — Рекомендации по методам HTTP в REST, таблицы сравнения и статусы: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
  2. REST HTTP PUT vs POST — Детальный разбор семантики RFC 2616 с примерами кода Spring: https://www.baeldung.com/rest-http-put-vs-post
  3. 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 для гибкости — это сделает ваш дизайн надежным и удобным. Начните с таблиц статусов и примеров кода, протестируйте повторами, и клиенты скажут спасибо.

Авторы
Проверено модерацией
Модерация
Разница POST и PUT в HTTP: когда использовать для создания