我有一个对象的层次结构,我需要通过一个RESTful API来揭露和我不知道我的网址应如何构建,他们应该返回什么。 我找不到任何的最佳做法。
比方说,我有狗和猫从动物继承。 我需要狗和猫的CRUD操作; 我也希望能够做业务上的动物一般。
我的第一个想法是做这样的事情:
GET /animals # get all animals
POST /animals # create a dog or cat
GET /animals/123 # get animal 123
问题是,在/动物收集现在是“矛盾”,因为它可以返回,并采取不具有完全相同的结构(狗和猫)的对象。 难道认为“基于REST的”有一个集合返回拥有不同属性的对象?
另一种解决方案是创建一个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
但是,现在的狗和猫之间的关系都将丢失。 如果希望检索所有动物中,狗和猫都资源必须查询。 网址的数量也将随着每一个新的动物亚型增加。
另一项建议是通过添加本,以增加第二溶液:
GET /animals # get common attributes of all animals
在这种情况下,动物返回将只包含通用于所有的动物属性,下探狗特异性和猫特定的属性。 这使得检索所有的动物,虽然使用较少的信息。 每个返回的对象可能包含链接到详细,具体的版本。
任何意见或建议?
Answer 1:
我会建议:
- 使用每个资源只有一个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
-得到所有的狗,只会返回雷克斯 -
GET /animals?type=cat
-得到所有的猫,那只手套
然后创建或修改的动物时,将是对主叫方有义务指定有关动物的类型:
媒体类型: application/vnd.vet-services.animal+json
{
"type":"dog",
"name":"Fido"
}
上述有效载荷可以用一个被发送POST
或PUT
请求。
上述方案让你的基本相似的特性OO继承通过REST,并与在没有重大手术或您的URI方案中的任何变化进一步增加特(即更多的动物种类)的能力。
Answer 2:
我会去/动物返回狗和鱼还有什么永远的列表:
<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的例子。
客户可以始终依靠“名称”元素在那里(属性相同)上。 但根据“type”属性还会有其他的元件作为动物表示的一部分。
没有什么返还这类列表固有的RESTful或unRESTful - REST没有规定任何具体的格式来表示数据。 所有它说的是,数据必须具有一定的代表性和该表示是由媒体类型(在HTTP是Content-Type头)识别的格式。
想想你的使用情况 - 你需要显示混合动物名单? 好了,然后返回混合动物数据的列表。 你只需要狗的名单? 好了,做出这样的列表。
?无论你做/动物类型=狗或/狗就无关紧要了,对于REST其中没有规定任何URL格式 - 这是留给REST范围以外的实现细节。 REST只是说资源应该有标识 - 别提什么格式。
你应该添加一些超链接的媒体来接近一个RESTful API。 例如通过向动物细节的参考:
<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(其定义是唯一的权威)。
Answer 3:
这个问题可以与支持,在OpenAPI的最新版本引入了最近的增强可以更好地回答。
它已经可以使用关键字,如oneOf,allOf,anyOf到模式相结合,让因为JSON模式V1.0验证消息负载。
https://spacetelescope.github.io/understanding-json-schema/reference/combining.html
然而,在OpenAPI的(前扬鞭),架构组成有所增强通过关键字鉴别 (V2.0 +)和oneOf(V3.0 +),以真正支持多态。
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaComposition
你可以继承使用oneOf的组合(选择亚型之一)和allOf(对于合并的类型及其亚型之一)进行建模。 下面是POST方法的样品定义。
paths:
/animals:
post:
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Fish'
discriminator:
propertyName: animal_type
responses:
'201':
description: Created
components:
schemas:
Animal:
type: object
required:
- animal_type
- name
properties:
animal_type:
type: string
name:
type: string
discriminator:
property_name: animal_type
Dog:
allOf:
- $ref: "#/components/schemas/Animal"
- type: object
properties:
playsFetch:
type: string
Cat:
allOf:
- $ref: "#/components/schemas/Animal"
- type: object
properties:
likesToPurr:
type: string
Fish:
allOf:
- $ref: "#/components/schemas/Animal"
- type: object
properties:
water-type:
type: string
Answer 4:
但是,现在的狗和猫之间的关系都将丢失。
的确,但请记住,URI根本不会反映对象之间的关系。
Answer 5:
我知道这是一个老问题,但我感兴趣的是上一个RESTful继承建模研究进一步的问题
我总是说,狗是一种动物和母鸡过,但母鸡让人蛋,而狗是哺乳动物,所以它不能。 一个API像
GET动物/:animalID /鸡蛋
是不是一致的,因为表明,动物的所有亚型可以有蛋(如Liskov替换的结果)。 将会有一个备用的,如果所有的哺乳动物都具有“0”回应这一请求,但如果我还能够POST方法? 我应该害怕明天会有狗蛋在我的煎饼?
处理这些情况的唯一方法是提供一个“超级资源”它汇集所有的不可能性“派生资源”,然后为每个派生资源需要它的专业化之间共享的所有子资源,就像当我们向下转换的对象进入接力
GET /动物/:animalID /儿子GET /母鸡/:animalID /鸡蛋POST /母鸡/:animalID /鸡蛋
缺点,在这里,有人可能会通过狗ID的引用母鸡集合的实例,但狗是不是母鸡,所以它不会是不正确的,如果回答是404或400个理由消息
我错了吗?
文章来源: How to model a RESTful API with inheritance?