Рекомендации по возвращению представления ресурса, который также является коллекцией


Предположим, я хочу сделать спокойный интерфейс, и я хочу работать с foos на основе их идентификаторов. Здесь нет ничего нового:

  • GET /api/foo1 возвращает представление (например, с помощью JSON) foo1.
  • DELETE /api/foo1 удаляет foo1.

И т. д.

Теперь позвольте мне сказать вам, что" фу " - это вещь коллекционного типа. Поэтому я хочу иметь возможность добавить " бар " к "foo":
  • PUT /api/foo1/bar3 добавляет bar3 к foo1.
  • GET /api/foo1/bar3 возвращает представление foo1.
  • DELETE /api/foo1/bar3 удаляет bar3 из foo1.
  • DELETE /api/foo1 удаляет foo1 полностью.

Теперь остается вопрос: Что значит GET /api/foo1 делать? Возвращает ли он просто представление foo1, как я первоначально предполагал в этом вопросе? Или он возвращает список баров? Или он возвращает представление foo1, которое одновременно является описанием foo1 , а также включает список всех содержащихся баров?

Или должен GET /api/foo1 просто возвращать представление foo1 как я предполагал в начале, и требуется запрос PROPFIND, чтобы перечислить бары внутри foo1 (подход, принятый WebDAV)? Но тогда, чтобы быть последовательным, не придется ли мне изменить всю мою другую функциональность типа списка на PROPFIND, прямо противореча всем тем тысячам спокойных учебников, которые говорят использовать GET /api/foo1 для перечисления содержимого?

3 7

3 ответа:

После некоторого размышления я думаю, что лучшее концептуальное объяснение с точки зрения покоя состоит в том, что обычно "вещь" - это не то же самое, что ее "коллекция". Таким образом, в то время как в мире WebDAV directory/ может быть то же самое, что содержит его файлы, в спокойном мире у меня может быть отдельный directory/files/ подпут для содержащихся файлов. Таким образом, я могу управлять каталогами отдельно от файлов, которые содержат.

Рассмотрим RESTful API для фермы, содержащей амбары. Конечная точка farm/api/barns/ может быть возвращен список амбаров, один из которых будет farm/api/barns/bigredbarn. Наивно я думал, что извлечение farm/api/barns/bigredbarn/ даст мне список животных в сарае, что и вызвало этот вопрос.

Но на самом деле животные в амбаре-это только один аспект Большого Красного амбара. Он может содержать транспортные средства и сено:
  • farm/api/barns/bigredbarn/animals/
  • farm/api/barns/bigredbarn/vehicles/
  • farm/api/barns/bigredbarn/haybales/
При таком подходе дилемма, с которой я столкнулся, не возникает.

Семантика webdav никогда по-настоящему не была согласована с идиомами RESTful interface.

В теории GET должен извлекать представление состояния ресурса, а PROPFIND должен использоваться для извлечения членов коллекции.

Итак, вы должны сделать следующее:

  • GET / api / foo1/ - возвращает только состояние foo1
  • PROPFIND / api/foo1 / - возвращает члены foo1

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

Лично я использую шлюз webdav/json, где запросы выполняются с использованием RESTful идиом, но направляются в мою реализацию webdav

Например, я бы сделал так:

GET /api/foo1/_PROPFIND?fields=name,fooProp1,fooProp2

И это вернуло бы

[
{ name : "bar1", fooProp1: "..", fooProp2 : ".."},
{ name : "bar2", fooProp1: "..", fooProp2 : ".."}
]

Одним из преимуществ этого подхода является то, что клиент получает возможность контролировать возвращаемые свойства json. Это хорошо, потому что богатый API будет иметь много свойств, но в большинстве случаев клиенты не нужно их всех.

Маршруты и их операции в RESTfull API полностью разработаны разработчиками. Это разработчик, который решает, что вернуть, запрашивая определенный маршрут, скажем, GET /api/foo1.

И разработчик должен спроектировать каждый маршрут, включая /api/foo1/bar. Нет никакого конкретного правила о том, что должен делать конкретный маршрут. Если ваш API-это проект с открытым исходным кодом, сделайте чистую и понятную документацию для каждого маршрута.

Не тратьте время на размышления о старых школьных стратегиях.