Чтобы включить идентификатор ресурса в полезную нагрузку или извлечь из URI

13

Разрабатывая API, мы столкнулись с вопросом, должна ли полезная нагрузка PUT содержать идентификатор обновляемого ресурса.

Вот что у нас сейчас есть:

PUT /users/123 Payload: {name: "Adrian"}

Наш код маршрута извлекает идентификатор из URI и продолжает обновление.

Первые пользователи нашего API задаются вопросом, почему мы не разрешаем ID в полезной нагрузке:

PUT /users/123 Payload: {id: 123, name: "Adrian"}

Причина, по которой мы этого не допустили, заключается в том, что идентификатор дублируется в полезной нагрузке и URI.

Размышляя об этом еще немного, мы связываем ресурс с URI.

Если URI не имеет идентификатора, необходимо изменить полезную нагрузку:

PUT /no/id/here Payload: {name: "Adrian"} < What user???

Есть ли причины не делать этого?

Адриан Линч
источник

Ответы:

14

Вы должен соединять Единый ресурс идентификатор к ресурсу .

Когда REST реализован с HTTP, вы используете GET для получения текущего значения ресурса и PUT для установки нового значения. GET не имеет полезной нагрузки, поэтому ресурс должен быть идентифицирован по URI. И PUT логически выполняется для того же URI, и полезная нагрузка должна выглядеть точно так, как вы хотите, чтобы следующий GET возвратил.

Вы можете использовать POST для разных URI, но это будет иметь меньше смысла, так как будет излишне асимметричным для GET. POST для общего URI может иметь смысл только для создания новых ресурсов ( POST /users/new, payload:, {name: "Adrian"}response {id: 345, name: "Adrian"}), но это не идемпотентно и поэтому следует избегать если вы стремитесь к ОТДЫХУ¹. Вместо этого вы должны зарезервировать идентификатор для одного вызова, а затем использовать PUT для установки нового идентификатора; это отказоустойчиво, потому что, если первый запрос завершается неудачно, резервирование идентификатора может в конечном итоге истечь, и PUTон идемпотентен. Или используйте клиентский UUID.


¹ Определение REST ничего не говорит о идемпотентности, поэтому я не могу утверждать, что это не REST, если у вас неидемпотентные операции. Это не меняет того факта, что соблюдение идемпотентных запросов делает вещи более надежными, не усложняя их, и поэтому рекомендуется.

Ян Худек
источник
3
Насколько я знаю, запрос POST не должен быть идемпотентным. Так что я не вижу проблем с публикацией в /users(нет необходимости добавлять «новый»).
lex82
Спасибо за ответ. Я вижу, что это хорошая возможность иметь идемпотентность для всех запросов, но кто сказал, что это требуется для REST API? Конечно, многие API, которые называют себя RESTful, допускают неидемпотентные POST-запросы (особенно, когда сервер генерирует идентификаторы для новых ресурсов). Но даже если вы применяете очень строгое определение REST, я не вижу, какое архитектурное ограничение нарушается неидемпотентными POST, как в примере выше.
lex82
Я, конечно, могу изобразить POST-запросы, которые нарушают ограничение. Я просто не понимаю, почему размещение ресурса в коллекции и разрешение серверу определять его идентификатор является нарушением ограничений REST.
lex82
3
Вся идея POST не в том, чтобы быть идемпотентом ....
EralpB
2

Размышляя об этом еще немного, мы связываем ресурс с URI.

Если URI не имеет идентификатора, необходимо изменить полезную нагрузку:

PUT / no / id / here Полезная нагрузка: {name: "Adrian"} <Какой пользователь ???

Есть ли причины не делать этого?

Ответ на этот вопрос зависит от того, хотите ли вы разрешить клиенту изменить идентификатор?

Если клиент может изменить идентификатор через PUT, то URI для ресурса изменится, и вы должны предоставить 301 Moved Permanently каждый раз, когда ресурс обращается к старому URI.

Так, например, вы начинаете с ресурса в

/users/123

и клиент помещает следующее на ресурс

{id: 222, name: "Adrian"}

ресурс обновлен, и теперь его URI

/users/222

LocationПоле в ответ PUT должен содержать новый URI, и если вы идете /users/123вы должны получить 301ответ с полем Расположение указывает на новый /users/222ресурс.

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

Если вы помещаете требование в другой URI на том же ресурсе, скажем,

/users/adian_lync

тогда, если этот ресурс не существует, сервер должен создать его и создать и ID, когда он это делает

Кормак Мулхолл
источник
1
Причина, по которой мы подвергли сомнению размещение идентификатора в полезной нагрузке, была вызвана тем, что Backbone.js по умолчанию передал идентификатор в запрос PUT. Мы можем остановить это, но теперь я хочу знать, почему это поведение по умолчанию.
Адриан Линч
1
Боюсь, я не знаком с Backbone.js. Кажется ненужным, если идентификатор также включен в URL. Возможно, недосмотр со стороны разработчиков
Кормак Мулхолл,