Правильный HTTP-метод для обновления ресурса, не затрагивая подресурсы в REST

#web-services #api #rest

#веб-службы #API #rest

Вопрос:

Давайте предположим, что у меня есть две сущности — команда проекта и сотрудник. Каждый сотрудник может быть частью нескольких команд, и в каждой команде может быть несколько сотрудников в качестве членов команды. Мне нужно предоставить REST API для управления командами, сотрудниками и отношениями между ними.

Я определил 3 ресурса — team, employee и member (связь между team и employee), который является подресурсом team. Причина, по которой я решил использовать member в качестве подресурса, основана исключительно на жизненном цикле этого ресурса. Всякий раз, когда команда удаляется, участники удаляются, а также они не имеют значения за пределами самой команды.

Я предоставляю следующий API (соответствующий):

  • POST /teams создает новую запись команды с именем, идентификатором отдела и т.д.
  • POST /teams/{name}/members создает связь между командой, идентифицируемой по имени, и конкретным сотрудником, поэтому входные данные содержат идентификатор сотрудника

Мне также нужно предоставить API для обновления идентификатора отдела и других атрибутов команды в одном запросе. Похоже, что PUT — это естественный выбор, но семантика PUT довольно ясна — я должен заменить весь ресурс, что в данном случае означает также замену всех подресурсов-членов.

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

Ответ №1:

Похоже, что PUT — это естественный выбор, но семантика PUT довольно ясна — я должен заменить весь ресурс, что в данном случае означает также замену всех подресурсов-членов.

Я никогда раньше не слышал, чтобы кто-нибудь создавал эту ассоциацию. Если это сделать, PUT /Foo на мой взгляд, это абсолютно ничего не говорит о /Foo/bar . То, что к ресурсу можно получить доступ через иерархическое пространство URI, не подразумевает никаких дополнительных связей между этими ресурсами.

Я слышал о людях, выполняющих сценарий, противоположный тому, что делаете вы PUT /Foo/bar , и если сервер знает, что это повлияет на состояние /Foo , вы можете включить заголовок Content-Location, который указывает на /Foo , чтобы разрешить интеллектуальные кэши аннулировать /Foo . Однако местоположение содержимого необходимо для явного создания взаимосвязи между двумя ресурсами.

Комментарии:

1. это очень хороший момент. Я думаю, вы правы в том, что я думаю об URI как о строгом древовидном представлении набора ресурсов. Вместо этого я должен думать о них просто как о способе идентификации, где вложенность означает спецификацию пространства имен. Кстати, если я соглашусь с этим предположением, как будет выглядеть ответ GET для «команды»? Хорошо ли возвращать URI членов в теле или я должен возвращать только атрибуты team и не учитывать членов? Может быть, можно включить URI типа «/ teams / 123 / members»?

2. @Oleg Я бы включил ссылку на /teams/123/members в представление вашей команды.

3. спасибо, мне нужно еще одно уточнение, пожалуйста, при выполнении PUT to /teams/123 стоит ли опускать ссылку на /teams/123/members в тело запроса, или это собьет клиентов с толку?

4. @Oleg Элементы представления, которые не находятся под контролем клиента, такие как ссылки на гипермедиа, могут быть безопасно опущены из тела запроса PUT.

Ответ №2:

В идеале вы должны использовать метод PATCH, но не уверены, в какой реализации он используется. Если нет, вам следует выполнить прямой цикл GET -> locally modify -> PUT.

Также, в зависимости от вашего дизайна, вы можете интерпретировать, что подресурсы не являются частью самого ресурса. Например, содержимое ресурса team может содержать ссылку на список участников ресурса, скажем « /team/{name}/members «, но не содержать весь список в качестве содержащегося элемента.

Комментарии:

1. проблема в том, что использование GET-> modify-> PUT значительно усложнит логику на сервере. Он должен будет определить, какие ассоциации изменились, и соответствующим образом выполнить внутренние действия. Я бы предпочел передать в методе и теле намерение обновить только атрибуты team.

2. с другой стороны, хорошей идеей может быть не рассматривать элементы как отдельный ресурс. Поэтому, если кто-то хочет получить список участников, ему придется отправить отдельный запрос, в то время как POST / PUT в URI ‘teams’ всегда будет манипулировать только атрибутами team

Ответ №3:

Я вроде как думаю, что вы отвечаете на свой собственный вопрос. PUT предлагает создать ресурс, POST используемый для обновления.

Другой способ взглянуть на это заключается в том, что каждое изменение ресурса на самом деле «создает новую версию» ресурса. каждое создание, обновление и удаление добавляет новую версию с новыми свойствами, и эта версия имеет свой собственный уникальный идентификатор. Когда вы PUT устанавливаете новую версию существующего ресурса, старый ресурс остается, так что новый ресурс может наследовать от него. Попытки PUT установить новую версию на более старую версию, когда более новая версия этого ресурса уже существует, вернут перенаправление вместо успеха, как и некоторые GET запросы (те, которые не указывают, что они на самом деле ищут старую версию)