我对REST比较陌生,但我一直在做关于RESTful应该如何做的功课。现在我正在尝试为我的模型创建一个实现JSON + HAL序列化程序的RESTful api,它与其他模型有关系。
python中的示例模型:
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 / catalog / food”:
{
"_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 / catalog
{
"_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": []
}
答案 0 :(得分:6)
关于第一个问题:我不会将URI存储在数据库中。您可以在运行时轻松地在控制器内计算它们,并且控制器负责关注URI。通过这种方式,您可以保持模型和API的分离,如果您决定在将来更改API结构,则无需使用新URI更新整个数据库。
关于多个标识符,我不确定问题是什么,但在我看来,它与DB无关,它是路由器和控制器应该关心如何处理任何URI。
关于第二个问题:首先,作为旁注:我会将 categories 这个词作为URI的一部分。例如,我有http://domain.com/api/categories/catalog/food
。这样,您就可以使API更具描述性并且更具“黑客性”,这意味着用户应该能够移除/catalog/food
部分并期望收到包含所有可用类别的集合。
现在,关于GET
应返回以允许发现的内容:我认为已经从您的URI结构中明确了这一点。当用户点击GET /categories
时,他希望得到一个包含类别的列表(每个类别的名称和URI,以保持轻量级),当他跟随其中一个URI(如GET /categories/catalog
)时,他应该收到资源catalog
这是一个类别。同样,当他想要GET /products/burger
时,他应该会收到一个包含模型中所有属性的产品资源。您可能需要查看this example有关回复结构的信息。
关于第3个问题:同样,the same example可以帮助您构建结构。我认为您的第二种回应方式更接近于此,但我还会添加name
字段,而不仅仅是href
。
关于第4个问题:当GET
请求期望收集资源(例如GET /categories
)时,我建议只为每个资源提供必要的资源,即名称和每个URI的URI,只有当用户遵循所需的URI时,他才能收到其他信息。
在您的示例中,catalog
是一个资源,因此GET /categories/catalog
我当然会包含资源(目录)的name
及其自身链接,以及{{1}与它相关的{}},parent
和sub-categories
,我只会提供每个的名称和URI,以保持清晰。 但是:这是关于设计API的一般想法。在您的实际问题中,您应根据您的具体业务问题做出决定。我的意思是,如果您的API是关于带有类别和菜肴的餐馆菜单,您可能想要包括价格或小描述,即使不是响应实际产品而是回复产品系列,因为可能对您的用户而言,一个重要的信息。因此,一般情况下,在回复资源列表时提供所有必要的信息(您只知道这些问题是什么),并在回复特定资源时提供资源的所有详细信息。
答案 1 :(得分:1)