自Swagger诞生以来,OpenAPI已经走过了漫长的道路。它有很好的工具,在所有的会议上被谈论,被政府、主要银行、医疗机构、GitHub、Stripe等各种机构使用。每个人都在用它为REST的世界带来类型系统的好处,多年来,很多人把REST与Rando-JSON-over-HTTP混淆了。尽管如此,仍有一些奇怪的问题需要解决,其中一个例子就是例子。
OpenAPI v3.0规范中关于如何添加实例的信息相当简短,在文件的几个部分提到了各种类型的实例对象。作为OpenAPI的用户,我在尝试学习如何添加示例的过程中有些困惑,因为SmartBear的这个教程只是让我更清楚一些。
在模式对象、参数和媒体类型对象(请求和响应)中都有例子。有时有例子,有时有例子,这些看起来也不一样,这取决于它们的位置。 OpenAPI v2(OAS2)和OpenAPI v3(OAS3)处理例子的方式不同。OAS2缺少一些类型,OAS3增加了新的方式,在某些步骤中保留了旧的方式,并改变了OAS2中一些事情的方式。😳
我在Stoplight从事工具工作的时间,以及对OpenAPI本身的贡献,只是让这个混乱的问题更加明显。
媒体类型
在OAS3媒体类型对象中添加例子,允许你创建一个完整的请求或响应例子。你可以展示一些不同类型的成功,如果你支持多态性,你可以创建一只猫和一只狗,有不同的猫或狗的相关属性。
有两个关键词可以创建媒体类型的例子:example 或examples 。这些关键字之间不仅有s ,它们的形状也不同。example 是单数的例子,它只包含实际的例子值:
# OpenAPI v3
responses:
"200":
description: OK
content:
application/json:
example:
id: 1
name: get food
completed: false
schema:
properties:
id:
type: integer
name:
type: string
completed:
type: boolean
completed_at:
type: string
format: date-time
nullable: true
required:
- id
- name
- completed
examples 是一个数组对象,它有一个任意的字符串,作为该例子的昵称,该属性是另一个对象,它包含几个可选的属性,包括一个 属性,然后包含实际的例子。value
# OpenAPI v3.0
responses:
"200":
content:
application/json:
examples:
Incomplete Task:
value:
id: 1
name: get food
completed: false
Complete Task:
value:
id: 2
name: get cider
completed: true
completed_at: 2020-08-23T13:22:52.685Z
schema:
properties:
id:
type: integer
name:
type: string
completed:
type: boolean
completed_at:
type: string
format: date-time
nullable: true
required:
- id
- name
- completed
在OAS3中,像 "未完成的任务 "或 "完整的任务 "这样的例子名称是任意的,大多数文档工具会显示它,以帮助用户挑选他们想看的例子。 Github用它来显示按路径请求版本库内容时的各种响应:可能是文件、目录、符号链接或子模块,所以他们对每种情况都有不同的例子:
# OpenAPI v3.0
responses:
'200':
description: response
content:
application/vnd.github.v3.object:
schema:
"$ref": "#/components/schemas/content-tree"
application/json:
schema:
oneOf:
- "$ref": "#/components/schemas/content-directory"
- "$ref": "#/components/schemas/content-file"
- "$ref": "#/components/schemas/content-symlink"
- "$ref": "#/components/schemas/content-submodule"
examples:
response-if-content-is-a-file:
"$ref": "#/components/examples/content-file-response-if-content-is-a-file"
response-if-content-is-a-directory:
"$ref": "#/components/examples/content-file-response-if-content-is-a-directory"
response-if-content-is-a-symlink:
"$ref": "#/components/examples/content-file-response-if-content-is-a-symlink"
response-if-content-is-a-submodule:
"$ref": "#/components/examples/content-file-response-if-content-is-a-submodule"
这看起来有点像这样:

来自GitHub OpenAPI描述的多个命名的例子,在ReDoc
中显示,这是一个免费的开源文档生成工具。
有这两种不同类型的例子,它们的形状相当不同,可能会让一些人感到困惑,但如果你看一下OAS2,就会更加困惑了:
# OpenAPI v2.0
responses:
'200':
description: 'OK'
schema:
properties:
id:
type: integer
name:
type: string
completed:
type: boolean
completed_at:
type: string
format: date-time
required:
- id
- name
- completed
examples:
application/json:
id: 2
name: get cider
completed: true
completed_at: 2020-08-23T13:22:52.685Z
尽管都使用了examples 这个关键字,但OAS2和OAS3在处理这个关键字的方式上有所不同,OAS2只为API被定义为生产/消费的每种mime类型处理一个单一的例子,而OAS3则允许有多个名字任意的例子。
请注意,这些例子都是在schema 关键字旁边定义的,而不是在它里面。如果一个例子被定义在模式对象里面,就会有完全不同的规则...
模式实例
模式对象在OAS2和OAS3中都有多处使用:请求和响应是最常见的两个。一个模式可以为整个对象、对象的一部分或该对象中的一个特定属性提供一个例子:
# OpenAPI v3
responses:
"200":
description: OK
content:
application/json:
schema:
properties:
id:
type: integer
name:
type: string
completed:
type: boolean
completed_at:
type: string
format: date-time
nullable: true
required:
- id
- name
- completed
example:
id: 2
name: get cider
completed: true
completed_at: 2020-08-23T13:22:52.685Z
这看起来和我们进一步看的媒体类型的例子很相似,没有什么有趣的,除了它在模式对象里面而不是在它旁边的事实。如果你维护OpenAPI工具,这可能会让你受挫,所以请检查你的工具是否支持它。
除了在模式内给整个模式提供例子外,你还可以在属性层面为单个属性创建例子:
responses:
'200':
description: 'OK'
content:
application/json:
schema:
properties:
id:
type: integer
name:
type: string
example: get food
completed:
type: boolean
example: false
completed_at:
type: string
format: date-time
example: '1955-04-23T13:22:52.685Z'
required:
- id
- name
- completed
OAS2和OAS3至少在这个逻辑上是一致的,但这对工具人员来说可能是相当混乱的。当你有这样的例子时,它们应该根据它们的模式或属性的例子进行验证。
如果正在渲染文档,就从相关的mime类型的例子开始--如果有多个媒体类型,就从该媒体类型的第一个相关例子开始--如果没有这些例子,就用openapi-sampler或类似的方法从属性例子中构建一个例子。我们也用它来生成Prism(HTTP模拟服务器)的模拟响应。
如果你只是想写OpenAPI,我一般喜欢使用属性例子的方法(example 在每个属性上),只要没有可以作为例子的东西。如果定义了一个default ,那就会被使用。如果定义了一个枚举,就会使用这些值中的一个。如果我想创建一些比从模式属性中生成的例子更实用的东西,我会定义一个媒体类型的例子,特别是当有效载荷是动态的和复杂的,并且我想显示字段的特定组合而不是所有的东西。
参数实例
OAS3参数对象描述了路径参数、查询参数、头文件等。在OAS3中,他们可以有examples 或一个example ,或者由于OAS3让参数对象有一个模式,他们可以有模式的例子,就像我们上面谈到的......那是三件事:
# OpenAPI v3
/params:
get:
parameters:
- name: single-example-good
description: Valid to its schema
in: query
schema:
type: string
enum: [foo, bar]
example: foo
- name: single-schema-example-good
description: Valid to its schema
in: query
schema:
type: string
enum: [foo, bar]
example: foo
- name: multiple-examples
description: Some valid to its schema some not
in: query
schema:
type: string
enum: [foo, bar]
examples:
the-good:
value: foo
the-bad:
value: 123
the-ugly:
value: [an, array]
AGH那是一个很多地方可以检查的例子。
OAS2没有让参数有一个例子,至少不是正式的。OAS2规范没有定义参数如何工作,但 "供应商扩展 "来了,让任何人使用x-example 关键字。根据你所使用的工具,它将知道这个关键词的含义,或者完全忽略它:
# OpenAPI v2.0
parameters:
- name: fruit
description: Short name of the fruit you're trying to find
in: query
type: string
x-example: apple
OAS2参数对象也没有schema 关键字,所以你不必像OAS3那样担心那个 "第三地"。
AGH
是的。这在Stoplight已经引起了一段时间的混乱,最近当我们试图确保Spectral 正确地验证所有的例子时,就出现了问题。我们主要是支持模式实例,即使如此,也主要是支持属性实例的方法,但我们已经投入了一大堆努力来支持所有的实例,以便未来版本的Spectral和Stoplight Studio能够让你知道你是否未能成功地穿越这个雷区。
OpenAPI v3.1也部分地解决了这个问题,并给他增加了一些燃料,因为JSON Schema有它自己的examples 关键字。这个多例关键词与OAS2或OAS3中的任何examples ,它只是一个模式或属性的可能值的裸阵列:
schema:
properties:
coordinates:
description: We couldn't pick a format for coordinates so we support
pretty much all of them.
examples:
- "52.3667° N, 4.8945° E"
- "52.377956, 4.897070"
- [52.377956, 4.897070]
- { lat: 52.377956, lon: 4.897070 }
如果这和媒体类型或参数examples 关键字一样,你需要给它一个mime类型的键,或一个任意的键,并将值嵌套在value ,但它是第三个完全不同类型的examples...
你不能在OAS2或OAS3中使用这种方法,但当OpenAPI v3.1发布时,你将能够使用它,因为它现在正确解决了JSON Schema的分歧。所以,支持JSON Schema是好的,但是......要用另一种方式来处理例子是很难的。
这些都是有效的,而且各种组合可以而且确实存在:
/infinite-examples:
get:
operationId: infinite-examples
responses:
"200":
description: OK
content:
application/json:
schema:
properties:
name:
type: string
# OpenAPI Schema Object Example
example: stowford
coordinates:
description: We couldn't pick a format for coordinates so we support
pretty much all of them.
# JSON Schema Examples from 2019-09
examples:
- "52.377956, 4.897070"
- [52.377956, 4.897070]
- { lat: 52.377956, lon: 4.897070 }
required:
- name
- coordinates
# OpenAPI Schema Object Example (but for an object)
example:
name: freddy
coordinates: "52.377956, 4.897070"
# OpenAPI Media Type Example
example:
name: finn
coordinates: "52.377956, 4.897070"
# OpenAPI Media Type Examples
# cannot have this and the OpenAPI Media Type Example together
examples:
arbitrary example name:
value:
name: finns evil twin
coordinates: "52.377956, 4.897070"
如果我们要走出这个困境,我们需要终端用户和工具人员的参与:
- 现在就把你的OpenAPI描述升级到OpenAPI v3.0,如果你的OpenAPI工具是更现代的,就切换到
如果你的旧工具不支持OpenAPI,请切换到更现代的OpenAPI工具。这样,我们都有希望用火烧掉Ye Olden OAS2,并远离供应商的扩展黑客。
-
让我们简化OAS3.1,但删除其中一些多余的例子方法。也许从OAS3.1的参数对象中废除
example和examples,让模式实例接管?或者从所有地方删除example,这样就只剩下OpenAPIexamples和JSON Schemaexamples? -
如果你维护工具,请在OAS3.1仍是候选发布版时加入对它的支持。
候选版本。开始使用其他OAS 3.1的工具,以确保它的工作,这将加速每个人的迁移,当它最终完成。
- 最终,OAS2和OAS3将成为遥远的记忆,无论是用户还是工具供应商,都不需要弄清楚这一团糟。
如果你想在OAS2和OAS3的例子中搞清楚更多的例子,无论什么原因,都可以看看这个样本库。