如何使用继承建模RESTful API?

Alp*_*rae 79 api rest polymorphism inheritance

我有一个对象层次结构,我需要通过RESTful API公开,我不确定我的URL应该如何构建以及它们应该返回什么.我找不到任何最佳做法.

假设我有继承动物的狗和猫.我需要对狗和猫进行CRUD操作; 我也希望能够对动物进行一般操作.

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

GET /animals        # get all animals
POST /animals       # create a dog or cat
GET /animals/123    # get animal 123
Run Code Online (Sandbox Code Playgroud)

问题是/ animals集合现在"不一致",因为它可以返回并获取不具有完全相同结构的对象(狗和猫).将集合返回具有不同属性的对象,它被认为是"RESTful"吗?

另一个解决方案是为每个具体类型创建一个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
Run Code Online (Sandbox Code Playgroud)

但现在狗和猫之间的关系就失去了.如果想要检索所有动物,必须查询狗和猫的资源.每个新的动物子类型的URL数量也会增加.

另一个建议是通过添加以下内容来扩充第二个解决方案:

GET /animals    # get common attributes of all animals
Run Code Online (Sandbox Code Playgroud)

在这种情况下,返回的动物将仅包含所有动物共有的属性,丢弃特定于狗的属性和特定于猫的属性.这允许检索所有动物,尽管细节较少.每个返回的对象都可以包含指向详细的具体版本的链接.

有什么意见或建议吗?

Bri*_*lly 37

我会建议:

  • 每个资源只使用一个URI
  • 仅在属性级别区分动物

为同一资源设置多个URI永远不是一个好主意,因为它可能会导致混淆和意外的副作用.鉴于此,您的单个URI应基于类似的通用方案/animals.

通过/animalsURI方法已经解决了在"基础"级别处理整个狗和猫集合的下一个挑战.

使用媒体类型中的查询参数和标识属性的组合,可以轻松解决处理狗和猫等特殊类型的最终挑战.例如:

GET /animals(Accept : application/vnd.vet-services.animals+json)

{
   "animals":[
      {
         "link":"/animals/3424",
         "type":"dog",
         "name":"Rex"
      },
      {
         "link":"/animals/7829",
         "type":"cat",
         "name":"Mittens"
      }
   ]
}
Run Code Online (Sandbox Code Playgroud)
  • GET /animals - 得到所有的狗和猫,将返回雷克斯和连指手套
  • GET /animals?type=dog - 得到所有的狗,只会返回雷克斯
  • GET /animals?type=cat - 得到所有的猫,只会是连指手套

然后,当创建或修改动物时,呼叫者有责任指定所涉及的动物类型:

媒体类型: application/vnd.vet-services.animal+json

{
   "type":"dog",
   "name":"Fido"
}
Run Code Online (Sandbox Code Playgroud)

上述有效载荷可以与a POSTPUT请求一起发送.

上述方案通过REST获得与OO继承相似的基本类似特征,并且能够在不进行大型手术或对URI方案进行任何更改的情况下添加更多特化(即更多动物类型).

  • 如果猫和狗各自具有独特的属性呢?你将如何处理`POST`操作,因为大多数框架都不知道如何正确地将其反序列化为模型,因为json没有良好的输入信息.你将如何处理邮件案例,例如`[{"type":"dog","name":"Fido","playsFetch":true},{"type":"cat","name":"Sparkles", "likesToPurr": "有时"}`? (17认同)
  • 我建议:'GET /动物 - 得到所有的狗和猫``GET /动物/狗 - 得到所有狗``GET /动物/猫 - 得到所有猫 (9认同)

dba*_*tor 6

在最新版本的 OpenAPI 中引入的最新增强功能的支持下,可以更好地回答这个问题。

自 JSON 模式 v1.0 起,可以使用 oneOf、allOf、anyOf 等关键字组合模式并获得验证的消息有效负载。

https://spacetelescope.github.io/understanding-json-schema/reference/combining.html

但是,在 OpenAPI(以前的 Swagger)中,模式组合已通过关键字discriminator (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
Run Code Online (Sandbox Code Playgroud)