Как переименовать ресурсы идемпотентным способом?
Я реализовал API, который переименовывает компанию следующим образом:
PUT /companies/A
{
"name": "B"
}
вернусь HTTP 301
с Location
заголовок, указывающий на новый URI компании: /companies/B
,
Как я могу сделать эту операцию идемпотентной с и без If-Match
заголовки?
Без
If-Match
Заголовок: если пользователь пытается переименовать несуществующую компанию, я ожидаю, что сервер вернетHTTP 404
но я не могу этого сделать, потому что тогда законные операции переименования не будут идемпотентными (они вернутся301
в первый раз, и404
при последующих вызовах). Это проблематично, потому что я хочу, чтобы клиенты могли различать неудачные переименования (компания не существует) и переименование, которое уже имело место.С
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:-)