Подтвердить что ты не робот

Каковы лучшие практики для вложенных ресурсов REST?

Насколько я могу судить, каждый отдельный ресурс должен иметь только один канонический путь. Итак, в следующем примере, какими были бы хорошие шаблоны URL?

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

Отдел не может существовать без ассоциированной компании.

Сотрудник не может существовать без соответствующего отдела.

Теперь я бы нашел естественное представление шаблонов ресурсов.

  • /companies Коллекция компаний - принимает предложение для новой компании. Получить для всей коллекции.
  • /companies/{companyId} Индивидуальная компания. Принимает GET, PUT и DELETE
  • /companies/{companyId}/departments Принимает POST для нового элемента. (Создает отдел внутри компании.)
  • /companies/{companyId}/departments/{departmentId}/
  • /companies/{companyId}/departments/{departmentId}/employees
  • /companies/{companyId}/departments/{departmentId}/employees/{empId}

Учитывая ограничения, в каждом из разделов, я чувствую, что это имеет смысл, если немного глубоко вложен.

Однако моя трудность возникает, если я хочу перечислить (GET) всех сотрудников во всех компаниях.

Шаблон ресурсов для этого наиболее точно соответствует /employees (коллекция всех сотрудников)

Означает ли это, что я должен иметь /employees/{empId} также потому, что если это так, есть два URI для получения одного и того же ресурса?

Или, может быть, вся схема должна быть сглажена, но это будет означать, что сотрудники - это вложенный объект верхнего уровня.

На базовом уровне /employees/?company={companyId}&department={deptId} возвращает тот же вид сотрудников, что и самый глубоко вложенный шаблон.

Какая наилучшая практика для шаблонов URL-адресов, в которых ресурсы принадлежат другим ресурсам, но должна быть доступна отдельно?


ОБНОВЛЕНИЕ: см. Мой ответ ниже, чтобы узнать, что я сделал.

4b9b3361

Ответ 1

То, что вы сделали, правильно. В общем, может быть много URI для одного и того же ресурса - нет правил, которые говорят, что вы не должны этого делать.

И вообще, вам может потребоваться доступ к элементам напрямую или как подмножество чего-то другого - так что ваша структура имеет смысл для меня.

Просто потому, что сотрудники доступны по отделу:

company/{companyid}/department/{departmentid}/employees

Не означает, что они не могут быть доступны и в компании:

company/{companyid}/employees

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

Но я надеюсь, что все обработчики URL-адресов используют один и тот же код поддержки для удовлетворения запросов, чтобы вы не дублировали код.

Ответ 2

Я пробовал обе стратегии разработки - вложенные и не вложенные конечные точки. Я обнаружил, что:

  • Если вложенный ресурс имеет первичный ключ и у вас нет родительского первичного ключа, вложенная структура требует его получения, даже если система на самом деле не требует его.

  • Вложенные конечные точки обычно требуют избыточных конечных точек. Другими словами, вы чаще всего будете нуждаться в конечной точке дополнительного/служащего, чтобы вы могли получить список сотрудников по отделам. Если у вас есть/сотрудники, что именно делает/компании/отделы/сотрудники покупают вас?

  • Вложенные конечные точки не так хорошо развиваются. Например. вам может не потребоваться поиск сотрудников, но вы можете позже, и если у вас есть вложенная структура, у вас нет выбора, кроме как добавить другую конечную точку. С не-вложенным дизайном вы просто добавляете больше параметров, что проще.

  • иногда ресурс может иметь несколько типов родителей. Результат в нескольких конечных точках возвращает все те же ресурсы.

  • избыточные конечные точки затрудняют запись документов, а также затрудняют изучение api.

Короче говоря, не-вложенный дизайн, по-видимому, позволяет использовать более гибкую и более простую схему конечных точек.

Ответ 3

Я переместил то, что я сделал от вопроса, к ответу, где больше людей, скорее всего, это увидит.

То, что я сделал, это иметь конечные точки создания на вложенной конечной точке. Каноническая конечная точка для изменения или запроса элемента не находится на вложенном ресурсе.

Таким образом, в этом примере (просто перечисление конечных точек, которые меняют ресурс)

  • POST /companies/ создает новую компанию, возвращает ссылку на созданную компанию.
  • POST /companies/{companyId}/departments при создании отдела создает новый отдел возвращает ссылку на /departments/{departmentId}
  • PUT /departments/{departmentId} изменяет отдел
  • POST /departments/{deparmentId}/employees создают нового сотрудника, который возвращает ссылку на /employees/{employeeId}

Таким образом, для каждого из коллекций есть ресурсы уровня корня. Однако создание находится в собственном объекте.

Ответ 4

Я прочитал весь ответ выше, но похоже, что у меня нет общей стратегии. Я нашел хорошую статью о лучших практиках в API проектирования в документе Microsoft Documents. Я думаю, что вы должны обратиться.

В более сложных системах может быть заманчиво предоставить URI, которые дать возможность клиенту перемещаться по нескольким уровням отношений, такие как /customers/1/orders/99/products. Однако этот уровень сложность может быть трудно поддерживать и является негибкой, если отношения между ресурсами изменятся в будущем. Вместо этого попробуйте сохранить URI относительно простыми. Как только приложение имеет ссылку на ресурс, должна быть возможность использовать эту ссылку для поиска предметов связанные с этим ресурсом. Предыдущий запрос можно заменить на URI /customers/1/orders, чтобы найти все заказы для клиента 1, и затем /orders/99/products, чтобы найти продукты в этом порядке.

.

Tip

Избегайте использования URI ресурса более сложным, чем collection/item/collection.

Ответ 5

То, как выглядят ваши URL, не имеет ничего общего с REST. Все идет. На самом деле это "деталь реализации". Так же, как вы называете свои переменные. Все они должны быть уникальными и долговечными.

Не тратьте слишком много времени на это, просто сделайте выбор и придерживайтесь его/будьте последовательны. Например, если вы используете иерархию, вы делаете это для всех своих ресурсов. Если вы используете параметры запроса... и т.д., Так же, как соглашения о присвоении имен в вашем коде.

Почему так? Насколько я знаю, API "RESTful" должен быть доступен для просмотра (вы знаете... "Гипермедиа как движок состояния приложения"), поэтому клиент API не заботится о том, на что похожи ваши URL, пока они действительный (там нет SEO, нет человека, который должен читать эти "дружеские ссылки", кроме как для отладки...)

То, насколько приятный/понятный URL-адрес в REST API, интересует вас, как разработчика API, а не клиента API, как было бы имя переменной в вашем коде.

Самое главное, чтобы ваш клиент API знал, как интерпретировать ваш тип мультимедиа. Например, он знает, что:

  • у вашего типа мультимедиа есть свойство links, в котором перечислены доступные/связанные ссылки.
  • Каждая ссылка идентифицируется отношением (так же, как браузеры знают, что ссылка [rel= "stylesheet"] означает, что это таблица стилей, или rel= favico - это ссылка на favicon...)
  • и он знает, что означают эти отношения ("компании" означают список компаний, "поиск" означает шаблонный URL для поиска в списке ресурсов, "отделы" означают отделы текущего ресурса)

Ниже приведен пример обмена HTTP (тела в yaml, так как его легче написать):

Запрос

GET / HTTP/1.1
Host: api.acme.io
Accept: text/yaml, text/acme-mediatype+yaml

Ответ: список ссылок на основной ресурс (компании, люди, что угодно...)

HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:04:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml

# body: this is your API entrypoint (like a homepage)  
links:
  # could be some random path https://api.acme.local/modskmklmkdsml
  # the only thing the API client cares about is the key (or rel) "companies"
  companies: https://api.acme.local/companies
  people: https://api.acme.local/people

Запрос: ссылка на компании (используя предыдущий ответ body.links.companies)

GET /companies HTTP/1.1
Host: api.acme.local
Accept: text/yaml, text/acme-mediatype+yaml

Ответ: частичный список компаний (по элементам), ресурс содержит связанные ссылки, например ссылку, чтобы получить следующую пару компаний (body.links.next), другую (шаблонную) ссылку для поиска (тело)..links.search)

HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:06:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml

# body: representation of a list of companies
links:
  # link to the next page
  next: https://api.acme.local/companies?page=2
  # templated link for search
  search: https://api.acme.local/companies?query={query} 
# you could provide available actions related to this resource
actions:
  add:
    href: https://api.acme.local/companies
    method: POST
items:
  - name: company1
    links:
      self: https://api.acme.local/companies/8er13eo
      # and here is the link to departments
      # again the client only cares about the key department
      department: https://api.acme.local/companies/8er13eo/departments
  - name: company2
    links:
      self: https://api.acme.local/companies/9r13d4l
      # or could be in some other location ! 
      department: https://api2.acme.local/departments?company=8er13eo

Итак, как вы видите, если вы идете по ссылкам/отношениям, то, как вы структурируете часть пути ваших URL, не имеет никакого значения для вашего API-клиента. И если вы сообщаете клиенту структуру ваших URL-адресов в качестве документации, то вы не выполняете REST (или, по крайней мере, не уровень 3 согласно "модель зрелости Ричардсона")

Ответ 6

Я не согласен с таким путем

GET /companies/{companyId}/departments

Если вы хотите получить отделы, я думаю, что лучше использовать ресурс /department

GET /departments?companyId=123

Я полагаю, что у вас есть таблица companies и таблица departments, а затем классы для их сопоставления на используемом вами языке программирования. Я также предполагаю, что отделы могут быть привязаны к другим объектам, кроме компаний, поэтому ресурс /department просто, удобно иметь ресурсы, сопоставленные с таблицами, а также вам не нужно столько конечных точек, поскольку вы можете повторно использовать

GET /departments?companyId=123

для любого вида поиска, например

GET /departments?name=xxx
GET /departments?companyId=123&name=xxx
etc.

Если вы хотите создать отдел,

POST /departments

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

Ответ 7

Rails предлагает решение этой проблемы: мелкое вложение.

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