Как переименовать ресурсы идемпотентным способом?

Я реализовал API, который переименовывает компанию следующим образом:

PUT /companies/A
{
  "name": "B"
}

вернусь HTTP 301 с Location заголовок, указывающий на новый URI компании: /companies/B,

Как я могу сделать эту операцию идемпотентной с и без If-Match заголовки?

  1. Без If-Match Заголовок: если пользователь пытается переименовать несуществующую компанию, я ожидаю, что сервер вернет HTTP 404 но я не могу этого сделать, потому что тогда законные операции переименования не будут идемпотентными (они вернутся 301 в первый раз, и 404 при последующих вызовах). Это проблематично, потому что я хочу, чтобы клиенты могли различать неудачные переименования (компания не существует) и переименование, которое уже имело место.

  2. С If-Match Заголовок: если компания ETag В зависимости от названия компании последующие операции переименования завершатся неудачно, поскольку предварительное условие больше не выполняется. Опять же, создается впечатление, что операция провалилась, хотя на самом деле она уже состоялась.

3 ответа

Решение

Операция PUT завершается успешно и должна возвращать 200 или 201. Последующие запросы для того же ресурса должны возвращать 301 с соответствующим телом ответа, указывающим URI нового ресурса.

404 должен быть только для ресурсов, которые действительно не могут быть найдены, то есть компаний, которые не существуют и никогда не существуют.

Как отмечено в протоколе, идемпотентность не означает, что вызов всегда возвращает одно и то же. Это означает, что нет никаких побочных эффектов. Кроме того, идемпотентность не применима в условиях ошибки, которая может отличаться от 2xx (например, 301).

Я действительно восхищаюсь обязательством сделать все правильно в соответствии со спецификацией, но, как и во всем, это подлежит интерпретации.

Хорошо, это два года, но я собираюсь ответить на него, если кто-то еще наткнется на это, как я.

Короткий ответ заключается в том, что с точки зрения HTTP переименование (перемещение) ресурсов не идемпотентно, и вы должны были использовать POST вместо PUT,

Длинный ответ: PUT является операцией "создать или заменить", определенной RFC 2616 следующим образом (выделено мной):

PUT Метод запрашивает, чтобы вложенный объект был сохранен под предоставленным Request-URI.

RFC 7231 (который в то время задавался этим вопросом, существовал только как черновик), формулирует это более четко:

PUT Метод запрашивает, чтобы состояние целевого ресурса было создано или заменено на состояние, определенное представлением, включенным в полезную нагрузку сообщения запроса. Успешный PUT данного представления предполагает, что последующее GET на том же целевом ресурсе приведет к отправке эквивалентного представления в 200 (OK) ответ.

Поскольку успешное переименование приведет к тому, что ресурс будет доступен в другом месте, PUT не применимо

PS. Возможно, вы могли бы сделать эту работу с PUT путем включения какого-либо уникального идентификатора компании, независимо от ее имени или других атрибутов, в тело запроса, которое позволит вам обнаружить предыдущие переименования и выполнить соответствующее перенаправление. Тем не менее, я думаю, что это идет вразрез с протоколом, и POST было бы более уместно.

Я не думаю, что 3xx имеет смысл здесь. Операция PUT выполнена успешно, поэтому она должна вернуть 2xx. 301 подразумевает, что ресурс находится не там, где, по его мнению, запрашивающий объект.

Вообще, меня продолжают удивлять люди, которые не используют MOVE, когда они действительно хотят MOVE:-)

Другие вопросы по тегам