如何使用继承对RESTful API建模?
我有一个需要通过RESTful API公开的对象层次结构,我不确定我的URL应该如何构造,它们应该返回什么。我找不到任何最佳实践 比如说,我养的狗和猫都是从动物身上遗传下来的。我需要对狗和猫进行CRUD手术;我也希望能够在动物身上做手术 我的第一个想法是这样做:如何使用继承对RESTful API建模?,rest,inheritance,polymorphism,restapi,Rest,Inheritance,Polymorphism,Restapi,我有一个需要通过RESTful API公开的对象层次结构,我不确定我的URL应该如何构造,它们应该返回什么。我找不到任何最佳实践 比如说,我养的狗和猫都是从动物身上遗传下来的。我需要对狗和猫进行CRUD手术;我也希望能够在动物身上做手术 我的第一个想法是这样做: GET /animals # get all animals POST /animals # create a dog or cat GET /animals/123 # get animal 123
GET /animals # get all animals
POST /animals # create a dog or cat
GET /animals/123 # get animal 123
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
问题是/animals集合现在“不一致”,因为它可以返回并获取结构不完全相同的对象(狗和猫)。让集合返回具有不同属性的对象是否被视为“RESTful”
另一种解决方案是为每种具体类型创建URL,如下所示:
GET /animals # get all animals
POST /animals # create a dog or cat
GET /animals/123 # get animal 123
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
- 仅在属性级别区分动物
/animals
这样的通用方案
下一个挑战是在“基础”级别处理整个猫狗集合,这一挑战已经通过/animals
URI方法解决了
使用媒体类型中的查询参数和标识属性组合,可以轻松解决处理狗和猫等特殊类型的最后一个难题。例如:
GET/animates
(Accept:application/vnd.vet services.animates+json
)
-获取所有的狗和猫,将返回Rex和手套GET/animals
-获取所有狗,只返回RexGET/animals?type=dog
-获取所有猫,只会获取手套GET/animals?type=cat
application/vnd.vet services.animal+json
{
"animals":[
{
"link":"/animals/3424",
"type":"dog",
"name":"Rex"
},
{
"link":"/animals/7829",
"type":"cat",
"name":"Mittens"
}
]
}
{
"type":"dog",
"name":"Fido"
}
上述有效载荷可通过POST
或PUT
请求发送
上述方案通过REST为您提供了与OO继承相似的基本特征,并且能够添加进一步的专门化(即更多的动物类型),而无需进行大手术或对URI方案进行任何更改。我会选择/animals返回狗和鱼的列表以及其他任何内容:
<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”元素(一个公共属性)。但根据“类型”属性的不同,作为动物表示的一部分还有其他元素
返回这样一个列表并没有本质上的RESTful或untable,REST并没有规定任何表示数据的特定格式。它所说的只是数据必须具有某种表示形式,并且该表示形式的格式由媒体类型(在HTTP中是内容类型头)标识
想想你的用例——你需要展示一个混合动物的列表吗?那么,返回一个混合动物数据列表。你只需要狗的名单吗?好吧,列个清单
您是否使用/animals?type=dog或/dogs与REST无关,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(根据定义,它是URL的唯一权威)
但是现在猫狗之间的关系消失了
的确如此,但请记住,URI从来不会反映对象之间的关系。如果支持最新版本的OpenAPI中引入的最新增强功能,这个问题可以得到更好的回答 自JSON schema v1.0以来,可以使用关键字(如oneOf、allOf、anyOf)组合模式,并获得经过验证的消息有效负载 然而,在OpenAPI(以前的Swagger)中,模式组合通过关键字鉴别器(v2.0+)和其中一个(v3.0+)得到了增强,以真正支持多态性 您的继承可以使用of(用于选择一个子类型)和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
我知道这是一个老问题,但我对研究RESTful继承建模的进一步问题感兴趣 我可以说,狗是动物,母鸡也是,但母鸡是蛋,而狗是哺乳动物,所以它不能。类似API的 获取动物/动物/蛋 不一致,因为表明所有动物亚型都可以有卵(作为Liskov替代的结果)。这个