Какой HTTP метод использовать для создания заказа: POST или PUT
Что проверяют
Знаете ли вы разницу в семантике и идемпотентности и видите ли, почему для заказа она важна: дубль заказа означает двойное списание денег. Следом спрашивают, как защититься от повторной отправки.
Как отвечать
Ответ по умолчанию: POST /orders. Номер заказа присваивает сервер, клиент его заранее не знает, а POST как раз и означает «обработай эти данные по правилам коллекции». Возвращаем 201 Created, тело заказа и Location: /orders/{id}.
Затем объясните, чем отличается PUT:
- адресует конкретный ресурс:
PUT /orders/{id}значит «положи по этому адресу вот такое состояние». Если ресурса не было, он создаётся и ответ 201, если был, он заменяется целиком и ответ 200 или 204; - идемпотентен: десять одинаковых запросов оставят систему в том же состоянии, что и один. POST такой гарантии не даёт;
- требует полное представление: для частичной правки, например смены адреса доставки, есть
PATCH.
Отсюда вывод: PUT для создания уместен, когда id генерирует клиент, обычно UUID. Тогда повтор по таймауту безопасен.
Если остаётся POST, идемпотентность добавляют сами. Клиент шлёт заголовок Idempotency-Key, сервер сохраняет ключ вместе с результатом. На повтор с тем же ключом отдаётся сохранённый ответ, а на тот же ключ с другим телом запроса ошибка, обычно 422. В Go это делают через уникальный индекс в базе и INSERT ... ON CONFLICT DO NOTHING в PostgreSQL, а не через проверку «есть ли такой ключ» перед вставкой: между проверкой и вставкой успеет проскочить второй запрос.
Где спотыкаются
Говорят «PUT для обновления, POST для создания» как закон. Это упрощение: PUT создаёт ресурс, если клиент знает его адрес.
Используют PUT, но передают неполный объект. PUT заменяет состояние целиком, и поля, которых нет в теле, по смыслу метода должны пропасть из заказа.
Считают, что повторы решаются на фронте блокировкой кнопки. Повторяет не только пользователь, но и мобильная сеть, прокси и ретраи HTTP-клиента.