Как смоделировать RESTful API с наследованием?

61

У меня есть иерархия объектов, которую мне нужно открыть через RESTful API, и я не уверен, как мои URL-адреса должны быть структурированы и что они должны возвращать. Я не мог найти лучших практик.

Скажем, у меня есть Собаки и Кошки, унаследованные от животных. Мне нужны операции CRUD на собаках и кошках; Я также хочу иметь возможность делать операции с животными в целом.

Моя первая идея заключалась в том, чтобы сделать что-то вроде этого:

GET /animals        # get all animals
POST /animals       # create a dog or cat
GET /animals/123    # get animal 123

Дело в том, что коллекция /animals теперь "непоследовательна", так как она может возвращаться и принимать объекты, которые не имеют точно такой же структуры (собаки и кошки). Считается ли это "RESTful" иметь коллекцию, возвращающую объекты с разными атрибутами?

Другим решением было бы создать URL-адрес для каждого конкретного типа, например:

GET /dogs       # get all dogs
POST /dogs      # create a dog
GET /dogs/123   # get dog 123

GET /cats       # get all cats
POST /cats      # create a cat
GET /cats/123   # get cat 123

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

Другое предложение состояло в том, чтобы увеличить второе решение, добавив следующее:

GET /animals    # get common attributes of all animals

В этом случае возвращаемые животные будут содержать только атрибуты, общие для всех животных, отбрасывая специфические для собаки и специфичные для кошки атрибуты. Это позволяет извлекать всех животных, хотя и с меньшим количеством деталей. Каждый возвращенный объект может содержать ссылку на подробную конкретную версию.

Любые комментарии или предложения?

Теги:
polymorphism
rest
inheritance

3 ответа

31
Лучший ответ

Я бы предложил:

  • Использование только одного URI для каждого ресурса
  • Дифференциация между животными только на уровне атрибута

Настройка нескольких URI на один и тот же ресурс никогда не является хорошей идеей, поскольку это может вызвать путаницу и неожиданные побочные эффекты. Учитывая, что ваш единственный URI должен основываться на общей схеме, такой как /animals.

Следующая задача борьбы со всей коллекцией собак и кошек на "базовом" уровне уже решена в силу подхода /animals URI.

Окончательная задача борьбы со специализированными типами, такими как собаки и кошки, может быть легко решена с использованием комбинации параметров запроса и атрибутов идентификации в вашем типе медиа. Например:

GET /animals (Accept : application/vnd.vet-services.animals+json)

{
   "animals":[
      {
         "link":"/animals/3424",
         "type":"dog",
         "name":"Rex"
      },
      {
         "link":"/animals/7829",
         "type":"cat",
         "name":"Mittens"
      }
   ]
}
  • GET /animals - получает всех собак и кошек, возвратит как Рекса, так и Рукавицы
  • GET /animals?type=dog - получает всех собак, только вернет Rex
  • GET /animals?type=cat - получает всех кошек, только Рукавицы

Затем при создании или модификации животных на вызывающего абонента было бы указано, к какому типу относится животное:

Тип носителя: application/vnd.vet-services.animal+json

{
   "type":"dog",
   "name":"Fido"
}

Вышеуказанная полезная нагрузка может быть отправлена ​​с запросом POST или PUT.

Вышеприведенная схема дает вам основные аналогичные характеристики, такие как наследование OO через REST, и возможность добавлять дополнительные специализации (то есть больше типов животных) без серьезной операции или любых изменений в вашей схеме URI.

  • 0
    Это похоже на «приведение» через REST API. Это также напоминает мне о проблемах / решениях в структуре памяти подкласса C ++. Например, где и как одновременно представлять базу и подкласс с одним адресом в памяти.
  • 8
    Я предлагаю: GET /animals - gets all dogs and cats GET /animals/dogs - gets all dogs GET /animals/cats - gets all cats
Показать ещё 4 комментария
4

Я пошел бы за/животными, возвращающими список как собак, так и рыб, и что еще:

<animals>
  <animal type="dog">
    <name>Fido</name>
    <fur-color>White</fur-color>
  </animal>
  <animal type="fish">
    <name>Wanda</name>
    <water-type>Salt</water-type>
  </animal>
</animals>

Это должно быть легко реализовать аналогичный пример JSON.

Клиенты всегда могут полагаться на находящийся там элемент "name" (общий атрибут). Но в зависимости от атрибута "type" в представлении животных будут другие элементы.

В возвращении такого списка нет ничего из RESTful или unRESTful - REST не задает какой-либо конкретный формат представления данных. Все, что он говорит, состоит в том, что данные должны иметь некоторое представление, а формат для этого представления идентифицируется типом носителя (который в HTTP является заголовком Content-Type).

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

Если вы делаете/животные? type = собака или/собака не имеет отношения к REST, который не задает никаких форматов URL-адресов, которые остаются в качестве детали реализации вне рамок REST. REST только заявляет, что ресурсы должны иметь идентификаторы - неважно, в каком формате.

Вы должны добавить ссылку на гиперссылку, чтобы приблизиться к API RESTful. Например, добавив ссылки на детали животных:

<animals>
  <animal type="dog" href="/animals/123">
    <name>Fido</name>
    <fur-color>White</fur-color>
  </animal>
  <animal type="fish" href="/animals/321">
    <name>Wanda</name>
    <water-type>Salt</water-type>
  </animal>
</animals>

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

1

Но теперь связь между собаками и кошками теряется.

В самом деле, но имейте в виду, что URI просто не отражает отношения между объектами.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню