OpenAPI 3 -- 属性 在写入时是可选的,但在读取时是必需的

OpenAPI 3 -- Property is optional when written but required when read

我的资源有一个ID(典型案例)。

它还有一个 slug,一个人类可读但仍然唯一的标识符(主要是为了美化 URL)。

创建资源时,此 slug 可选。如果客户提供了一个,它正在被使用;否则,服务器生成一个。

但是在读取资源时slug必需的

我们确实希望这种区别很明确,因此任何阅读 OpenAPI 规范的工具都知道会发生什么。

这当然可以使用与 allOf 修饰符链接的不同模式的组合来实现(参见下面的示例),但我想避免必须执行此组合(假设它与第一名)。

所以我的问题是:

在 OpenAPI >= 3.0.2 中有没有办法声明 属性 required-readOnly 和 optional-writeOnly?

使用组合的解决方案:

openapi: 3.0.2
info:
  title: Person API
  version: 1.0.0

paths: 
  '/persons/{person-slug}':
    get:
      parameters:
        - $ref: '#/components/parameters/PersonSlug'
      responses:
        200:
          description: Information on a person.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SlugRead'
                  - $ref: '#/components/schemas/Person'
    post:
      parameters:
        - $ref: '#/components/parameters/PersonSlug'
      responses:
        200:
          description: Information on a person.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SlugWrite'
                  - $ref: '#/components/schemas/Person'

components:

  parameters:

    PersonSlug:
      name: 'person-slug'
      description: Human readable unique ID of a person.
      required: true
      in: path
      schema:
        type: string

  schemas:

    SlugRead: # required
      required:
        - slug
      properties:
        slug:
          type: string
          readOnly: true

    SlugWrite: # not required
      properties:
        slug:
          type: string

    Person:
      required:
        - first_name
        - last_name
        - birth_date
      properties:
        first_name:
          type: string
        last_name:
          type: string
        birth_date:
          type: string
          format: date    

看不出如何实现完全你所追求的是可能的,但我认为效果可以通过使用单独的字段来定义所需的(只写)与已分配(只读)slug:

Person:
  required:
    - first_name
    - last_name
    - birth_date
    - assigned_slug
  properties:
    first_name:
      type: string
    last_name:
      type: string
    birth_date:
      type: string
      format: date
    assigned_slug:
      type: string
      readOnly: true
    desired_slug:
      type: string
      writeOnly: true

虽然不知道是否有任何工具可以用它做正确的事情!

对于将来偶然发现这个问题的人,我使用基本模式和派生模式解决了这个问题。

Person:
  properties:
    id:
      type: integer
      readOnly: true
    name:
      type: string
    slug:
      type: string

PersonWRITE:
  allOf: # name is required, slug is optional and id is not available
    - $ref: '#/components/schemas/Person'
    - required: [name]

PersonREAD:
  allOf: # everything is available and required
    - $ref: '#/components/schemas/Person'
    - required: [id, name, slug]

它本质上是 OP 的解决方案,但已移至组件级别,因此您不必在 request/response 定义中使用 'allOf'。


编辑: 请注意以下内容将不起作用:

PersonWRITE: #wrong
  $ref: '#/components/schemas/Person'
  required: [name]

正如文档所述 Any sibling elements of a $ref are ignored. This is because $ref works by replacing itself and everything on its level with the definition it is pointing at.

关键是allOf,它结合了列表中对象的所有属性。在 $ref 扩展后,架构看起来像这样(仍然是一个有效的 yaml,我只是对其进行了一些 jsonified 以指出数组元素实际上是两个属性将被组合的对象):

PersonWRITE:
  allOf: [
    {
      properties: {
        id:   { type: integer, readOnly: true },
        name: { type: string },
        slug: { type: string }
      }
    },
    {
      required: [name]
    }
  ]