Может ли REST API возвращать несколько ресурсов как один составной ресурс?

10

Я нахожусь в процессе создания REST API и в настоящее время сталкиваюсь со следующей проблемой:

  • Fooэто первый ресурс. Операции CRUD могут применяться через /foo/URI.
  • Barэто второй ресурс. Операции CRUD могут применяться через /bar/URI.
  • Каждый Fooсвязан с нулем или единицей Bar. Причина, по которой я не рассматриваю себя Barкак подресурс, Fooзаключается в том, что один и тот же Barэкземпляр может использоваться несколькими объектами Foo. Поэтому я решил, что лучше получить к нему доступ через независимый URI, а не /foo/[id]/bar.

Моя проблема в том, что в значительном количестве случаев клиенты, которые запрашивают Fooэкземпляр, также заинтересованы в связанном Barэкземпляре. В настоящее время это означает, что они должны выполнить два запроса вместо одного. Я хочу представить способ, который позволяет получать оба объекта одним запросом, но я не знаю, как смоделировать API для этого. Что я придумал до сих пор:

  • Я мог бы ввести параметр запроса , похожий на этот: /foo/[id]?include_bar=true. Проблема этого подхода заключается в том, что представление ресурса (например, структура JSON) ответа должно выглядеть иначе (например, контейнер, например, { foo: ..., bar: ... }вместо сериализации Foo), что делает Fooконечную точку ресурса «неоднородной». Я не думаю, что это хорошо. При запросе /fooклиенты всегда должны получать одинаковое представление ресурса (структуру) независимо от параметров запроса.
  • Другая идея состоит в том, чтобы ввести новую конечную точку только для чтения, например /fooandbar/[foo-id]. В этом случае нет проблем с возвращением представления типа { foo: ..., bar: ... }, потому что тогда это просто «официальное» представление fooandbarресурса. Тем не менее, я не знаю, является ли такая вспомогательная конечная точка действительно RESTful (вот почему я написал «can» в заголовке вопроса. Конечно, это технически возможно, но я не знаю, хорошая ли это идея).

Что вы думаете? Есть ли другие возможности?

CERAN
источник
Какой термин для отношений между Фу и Баром? Не могли бы вы сказать, что Бар является родителем Foo?
Натан Меррилл
А Barне может существовать без связи с Foo. Однако, как я уже писал выше, возможно, что множественные Foos имеют одинаковые значения Bar. Должно быть возможно создать Fooбез Barсвязанного, поэтому я не думаю, что Barследует рассматривать как родительский.
Ceran
1
Я думаю, что вы испытываете некоторые из проблем, которые у меня возникли, путем непосредственного перевода отношений модели домена в URI и приравнивания ресурсов к объектам домена . Это может заинтересовать REST API, которые должны управляться гипертекстом . Особое внимание к 4-му пункту
Laiv

Ответы:

6

Уровень 3 REST API будет возвращать вам , Fooа также ссылку , указывающую связанными с этим Bar.

GET /foo/123
<foo id="123">
  ..foo stuff..
  <link rel="bar" uri="/bar/456"/>
</foo>

Затем вы можете добавить функцию «детализации» в свой API, которая позволяет перемещаться по ссылкам;

GET /foo/123?drilldown=bar
<foo id="123">
  ..foo stuff..
  <link rel="bar" uri="/bar/456">
    <bar id="456">
      ..bar stuff...
    </bar>
  </link>
</foo>

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

Это довольно распространенная вещь в REST уровня 3, так как она значительно снижает общение между клиентом и сервером по медленному http. Компания, в которой я работаю, производит API REST уровня 3 именно с этой функцией.

Обновление: для чего оно стоит, вот как это может выглядеть в JSON. Вот как наш API будет структурировать это. Обратите внимание, что вы можете вкладывать детализацию в ссылки и т. Д.

GET /foo/123?drilldown=bar

{
  "self": {
    "type": "thing.foo",
    "uri": "/foo/123=?drilldown=bar",
    "href": "http://localhost/api/foo/123?drilldown=bar"
  },
  "links": [
    {
      "rel": "bar",
      "rev": "foo",
      "type": "thing.bar",
      "uri": "/bar/456",
      "href": "http://localhost/api/bar/456"
    }
  ],
  "_bar": [
    {
      "self": {
        "type": "thing.bar",
        "uri": "/bar/456",
        "href": "http://localhost/api/bar/456"
      },
      "links": [
        {
          ..other link..
        },
        {
          ..other link..
        }
      ]
    }
  ]
}
Qwerky
источник
Интересно, что я уже использую гипермедиа ссылки / элементы управления, чтобы удалить тесную связь с URI, но я не думал об идее «детализации», которая кажется очень многообещающей. Как может выглядеть представление JSON как? На данный момент, каждый из JSON представление моих ресурсов содержит linksмассив, каждый элемент представляет собой объект ссылки с relи uriполе (аналогично вашим пример XML). Должен ли я просто добавить третье поле для каждого объекта ссылки (например data)? Есть ли какой-нибудь стандарт?
Ceran
Развертка не совсем функция отдыха, поэтому нет стандартов (по крайней мере, я знаю).
Qwerky
Существует несколько предлагаемых стандартов, таких как stateless.co/hal_specification.html, которые я использую в наших приложениях. Это очень близко к вашему примеру.
Пит Киркхэм
4

Если 95% всех запросов хотят Foo, а также Bar, то просто вернуть его внутрь от Fooобъекта , когда вы запрашиваете Foo. Просто добавьте свойство bar(или другой термин для отношения) и поместите Barобъект туда. Если отношения не существуют, используйте нуль.

Я думаю, что вы думаете об этом :)

Натан Меррилл
источник
Я не должен был придумать это число (95%), это была ошибка, извините. Я хотел сказать, что большая часть запросов заинтересована в обоих ресурсах одновременно. Но все еще существует релевантное количество запросов, которые только интересуют Foo, и поскольку каждый из них Barимеет достаточно большой объем памяти (примерно в 3–4 раза больше Foo), я не хочу возвращать, Barесли клиент не запрашивает его явно.
Ceran
Насколько большой мы говорим? Я сомневаюсь , что он собирается сделать что большая разница во времени передачи, и я предпочитаю чистый API над скоростью
Nathan Меррилл