Разработка RESTful API с использованием HAL - сериализация отношений модели
Я относительно новичок в REST, но я делаю домашнюю работу о том, каким должен быть RESTful. Сейчас я пытаюсь создать RESTful API, реализующий сериализатор JSON+HAL для моих моделей, которые связаны с другими моделями.
Пример модели в питоне:
class Category(Model):
name = CharField()
parent = ManyToOneField(Category)
categories = OneToManyField(Category)
products = ManyToManyField(Product)
class Product(Model):
name = CharField()
price = DecimalField()
related = ManyToManyField(Product)
categories = ManyToManyField(Category)
Предположим, у нас есть категория "каталог" с подкатегорией "еда" с продуктами "гамбургер" и "хот-дог", которые связаны между собой.
Первый вопрос. Категории и продукты должны быть ресурсами, поэтому им нужен URI, должен ли я реализовать поле uri в моей модели и сохранить его в БД или каким-то образом рассчитать его во время выполнения, как насчет нескольких идентификаторов (URI)?
Второй вопрос Обнаруживаемость, в формате Hal то, что должны возвращать "GET /" и различные узлы, чтобы сделать API легко обнаруживаемым.
{
"_links":{
"self":{
"href":"/"
},
"categories":[
{
"href":"/catalog"
}
]
}
}
Третий вопрос Добавить как свойства, встроить или ссылку. Пример "GET / каталог / еда":
{
"_links":{
"self":{
"href":"/catalog/food"
}
},
"name":"food",
"parent":"/catalog",
"categories":[],
"products":[
"/products/burger",
"/products/hot-dog"
]
}
{
"_links":{
"self":{
"href":"/catalog/food"
},
"parent":{
"href":"/catalog"
},
"categories":[
],
"products":[
{
"href":"/products/burger"
},
{
"href":"/products/hot-dog"
}
]
},
"name":"food"
}
{
"_links":{
"self":{
"href":"/catalog/food"
}
},
"name":"food",
"_embedded":{
"parent":{
"_links":{
"self":{
"href":"/catalog"
}
},
"name":"catalog",
...
},
"categories":[
],
"products":[
{
"_links":{
"self":{
"href":"/products/burger"
}
},
"name":"burger",
...
},
{
"_links":{
"self":{
"href":"/products/hot-dog"
}
},
"name":"hot-dog",
...
}
]
}
}
Четвертый вопрос. Как глубоко я должен идти при возвращении структур. Пример "GET / каталог
{
"_links":{
"self":{
"href":"/catalog"
}
},
"name":"catalog",
"parent":null,
"categories":[
{
"name":"food",
"parent":{...},
"categories":[],
"products":[
{
"name":"burger",
"price":"",
"categories":[...],
"related":[...]
},
{
"name":"hot-dog",
"price":"",
"categories":[...],
"related":[...]
}
]
}
],
"products": []
}
2 ответа
О 1-м вопросе: я не буду хранить URI в БД. Вы можете легко вычислить их внутри вашего контроллера во время выполнения, и ответственность за URIs лежит на контроллере. Таким образом вы не будете связывать свою модель и API, и если вы решите изменить структуру API в будущем, вам не нужно будет обновлять всю базу данных новыми URI.
Насчет нескольких идентификаторов, я не уверен, что вопрос, но опять же, на мой взгляд, это не имеет ничего общего с БД, это маршрутизация и контроллеры, которые должны заботиться о том, как обращаться с любыми URI.
О 2-м вопросе: Прежде всего, как примечание: я бы оставил слова категории как часть URI. Например, у меня есть http://domain.com/api/categories/catalog/food
, Таким образом, вы делаете свой API более информативным и более "взломанным", что означает, что пользователь должен иметь возможность удалить /catalog/food
расстаться и ожидать получить коллекцию со всеми доступными категориями.
Теперь о том, что GET
должен вернуться, чтобы разрешить обнаружение: я думаю, это уже проясняется из вашей структуры URI. Когда пользователь нажимает GET /categories
он ожидает получить список с категориями (имя и URI для каждой, чтобы сохранить его легким), и когда он следует за одним из URI как GET /categories/catalog
он должен получить ресурс catalog
которая является категорией. Точно так же, когда он хочет GET /products/burger
Он должен получить ресурс продукта со всеми атрибутами, которые есть в вашей модели. Вы можете проверить этот пример на предмет структуры ваших ответов.
О третьем вопросе: опять тот же пример может помочь вам сформировать структуру. Я думаю, что ваш второй способ ответа ближе к этому, но я бы также добавил name
поле, а не только href
,
О четвертом вопросе: когда GET
запрос ожидает коллекцию ресурсов (например, GET /categories
) Я бы предложил предоставить только необходимый для каждого ресурса, то есть имя и URI для каждого, и только когда пользователь следует желаемому URI, он может получить остальную информацию.
В вашем примере catalog
это ресурс, так далее GET /categories/catalog
Я бы, конечно, включил name
ресурса (каталога) и его собственной ссылки, а также для parent
, sub-categories
а также products
которые связаны с этим, я бы просто предоставил имя и URI для каждого, чтобы было легче. Но: это была общая мысль о разработке API. В вашей реальной проблеме вы должны решить в зависимости от вашей конкретной бизнес-проблемы. Я имею в виду, если ваш API касается меню ресторана с категориями и блюдами, вы можете указать цену или небольшое описание, даже если отвечаете не за фактический продукт, а за набор продуктов, потому что, вероятно, для ваших пользователей это важная информация. Поэтому, как правило, предоставьте всю необходимую информацию (вы только знаете, что это за проблема), когда отвечаете о списке ресурсов, и предоставьте все детали ресурса, когда отвечаете о конкретном ресурсе.
- Я хотел бы что-то хранить в БД и вычислять URI во время выполнения. Таким образом, если вы перемещаете коробки, это не статично.
- Создайте страницу "закладки". Страница, которую мы создали, была просто списком ссылок с их ссылками. Я считаю, что HAL определяет это конкретно. Страница закладок была единственной страницей, о которой нужно было знать другим страницам.
- Не уверен насчет этого
- Как глубоко вы идете, зависит от вас. Сейчас у меня много споров о том, что зерно лучше, чем зерно. Я собираюсь сделать мелкое зерно с небольшим ресурсом, чтобы упростить API, но затем использую концепцию расширения возможностей. Это сочетание идеи составных ресурсов, определенной на стр. 35 книги REST Subbu, и концепции расширения, используемой Netflix. http://developer.netflix.com/docs/REST_API_Conventions