Каков наилучший шаблон для добавления существующего элемента в коллекцию в REST API?


23

Я разрабатываю прагматичный REST API и немного застрял в том, как лучше всего добавить существующие сущности в коллекцию. Моя модель домена включает в себя проект, который имеет коллекцию сайтов. Это строгое отношение «многие ко многим», и мне не нужно создавать сущность, которая явно моделирует отношение (например, ProjectSite).

Мой API позволит потребителям добавлять существующий сайт в проект. Я зациклен на том, что мне нужны только данные ProjectId и SiteId. Моя первоначальная идея была:

1. POST myapi/projects/{projectId}/sites/{siteId}

Но я тоже думал о

2. POST myapi/projects/{projectId}/sites

с объектом Site, отправленным как контент JSON.

Вариант 1 прост и работает, но не совсем правильно, и у меня есть другие отношения, которые не могут следовать этому шаблону, поэтому он добавляет непоследовательность в мой API.

Вариант 2 чувствует себя лучше, но приводит к двум проблемам:

  • Должен ли я создать сайт или выдать исключение, если новый сайт опубликован (SiteId = 0)?
  • Поскольку мне нужны только ProjectId и SiteId для создания отношения, сайт может быть опубликован с неверными или отсутствующими данными для других свойств.

Третий вариант - предоставить простую конечную точку исключительно для создания и удаления отношений. Эта конечная точка будет ожидать полезной нагрузки JSON, содержащей только ProjectId и SiteId.

Что вы думаете?



@RoryHunter В этой ссылке есть интересное обсуждение, но ничего, что устраняет мою неуверенность. Мне особенно нравится, что принятый ответ гласит: «Вы правильно поняли». и 2-е место (хотя и с большим отрывом) ответ «Проще говоря, вы делаете это полностью назад».
Джейми Иде

Ваш первый вариант подходит, хотя я бы использовал PUT вместо POST, так как клиент контролирует идентификацию, добавляемую в коллекцию. Ваша первая проблема с вариантом 2 полностью зависит от вас, если вы не хотите новых сайтов, не бросайте исключение, а возвращайте один из кодов 4xx. Ваше второе беспокойство не здесь и не там. Вы не должны публиковать весь сайт в любом случае, если вы не разрешаете добавления. Добавление существующего сайта должно иметь идентификатор только при изменении сайта, но только в коллекции «ProjectSite» (даже если вы не создаете для него отдельный ресурс).
Марьян Венема

Ответы:


14

POST - это глагол «добавление», а также глагол «обработка». PUT - это глагол «создать / обновить» (для известных идентификаторов), и он выглядит почти как правильный выбор, потому что известен полный целевой URI. projectIdи siteIdуже существует, так что вам не нужно "POST в коллекцию", чтобы создать новый идентификатор.

Проблема с PUT заключается в том, что тело должно быть представлением ресурса, который вы PUTting. Но цель здесь - добавить ресурс коллекции проекта / сайтов, а не обновлять ресурс сайта.

Что, если кто-то помещает полное JSON-представление существующего сайта? Стоит ли обновлять коллекцию и обновлять объект? Вы могли бы поддержать это, но похоже, что это не цель. Как вы сказали,

единственные данные, которые мне действительно нужны, это ProjectId и SiteId

Скорее, я бы попробовал поместить POST в siteIdколлекцию и полагаться на «добавление» и «процесс» природы POST:

POST myapi / projects / {projectId} / сайты

{'мне бы': '...' }

Поскольку вы изменяете ресурс семейства сайтов, а не ресурс сайта , вам нужен именно этот URI. POST может знать «добавить / обработать» и добавить элемент с этим идентификатором в семейство сайтов проекта.

Это по-прежнему оставляет открытой возможность для создания совершенно новых сайтов для проекта, уточняя JSON и опуская идентификатор. "Нет идентификатора" == "создать с нуля". Но если URI коллекции получает идентификатор и ничего больше, становится ясно, что должно произойти.

Интересный вопрос. :)


Я верю в то, что POST предназначен для создания, а PUT для обновления, но вы пришли к выводу, что я оказался там вчера. Приятно то, что благодаря атрибутной маршрутизации в Web API у меня есть код в контроллере ProjectSites, поэтому код хорошо организован.
Джейми Ид

Я думаю, что определяющая причина, которую вы должны использовать POSTвместо PUTили PATCHздесь, заключается в том, что у вас нет всей Siteсущности, которую нужно поместить в sitesресурс. У вас есть только идентификатор, который требует обработки, чтобы добавить его в коллекцию.
раздавить

4

Мы используем Patchметод для таких вещей, как это. Что вы хотите сделать, это изменить существующий проект, чтобы добавить сайт к нему.

Так что-то вроде этого будет работать

PATCH myapi/projects/{id} 

с сущностью Site (s) как JSON / JSONArray в теле запроса.

Таким образом, вы можете использовать один и тот же URL-адрес для изменения различных частей проекта, если вам нужно - ваш код в реализации должен быть достаточно интеллектуальным, чтобы справиться с этой частичной модификацией ресурса.


Интересный подход. У меня есть «богатая» (то есть сильно зависимая) более старая модель предметной области, и у Project особенно есть множество коллекций, висящих на ней. Определение типа сущности в запросе было бы проблемой и не соответствовало моей прагматической цели.
Джейми Иде

Почему вызов? Если у вас есть эти ограничения, вы всегда можете использовать JSON, который явно указывает, что он отправляет ... как {"sites": [], "other-stuff": {}}, вы можете затем ветвить свой код, чтобы очень легко обрабатывать все эти "подзоны". Это действительно зависит от вашей конкретной проблемы, но я все равно рекомендую использовать PATCH, так как он разработан специально для такого рода вещей.
хуан

Недостатки, которые я вижу: 1) API не сообщает явно, какие коллекции допускают изменения; 2) не может использовать привязку параметров Web API; 3) большой переключатель или заявление.
Джейми Иде

Я никогда не видел, чтобы этот метод исправления использовался где-либо еще
NimChimpsky

Разве PATCHтакже не следует ожидать, что здесь будет передана полная сущность, а не идентификатор, указывающий на какую-то сущность?
раздавить
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.