У меня есть иерархия объектов, которую мне нужно открыть через 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
В этом случае возвращаемые животные будут содержать только атрибуты, общие для всех животных, отбрасывая специфические для собаки и специфичные для кошки атрибуты. Это позволяет извлекать всех животных, хотя и с меньшим количеством деталей. Каждый возвращенный объект может содержать ссылку на подробную конкретную версию.
Любые комментарии или предложения?
Я бы предложил:
Настройка нескольких 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
- получает всех собак, только вернет RexGET /animals?type=cat
- получает всех кошек, только РукавицыЗатем при создании или модификации животных на вызывающего абонента было бы указано, к какому типу относится животное:
Тип носителя: application/vnd.vet-services.animal+json
{
"type":"dog",
"name":"Fido"
}
Вышеуказанная полезная нагрузка может быть отправлена с запросом POST
или PUT
.
Вышеприведенная схема дает вам основные аналогичные характеристики, такие как наследование OO через REST, и возможность добавлять дополнительные специализации (то есть больше типов животных) без серьезной операции или любых изменений в вашей схеме URI.
Я пошел бы за/животными, возвращающими список как собак, так и рыб, и что еще:
<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-адреса (которые по определению являются единственными полномочиями).
Но теперь связь между собаками и кошками теряется.
В самом деле, но имейте в виду, что URI просто не отражает отношения между объектами.
GET /animals - gets all dogs and cats
GET /animals/dogs - gets all dogs
GET /animals/cats - gets all cats