OpenAPI:为 GET、PUT、POST 设计可重用的架构定义

Max*_*Max 10 rest nullable dto swagger openapi

我正在使用 OpenAPI 3.0 设计和实现一个简单的 API,以允许对数据库实体进行基本的 CRUD 操作。

让我们假设一个实体Pet具有一些客户端给定的属性 ( name& age) 和一些生成的属性 ( id& created):

components:
  schemas:
    Pet:
      type: object
      properties:
        id:
          type: string
        created:
          type: string
          format: date-time
        name:
          type: string
        age:
          type: integer
Run Code Online (Sandbox Code Playgroud)

我想指定 REST 端点来 POST、GET、PUT(如果可能的话 PATCH)宠物:

paths:

  /pets:
    post:
      operationId: createPet
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "200":
          content:
            application/json:
              schema:
                type: boolean
 
  /pets/{id}:
    parameters:
      - name: id
        schema:
          type: string
        in: path
        required: true

    get:
      operationId: getPet
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"

    put:
      operationId: updatePet
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "200":
          content:
            application/json:
              schema:
                type: boolean

    patch:
      operationId: alterPet
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "200":
          content:
            application/json:
              schema:
                type: boolean
Run Code Online (Sandbox Code Playgroud)

从逻辑角度来看,每个端点都需要以下属性:

  • 得到:idcreatednameage
  • 邮政:nameage
  • 放:nameage
  • PATCH:nameage(或无,取决于实现)

我在这里看到两种主要方法:

方法 1:将所有属性保留为可选

DTOPet用作外壳,其中所有属性都是可选的,如上面定义的。由服务器和客户端检查端点调用是否填写了所需的属性。如果在 POST/PUT/PATCH 请求正文中设置了非必需属性,它们将被忽略。

优点:

  • API 规范中的单一、简单且对称的 DTO 架构定义。
  • 客户端“GET -> 修改 -> PUT”工作流程的顺利集成(例如,带有 Formik 的 React 前端)。
  • 支持开箱即用的 PATCH 端点。

缺点:

  • 所有属性都是可选的,在 Typescript 中处理这些string | undefined类型非常繁琐。这在 GET 方向上尤其烦人,因为我们知道只要检索 DTO,所有属性都会被填充Pet
  • 实现上的“痛苦”遍布服务器和所有可能的客户端。

方法 2:为所有操作定义单独的 DTO 架构

我们在 API 规范中引入了GetPetPutPetPostPet、 和 以及PatchPet相应的列表。required是的,PutPet并且PostPet可以相同,但也许我们希望允许GetPet使用修改来PutPet简化客户端的“GET -> 修改 -> PUT”工作流程。

想象一下该Pet实体具有 5 个以上生成的属性和 20 个以上客户端给定的属性。我们不想平面定义每个实体的 4 个变体,而是使用某种继承。有没有办法引入一个BasePet轴承所有属性(设置为可选)并让 4 个特定于操作的 DTO 通过覆盖列表来扩展它required

优点:

  • 由于所有属性的可选性都得到澄清,因此客户端和服务器的实现更容易。
  • API 规范中处理了“痛苦”。

缺点:

  • API 规范变得越来越复杂。我不清楚所需的继承是否可能以及如何指定。
  • API 的可扩展性和可维护性可能会受到影响。

所以我对这个主题的问题是:如何为 OpenAPI 3.0 指定 DTO 的继承?有更好的选择吗?我很高兴收到有关基本 CRUD 操作 API 的这些想法的所有建议。

use*_*751 1

我最近在为客户设计 API 时确实遇到了类似的问题。这里的技巧是$ref:在不同路线的模式中进行冗余。为简单起见,我将仅定义GETPOSTPATCH

注意:在这种情况下,它更容易,因为操作的模式是操作的模式的子集。

相比之下,想象一个用户对象,其中有一个password字段,它应该是可写的,但不可读。在这种情况下,您将需要三个不同的模式:

  • userRead
  • userWrite
  • userCommonFieldsForReadAndWrite

无论如何,这是解决您的问题的一种可能的解决方案:

paths:
  /pets:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/petCreateUpdate"
      responses:
        "201":
          description: Pet created.
          headers:
            Location:
              schema:
                type: string
              description: points to the Pet resource created. Can be directly used in a subsequent GET request.
              example: "Location: /pets/32"
  /pets/{id}:
    parameters:
      - name: id
        schema:
          type: string
        in: path
        required: true
    get:
      responses:
        "200":
          description: Pet with given id returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/petRead"
        "404":
          description: Pet with given id not found.
    patch:
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/petCreateUpdate"
      responses:
        "204":
          description: Pet updated successfully.

components:
  schemas:

    petRead:
      allOf:
        - type: object
          properties:
            id:
              type: integer
              format: int32
              minimum: 1
            created:
              type: string
              format: date-time
        - $ref: "#/components/schemas/petCreateUpdate"

    petCreateUpdate:
      type: object
      properties:
        name:
          type: string
        age:
          type: number
          format: integer
          minimum: 1
          maximum: 100
Run Code Online (Sandbox Code Playgroud)

这在 SwaggerEditor 中呈现如下: