如何使用继承对RESTful API建模?

如何使用继承对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

我有一个需要通过RESTful API公开的对象层次结构,我不确定我的URL应该如何构造,它们应该返回什么。我找不到任何最佳实践

比如说,我养的狗和猫都是从动物身上遗传下来的。我需要对狗和猫进行CRUD手术;我也希望能够在动物身上做手术

我的第一个想法是这样做:

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
  • 仅在属性级别区分动物
为同一资源设置多个URI从来都不是一个好主意,因为它会导致混乱和意外的副作用。考虑到这一点,您的单个URI应该基于像
/animals
这样的通用方案

下一个挑战是在“基础”级别处理整个猫狗集合,这一挑战已经通过
/animals
URI方法解决了

使用媒体类型中的查询参数和标识属性组合,可以轻松解决处理狗和猫等特殊类型的最后一个难题。例如:

GET/animates
Accept:application/vnd.vet services.animates+json

  • GET/animals
    -获取所有的狗和猫,将返回Rex和手套
  • GET/animals?type=dog
    -获取所有狗,只返回Rex
  • 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替代的结果)。这个