Ресурсы, методы и коды состояния — Примеры

Пример 1 — Возврат — это ресурс

Команде поддержки нужен способ вернуть деньги за заказ, и первый черновик добавляет операцию в путь заказа и сообщает результат одним кодом успеха. Перепишите запрос и ответ так, чтобы адрес обозначал вещь, статус говорил, что произошло, а клиент мог найти возврат, не угадывая.

POST /orders/refundOrder
{ "order_id": 42, "amount": "12.50" }

200 OK
{ "success": true }

Решение

POST /orders/42/refunds
Idempotency-Key: refund-42-1
{ "amount": "12.50", "reason": "damaged" }

201 Created
Location: /orders/42/refunds/7

{ "id": 7, "order_id": 42, "amount": "12.50", "status": "pending" }

Теперь возврат — вещь, принадлежащая заказу: адрес говорит, что было создано, а не какое действие выполнено, статус сообщает, что новый ресурс существует, а заголовок location несёт его адрес, поэтому клиент ни опрашивает сервер, ни конструирует идентификатор. Однако черновик возвращает один и тот же код успеха и когда возврат принят, и когда заказ уже был возвращён, — и каждый клиент вынужден делать особый случай. Обычно ошибаются с чтением, которое меняет состояние: такое чтение выполнит предзагрузчик.

Пример 2 — Повтор, который не должен вернуть деньги дважды

Клиент перестал ждать ответа о возврате и отправил запрос снова, сервер создал второй возврат, и клиенту заплатили дважды. Журнал ниже — из того дня. Напишите запрос и поведение сервера, которые делают повтор безопасным.

14:02:11  POST /orders/42/refunds   201  id=7
14:02:11  ...клиент дождался тайм-аута, ответ так и не пришёл
14:02:12  POST /orders/42/refunds   201  id=8
14:02:12  тревога: заказ 42 возвращён дважды        (иллюстративно)

Решение

14:02:11  POST /orders/42/refunds
          Idempotency-Key: refund-42-1        201  id=7
14:02:12  POST /orders/42/refunds
          Idempotency-Key: refund-42-1        201  id=7

сервер записывает ключ вместе с первым результатом и возвращает его без изменений

Клиент передаёт ключ, который обозначает попытку, а не момент времени; сервер хранит ключ рядом с результатом, поэтому вторая доставка возвращает первый исход и ничего не записывает, — и тайм-аут можно считать неизвестным, а не фатальным. Обычно ошибаются иначе: ключ генерируют на каждую попытку, из-за чего повтор всякий раз выглядит новой работой.