赞
踩
这一章主要介绍 API 的基本组成部分,包括提供给 API 消费者(所有可能访问 API 的个体,下简称 “消费者”)的的不同 HTTP 请求方法、路径,请求和消息体中的参数,以及返回给消费者的不同 HTTP 状态及响应消息体。
<?php
/**
* @SWG\Swagger(
* swagger="2.0",
* schemes={"https"},
* host="tcmzapi.emao.com",
* basePath="/",
* @SWG\Info(
* version="1.0.0",
* title="淘车猫中转库 Api 文档",
* description="淘车猫中转库 Api 文档"
* )
* )
*/
这个文档的内容分成四部分,下面分别来说明。
首先我们要通过一个 swagger 属性来声明 OpenAPI 规范的版本。
<?php
/**
* @SWG\Swagger(
* swagger="2.0"
* )
*/
你没看错,是 swagger
,上面已经介绍了,OpenAPI 规范是基于 Swagger 的,在未来的版本中,这个属性可能会换成别的。 目前这个属性的值,暂时只能填写为 2.0
。
然后我们需要说明一下 API 文档的相关信息,比如 API 文档版本(注意不同于上面的规范版本)、API 文档名称已经可选的描述信息。
<?php
/**
* @SWG\Info(
* version="1.0.0",
* title="淘车猫中转库 Api 文档",
* description="淘车猫中转库 Api 文档"
* )
*/
作为 web API,一个很重要的信息就是用来给消费者使用的根URL
,可以用协议(http 或者 https)、主机名、根路径来描述:
<?php
/**
* @SWG\Swagger(
* schemes={"https"},
* host="tcmapi.emao.com",
* basePath="/v1"
* )
*/
这这个例子中,消费者把 https://tcmapi.emao.com/v1
作为根节点来访问各种 API。因为和具体环境有关,不涉及 API 描述的根本内容,所以这部分信息是可选的。
如果我们要展示一组用户信息,可以这样描述:
<?php /** * @SWG\Swagger( * swagger="2.0" * schemes={"https"}, * host="tcmapi.emao.com", * basePath="/v1.0", * @SWG\Info( * version="1.0.0", * title="淘车猫 Api 文档", * description="淘车猫 Api 文档" * ) * ) * @SWG\Get( * path="/persons", * summary="获取一些人", * description="返回包含所有人的列表。", * @SWG\Response( * response=200, * description="一个用户列表", * @SWG\Schema( * type="array", * @SWG\Items( * required={"username"} * @SWG\Properties() * ) * ), * ), * ) */
路径
(path)我们添加一个 /persons
的 路径
,用来访问一组用户信息
在每个路径
中,我们可以添加任意的 HTTP动词
,如 GET/POST/PUT/DELETE
等来操作所需要的资源。
比如需要展示一组用户信息,我们可以在 /person
路径中添加 get
方法,同时还可以填写一些简单的描述信息 (summary) 或者说明该主主法的一段长篇大论 (description)
<?php
/**
* @SWG\Get(
* path="/persons",
* summary="获取一些人",
* description="返回包含所有人的列表。"
* )
*/
这样一来,我们调 get https://tcmapi.emao.com/v1/persons 方法就能获取一个用户信息列表了。
响应
(response) 类型对于每个方法(或操作),我们都可以在响应
(responses) 中添加任意的 HTTP状态码
(比如 200 OK 或者 404 Not Found 等)。这个例子中我们添加上 200
的响应:
<?php
/**
* @SWG\Response(
* response=200,
* description="一个用户列表"
* )
*/
get /persons 这个接口返回一组用户信息,我们通过响应消息中的模式
(schema)属性来描述清楚具体的返回内容。
一组用户信息就是一个用户信息对象的数组
(array),每一个数组元素则是一个用户信息对象
(object),该对象包含三个 string 类型的属性:姓氏
、名字
、用户名
,其中用户名必须提供(required)。
<?php /** * @SWG\Schema( * type="array", * @SWG\Items( * required={"username"}, * @SWG\Property( * property="firstName", * type="string", * description="firstName" * ), * @SWG\Property( * property="lastName", * type="string", * description="lastName" * ), * @SWG\Property( * property="username", * type="string", * description="username" * ) * ) * ) */
请求参数
(query parameteers)用户太多,我们不想一股脑全部输出出来。这个时候,分页输出是个不错的选择,我们可以通过添加请求参数来实现。
<?php
/**
* @SWG\Parameter(
* name="pageSize",
* in="query",
* description="Number of persons returned",
* type="integer"
* ),
* @SWG\Parameter(
* name="pageNumber",
* in="query",
* description="Page number",
* type="integer"
* )
*/
<?php
/**
* @SWG\Get(
* path="/persons",
* summary="获取一些人",
* description="返回包含所有人的列表。",
* @SWG\Parameter()
* )
*/
在参数列表中,我们添加两个名字(name)分别叫做 pageSize
和 pageNumber
的整型(integer)参数,并作简单描述:
<?php /** * @SWG\Get( * path="/persons", * summary="获取一些人", * description="返回包含所有人的列表。", * @SWG\Parameter( * name="pageSize", * in="query", * description="Number of persons returned", * type="integer" * ), * @SWG\Parameter( * name="pageNumber", * in="query", * description="Page number", * type="integer" * ) * ) */
这样一来,消费者就可以通过 get /persons?pageSize=20&pageNumber=2 来访问第 2 页的用户信息(不超过 20 条)了。
路径参数
(path parameter)有时候我们想要根据用户名来查找用户信息,这时我们需要增加一个接口操作,比如可以添加一个类似 /persons/{username} 的操作来获取用户信息。注意,{username} 是在请求路径中的参数。
<?php
/**
* @SWG\Get(
* path="/persons/{username}",
* summary="获取一些人",
* description="返回包含所有人的列表。",
* @SWG\Parameter(
* name="username",
* in="path",
* required="true"
* description="The person's username",
* type="string"
* )
* )
*/
首先我们在 /persons 路径后面,增加一个 /persons/{username} 的路径,并定义一个 get (操作)方法。
<?php
/**
* @SWG\Get(
* path="/persons/{username}",
* summary="Gets a person",
* description="Returns a single person for its username"
* )
*/
因为 {username} 是路径参数,我们需要先像请求参数一样将它添加到 parameters 属性中,注意名称应该同上面大括号( { } ) 里面的名称一致。并通过 in
这个属性,来表示它是一个路径(path)参数。
<?php
/**
* @SWG\Parameter(
* name="username",
* in="path",
* required="true"
* description="The person's username",
* type="string"
* )
*/
定义路径参数时很容易出现的问题就是忘记:required: true
,Swagger 的自动完成功能中没有包含这个属性定义。 如果没有写 require 属性,默认值是 false,也就是说 username 参数时可选的。可事实上,作为路径参数,它是必需的。
别忘了获取单个用户信息也需要填写 200 响应消息,响应消息体的内容就是之前描述过的用户信息(用户信息列表中的一个元素):
<?php /** * @SWG\Response( * response=200, * description="A Person", * @SWG\Schema( * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) * ) */
当然,API 的提供者会对 username 进行校验,如果查无此人,应该返回 404 的异常状态。所以我们再加上 404 状态的响应:
<?php
/**
* @SWG\Response(
* response=404,
* description="The Person does not exists."
* )
*/
消息体参数
(body parameter)当我们需要添加一个用户信息时,我们需要一个能够提供 post /persons 的 API 操作。
<?php /** * @SWG\Post( * path="/persons", * summary="Creates a person", * description="Adds a new person to the persons list.", * @SWG\Parameter( * name="person", * in="body", * required="true" * description="The person to create.", * @SWG\Schema( * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) * ), * @SWG\Response( * response="200", * description="Persons succesfully created." * ), * @SWG\Response( * response="400", * description="Persons couldn't have been created." * ) * ) */
首页在 /persons 路径下添加一个 Post 操作:
<?php
/**
* @SWG\Post(
* path="/persons",
* summary="Creates a person",
* description="Adds a new person to the persons list.",
* )
*/
接下来我们给 post 方法添加参数,通过 in
属性显式说明参数是在 body 中的。参数的定义参考 get /persons/{username} 的 200 响应消息体参数,也就是包含用户的姓氏、名字、用户名。
<?php /** * @SWG\Parameter( * name="person", * in="body", * required="true" * description="The person to create.", * @SWG\Schema( * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) * ) */
最后不要忘记定义 post 操作的响应消息。
<?php
/**
* @SWG\Response(
* response="200",
* description="Persons succesfully created."
* ),
* @SWG\Response(
* response="400",
* description="Persons couldn't have been created."
* )
*/
现在我们已经学会了编写 API 文档的基本方法。不过上面的例子中存在一些重复,这对于程序员的嗅觉来说,就是代码的 “坏味道”。这一章我们一起学习如何通过抽取可重用的定义(definitions)来简化 API 文档。
我们认真观察第 2 章最后输出的 API 文档,很容易发现 Person 的定义出现了三次,非常的不 DRY☹。
现在,我们通过可重用的 定义
(definition)来重构这个文档:
<?php /** * @SWG\Definition( * definition="Person", * type="object", * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) */ /** * @SWG\Definition( * definition="Persons", * type="array", * @SWG\Items(ref="#/definitions/Person") * ) */
文档简化了很多。这得益于 OpenAPI 规范中关于定义(definition)的章节中允许我们 “一处定义,处处使用”。
定义
(definitions) 项我们首先在 API 文档的尾部添加一个 定义
(definitions)项(其实它也可以放在文档的任意位置,只不过大家习惯放在文档末尾):
<?php
/**
* @SWG\Definition()
*/
定义
然后我们增加一个 Person 对象的定义:
<?php /** * @SWG\Definition( * definition="Person", * type="object", * required={"username"}, * @SWG\Property( * name="firstName", * type="string" * ), * @SWG\Property( * name="lastName", * type="string" * ), * @SWG\Property( * name="username", * type="string" * ) * ) */
定义
来增加另一个 定义
在定义项中,我们可以立即引用刚才定义好的 Person 来增加另一个 定义
,Persons。Persons 是一个 Person 对象的数组。与之前直接定义的不同之处是,我们增加了一个 引用
(reference)属性,也就是 ref 来引用 Person 。
<?php
/**
* @SWG\Definition(
* definition="Persons",
* type="array",
* @SWG\Items(ref="#/definitions/Person")
* )
*/
定义
一旦定义好了 Person ,我们可以把原来在响应消息中相应的定义字段替换掉。
原来:
<?php /** * @SWG\Response( * response=200, * description="A list of Person", * @SWG\Schema( * type="array", * @SWG\Items( * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) * ) * ) */
现在:
<?php
/**
* @SWG\Response(
* response=200,
* description="A list of Person",
* @SWG\Schema(
* type="array",
* @SWG\Items(ref="#/definitions/Persons")
* )
* )
*/
原来:
<?php /** * @SWG\Response( * response=200, * description="A Person", * @SWG\Schema( * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) * ) */
现在:
<?php
/**
* @SWG\Response(
* response=200,
* description="A Person",
* @SWG\Schema(ref="#/definitions/Person")
* )
*/
定义
不仅仅在消息中可以使用 定义
,在参数中也可以使用。
原来:
<?php /** * @SWG\Post( * path="/persons", * summary="Creates a person", * description="Adds a new person to the persons list.", * @SWG\Parameter( * name="person", * in="body", * required="true" * description="The person to create.", * @SWG\Schema( * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) * ) * ) */
现在:
<?php
/**
* @SWG\Post(
* path="/persons",
* summary="Creates a person",
* description="Adds a new person to the persons list.",
* @SWG\Parameter(
* name="person",
* in="body",
* required="true"
* description="The person to create.",
* @SWG\Schema(ref="#/definitions/Person")
* )
* )
*/
我们看到了 引用
($ref)的作用,接下来我们再把它用到响应消息的定义中:
发生 HTTP 500 错误时,假如我们希望每一个 API 操作都返回一个带有错误码(error code)和描述信息(message)的响应,我们可以这样做:
<?php /** * @SWG\Response( * response="500", * description="An unexpected error occured.", * @SWG\schema( * @SWG\Property( * property="code", * type="string" * ), * @SWG\Property( * property="message", * type="string" * ) * ) * ) */
按照 “一处定义、处处引用” 的原则,我们可以在定义项中增加 Error 的定义:
<?php
/**
* @SWG\Definition(
* definition="Error",
* @SWG\Property(
* property="code",
* type="string"
* ),
* @SWG\Property(
* property="message",
* type="string"
* )
* )
*/
而且我们也学会了使用 引用
($ref),所以我们可以这样写:
<?php
/**
* @SWG\Response(
* response="500",
* description="An unexpected error occured.",
* @SWG\schema(ref="#/definitions/Error")
* )
*/
上面的文档中,还是有一些重复的内容。我们可以根据 OpenAPI 规范中的 responses 章节的描述,通过定义一个可重用的响应消息,来进一步简化文档。
<?php
/**
* @SWG\Response(
* response="Standard500ErrorResponse",
* description="An unexpected error occured.",
* @SWG\schema(ref="#/definitions/Error")
* )
*/
注意:响应消息中引用了 Error 的定义。
我们还是通过 引用
($ref)来使用一个已经定义好的响应消息,比如:
<?php
/**
* @SWG\Response(
* response="200",
* description="A list of Person",
* @SWG\schema(ref="#/definitions/Persons")
* ),
* @SWG\Response(
* response="500",
* ref="#/responses/Standard500ErrorResponse"
* )
*/
略去一部份不重要的。
通过前面的练习,我们可以写出一篇结构清晰、内容精炼的 API 文档了。可是 OpenAPI 规范还给我们提供了更多的便利和惊喜,等着我们去了解和掌握。这一章主要介绍用于定义属性和数据模型的高级方法。
使用 JSON Schema Draft 4,我们可以定义任意类型的各种属性,举例说明。
当定义个字符串属性时,我们可以定制它的长度及格式:
属性 | 类型 | 描述 |
---|---|---|
minLength | number | 字符串最小长度 |
maxLength | number | 字符串最大长度 |
pattern | string | 正则表达式 |
如果我们规定用户名是长度介于 8~64,而且只能由小写字母和数字来构成,那么我们可以这样写:
<?php
/**
* @SWG\Property(
* property="username",
* type="string",
* pattern="[a-z0-9]{8,64}",
* minLength="8",
* maxLength="64"
* )
*/
日期和时间的处理参考 RFC 3339 ,我们唯一要做的就是写对格式:
格式 | 属性包含内容 | 属性示例 |
---|---|---|
date | ISO8601 full-date | 2016-04-01 |
dateTime | ISO8601 date-time | 2016-04-16T16:06:05Z |
如果我们在 Person 的定义中增加 生日
和 上次登录时间
时间戳,我们可以这样写:
<?php
/**
* @SWG\Property(
* property="dateOfBirth",
* type="string",
* format="date"
* ),
* @SWG\Property(
* property="lastTimeOnline",
* type="string",
* format="dateTime"
* )
*/
当我们定义一个数字类型的属性时,我们可以规定它是一个整型、长型、浮点型或者双浮点型。
名称 | 类型 | 格式 |
---|---|---|
integer | integer | int32 |
long | integer | int64 |
float | number | float |
double | number | double |
和字符串一样,我们也可以定义数字属性的范围,比如:
属性 | 类型 | 描述 |
---|---|---|
minimum | number | 最小值 |
maximum | number | 最大值 |
exclusiveMinimum | boolean | 数值必须 > 最小值 |
exclusiveMaximum | boolean | 数值必须 < 最大值 |
multipleOf | number | 数值必须是 multipleOf 的整数倍 |
如果我们规定 pageSize 必须是整数,必须 > 0 且 <=100,还必须是 10 的整数倍,可以这样写:
<?php
/**
* @SWG\Parameter(
* name="pageSize",
* in="query"
* description="Number of persons returned",
* type="integer",
* format="int32",
* minimum: 0,
* exclusiveMinimum: true,
* maximum: 100,
* exclusiveMaximum: false,
* multipleOf: 10
* )
*/
我们还可以定义枚举类型,比如定义 Error 时,我们可以这样写:
<?php
/**
* @SWG\Property(
* property="code",
* type="string",
* enum={"DBERR", "NTERR", "UNERR"}
* )
*/
code 的值只能从三个枚举值中选择。
数字的大小和唯一性通过下面这些属性来定义:
属性 | 类型 | 描述 |
---|---|---|
minItems | number | 数值中的最小元素个数 |
maxItem | number | 数值中的最大元素个数 |
uniqueItems | boolean | 标示数组中的元素是否唯一 |
比如我们定义一个用户数组 Persons,希望返回的用户信息条数介于 10~100 之间,而且不能有重复的用户信息,我们可以这样写:
<?php
/**
* @SWG\Property(
* property="Persons",
* @SWG\Items(
* type="array",
* minItems="10",
* maxItems="100",
* uniqueItems="true",
* ref="#/definitions/Person"
* )
* )
*/
可以用 string 类型来表示二进制数据:
格式 | 属性包含 |
---|---|
byte | Base64 编码字符 |
binary | Base64 任意十进制的数据序列字符 |
比如我们需要在用户信息中增加一个头像属性(avatarBase64PNG)用 base64 编码的 PNG 图片来表示,可以这样写:
<?php
/**
* @SWG\Property(
* property="avatarBase64PNG",
* type="string",
* format="byte"
* )
*/
有时候我们读取资源信息的内容会比我们写入资源信息的内容(属性)更多,这很常见。是不是意味着我们必须专门为读取资源和写入资源分别定义不同的数据模型呢?幸运的是,OpenAPI 规范中提供了 readOnly 字段来帮我们解决整问题。比如:
<?php
/**
* @SWG\Property(
* property="lastTimeOnline",
* type="string",
* format="dateTime",
* readOnly="true"
* )
*/
上面这个例子中,上次在线时间
(lastTimeOnline )是 Person 的一个属性,我们获取用户信息时需要这个属性。但是很明显,在创建用户时,我们不能把这个属性 post 到服务器。于是我们可以把它标记为 readOnly。
一致性设计是在编写 API 文档时需要重点考虑的问题。比如我们在获取一组用户信息时,需要同时获取页面信息
(totalItems, totalPage, pageSize, currentPage)等,而且这些信息 必须 在根节点上。
怎么办呢?首先想到的做法就是:
<?php /** * @SWG\Definition( * definition="PagedPersonsV1", * @SWG\Property( * property="items", * type="array", * @SWG\Items(ref="#/definitions/Person") * ), * @SWG\Property( * property="totalItems", * type="integer" * ), * @SWG\Property( * property="totalPages", * type="integer" * ), * @SWG\Property( * property="pageSize", * type="integer" * ), * @SWG\Property( * property="currentPage", * type="integer" * ) * ) */
如果其他 API 操作也需要这些 页面信息
,那就意味着这些属性必须一遍又一遍的定义。不仅重复体力劳动,而且还很危险:比如忘记了其中的一两个属性,或者需要添加一个新的属性进来,那就是霰弹式的修改,想想都很悲壮。
稍微好一点的做法,就是根据前面学习的内容,把这几个属性抽取出来,建立一个 Paging 模型,“一处定义、处处使用”:
<?php /** * @SWG\Definition( * definition="PagedPersonsV2", * @SWG\Property( * property="items", * type="array", * @SWG\Items(ref="#/definitions/Person") * ), * @SWG\Property( * property="Paging", * @SWG\Items(ref="#/definitions/Paging") * ) * ), * @SWG\Definition( * definition="Paging", * @SWG\Property( * property="totalItems", * type="integer" * ), * @SWG\Property( * property="totalPages", * type="integer" * ), * @SWG\Property( * property="pageSize", * type="integer" * ), * @SWG\Property( * property="currentPage", * type="integer" * ) * ) */
但是,页面属性都不再位于 根节点!与我们前面设定的要求不一样了。怎么破?
JSON Schema v4 property 中定义的 allOf
,能帮我们解围:
<?php
/**
* @SWG\Definition(
* definition="PagedPersons",
* allOf={
@SWG\Schema(ref="#/definitions/Persons"),
* @SWG\Schema(ref="#/definitions/Paging"),
* }
* )
*/
上面这个例子表示,PagedPersons 根节点下,具有将 Persons 和 Paging 展开 后的全部属性。
allOf
同样可以使用行内的数据定义,比如
<?php /** * @SWG\Definition( * definition="PagedCollectingItems", * allOf={ @SWG\Property( * property="items", * type="array", * minItems="10", * maxItems="100", * uniqueItems="true", * @SWG\Items(ref="#/definitions/CollectingItem") * ), * @SWG\Schema(ref="#/definitions/Paging"), * } * ) */
这一章主要介绍如何定义高度精确化的参数和响应消息等。
我们已经知道使用关键字 required
来定义一个必带参数。
在一个参数中,required
是一个 boolean
型的可选值。它的默认值是 false 。
比如在某个操作中,username 是必填参数:
<?php
/**
* @SWG\Parameter(
* name="username",
* in="path",
* required="true"
* description="The person's username",
* type="string"
* )
*/
根据定义,required
是一个字符串列表,列表中包含各必带参数名。如果某个参数在这张列表中找不到,那就说明它不是必带参数。如果没有定义 required
,就说明所有参数都是可选。如果 required
定义在一个 HTTP 请求上,这说明所有的请求参数都是必填。
在 POST 、persons 中有 Person 的定义,在这里 username 这个属性是必带的,我们可以指定它为 required
,其他非必带字段则不指定:
<?php /** * @SWG\Items( * required={"username"}, * @SWG\Property( * property="firstName", * type="string" * ), * @SWG\Property( * property="lastName", * type="string" * ), * @SWG\Property( * property="username", * type="string" * ) * ) */
通过关键字 default
,我们可以定义一个参数的默认值。当这个参数不可得(请求未带或者服务器未返回)时,这个参数就取默认值。因此设定了某个参数的默认值后,它是否 required
就没意义了。
我们定义参数 pageSize 的默认值为 20 ,那么如果请求时没有填写 pageSize ,服务器也会默认返回 20 个元素。
<?php /** * @SWG\Parameter( * name="pageSize", * in="query" * description="Number of persons returned", * type="integer", * format="int32", * minimum="0", * exclusiveMinimum="true", * maximum="100", * exclusiveMaximum="false", * multipleOf="10", * default="20" * ) */
同参数,使用关键字 default
即可。
在 GET /persons 时,如果我们想添加一个参数来过滤 “是否通过实名认证” 的用户,应该怎么做呢?首先想到的是这样:GET /persons?page=2&includeVerifiedUsers=true ,问题是 includeVerifiedUsers 语义已经如此清晰,而让 “=true” 显得很多余。我们能不能直接用:GET /persons?page=2&includeVerifiedUsers 呢?
要做到这种写法,我们需要一个关键字 allowEmptyValue
。我们定义 includeVerifiedUsers 时允许它为空。那么如果我们请求 GET /persons?page=2&includeVerifiedUsers 则表示需要过滤 “实名认证” 用户,如果我们直接请求 GET /persons?page=2 则表示不过滤:
<?php
/**
* @SWG\Parameter(
* name="includeNonVerifiedUsers",
* in="query"
* type="boolean",
* default="false",
* allowEmptyValue="true"
* )
*/
设计 API 的时候,我们经常会遇到在 GET 请求中需要携带一组请求参数的情况。如何在 API 文档章呈现呢?很简单,我们只需要设定 参数类型(type)
为 array
,并选择合适的 组合格式(collectionFormat)
就行了。
组合格式 | 描述 |
---|---|
csv (default value) | Comma separated values(逗号分隔) foo,bar |
ssv | Space separated values(空格分隔) foo bar |
tsv | Tab separated values(反斜杠分隔) foo\bar |
pipes | Pipes separated values(竖线分隔) foo|bar |
multi | 单属性可以取多个值,比如 foo=bar&foo=baz. 只适用于查询参数和表单参数。 |
比如我们想根据多种参数(username , firstname , lastname , lastTimeOnline )等来对 Person 进行带排序的查询。我们需要一个这样的 API 请求: GET /persons?sort=-lastTimeOnline|+firtname|+lastname 。用于排序的参数是 sort ,+ 表示升序,- 表示降序。
相应的 API 文档,可以这样写:
<?php /** * @SWG\Parameter( * name="sort", * in="query" * type="array", * uniqueItems=true, * minItems=1, * maxItems=3, * collectionFormat="pipes", * @SWG\items( * type="string", * pattern="[-+](username|lastTimeOnline|firstName|lastName)", * ) * ) */
现在我们就能搞定 GET /persons?sort=-lastTimeOnline|+firtname|+lastname 这种请求了。当然,我们还可以指定排序的默认值,锦上添花。
<?php /** * @SWG\Parameter( * name="sort", * in="query" * type="array", * uniqueItems=true, * minItems=1, * maxItems=3, * collectionFormat="pipes", * @SWG\items( * type="string", * pattern="[-+](username|lastTimeOnline|firstName|lastName)", * ), * default={"-lastTimeOnline", "+username"} * ) */
参数,按照位置来分,不仅仅包含路径参数、请求参数和消息体参数,还包括消息头参数和表单参数等。比如我们可以在 HTTP 请求的消息头上加一个 User-Agent (用于跟踪、调试或者其他),可以这样定义它:
<?php
/**
* @SWG\Parameter(
* parameter="userAgent",
* name="User-Agent",
* type="string",
* in="header",
* required=true
* )
*/
然后像使用其他参数一样使用它:
<?php
/**
* @SWG\Parameter(ref="#/parameters/userAgent")
*/
有些 js-less-browser 的老浏览器不支持 POST JSON 数据,比如在创建用户时,只能够以这样个格式请求:
POST /js-less-persons
username=apihandyman&firstname=API&lastname=Handyman
没有问题,丝袜哥可以搞定。我们只需要把各个属性的 in
关键字定义为 formData
,然后设置 consumes
的媒体类型为 application/x-www-form-urlencoded
即可。
<?php /** * @SWG\Post( * path="/persons", * summary="Creates a person", * description="For JS-less partners", * consumes={"application/x-www-form-urlencoded"}, * produces={"text/html"}, * @SWG\parameter( * name="username", * in="formData", * required="true", * pattern="[a-z0-9]{8,64}", * minLength=8, * maxLength=64, * type="string" * * ) * ) */
当我们要处理一个请求,输入类型是 文件 时,我们需要:
使用 multipart/form-data 媒体类型;
设置参数的 in
关键字为 formData;
设置参数的 类型(type)
为 file。
比如:
<?php /** * @SWG\Post( * path="/images", * summary="Uploads an image", * consumes={"multipart/form-data"}, * @SWG\Parameter( * name="image", * in="formData", * type="file" * ) * @SWG\Response( * response="200", * description="Image's ID", * @SWG\Schema( * @SWG\Property( * property="imageId", * type="string" * ) * ) * ) * ) */
有时候我们想限定输入文件的类型(后缀),很不幸的是,根据现在 V2.0 的规范暂时还做不到☹
一个 API 可以消费各种不同的媒体类型,比如说最常见的是 application/json 类型的数据,当然这不是 API 唯一支持的类型。我们可以在 文档的根节点 或者 一个操作的根节点 下添加关键字 consumes
,来定义这个操作能够消费的媒体类型。
比如我们的 API 全部都接受 JSON 和 YAML 的数据,那我们可以在文档的根节点下添加:
<?php
/**
* @SWG\Swagger(
* consumes={"application/json", "application/x-yaml"}
* )
*/
如果某个操作(比如上传图片的操作)很特殊,它可以通过自己添加 consumes 来覆盖全局设置:
<?php /** * @SWG\Post( * path="/images", * summary="Uploads an image", * consumes={"multipart/form-data"}, * @SWG\Parameter( * name="image", * in="formData", * type="file" * ) * @SWG\Response( * response="200", * description="Image's ID", * @SWG\Schema( * @SWG\Property( * name="imageId", * type="string" * ) * ) * ) * ) */
不带消息体的响应很常见,比如 HTTP 204 状态响应本身就表示服务器返回不带任何消息内容的成功消息。
要定义一个不带消息体的响应很简单,我们只需要写响应状态和描述就行了:
<?php
/**
* @SWG\Response(
* response="204",
* description="Person succesfully created."
* )
*/
与请求消息中类似,我们使用 required
参数来表示,比如请求一个用户信息时, 服务器必须返回 username。
API 的返回结果不仅仅体现下 HTTP 状态和响应消息体,还可以在响应消息头上做文章。比如我们可以限定一个 API 的使用次数和使用时间段,在响应消息头中,增加一个属性 X-Rate-Limit-Remaining 来表示 API 可调用的剩余次数,增加另一个属性 X-Rate-Limit-Reset 来表示 API 的有效截止时间。
<?php /** * @SWG\Response( * response="204", * description="Person succesfully created." * @SWG\Header( * header="X-Rate-Limit-Remaining", * type="integer", * format="int32" * ), * @SWG\Header( * header="X-Rate-Limit-Reset", * type="string", * format="date-time" * ) * ) */
我们在定义响应消息时,通常会列举不同的 HTTP 状态结果。如果有些状态不在我们 API 文档的定义范围(比如服务器需要返回 993 的状态),该怎么处理呢?这时需要通过关键字 default
来定义一个默认响应消息,用于各种 定义之外 的状态响应,比如:
<?php
/**
* @SWG\Response(
* response="default",
* description="default response"
* )
*/
目前这个配置也不支持 “一次定义,处处使用” 。☹
与请求消息一样,我们也可以定义响应消息所支持的媒体类型,不同的是我们要用到关键字 produces
(与请求消息中的 consumes
相对,由此可见,API 文档描述的主体是服务提供者)。
比如,我们可以在文档的根路径下全局设置:
<?php
/**
* @SWG\Swagger(
* produces={"application/json", "application/x-yaml"}
* )
*/
也可以在某个操作的根路径下覆盖设置。
如前章节 4.2.1 中已经提到的,定义一个对象,其中某个属性我们只希望在响应消息中携带,而不希望在请求消息中携带,应该用 readOnly
关键字来表示
通过关键字 tags
我们可以对文档中接口进行归类,tags
的本质是一个字符串列表。tags
定义在文档的根路径下。
比如说 GET /persons 属于用户(Person) 这个分类的,那么我们可以给它贴个标签:
<?php
/**
* @SWG\Get(
* path="/persons",
* summary="Gets some persons",
* description="Returns a list containing all persons. The list supports paging.",
* operationId="searchUsers",
* @SWG\Parameter(ref="#/parameters/userAgent"),
* tags={"Persons"}
* )
*/
一个操作也可以同时贴几个标签,比如:
<?php
/**
* @SWG\Get(
* path="/js-less-consumer-persons",
* summary="Creates a person",
* description="For JS-less partners",
* operationId="createUserJS",
* @SWG\Parameter(ref="#/parameters/userAgent"),
* deprecated=true
* tags={"JSLess", "Persons"}
* )
*/
贴上标签后,在 Swagger Editor 和 Swagger UI 中能够自动归类,我们可以按照标签来筛选接口,试试吧?
description
这个属性几乎是无处不在,为了提高文档的可读性,我们应该在必要的地方都加上描述文字。
每一种模式(Schema),都会有一个标题(title)和一段描述,比如:
<?php
/**
* @SWG\Definition(
* definition="Person",
* title="Human",
* description="A person which can be the user itself or one of his friend"
* )
*/
比如:
<?php
/**
* @SWG\Property(
* property="firstName",
* description="first name",
* type="string"
* )
*/
比如:
/**
* @SWG\Parameter(
* parameter="username",
* name="username",
* type="string",
* in="path",
* required=true,
* description="The person's username"
* )
*/
一个操作(Operation)通常都会包含概述和描述信息。而且我们还可以添加一个关键字 operationId
,这个关键字通常用来指示服务提供者对这个操作的 处理函数 的函数名。比如:
<?php
/**
* @SWG\Get(
* path="/persons",
* summary="Gets some persons",
* description="Returns a list containing all persons. The list supports paging.",
* operationId="searchUsers",
* @SWG\Parameter(ref="#/parameters/userAgent"),
* tags={"Persons"}
* )
*/
<?php
/**
* @SWG\Response(
* response=200,
* description="A list of Person"
* )
*/
我们在 API 文档的根路径下添加了 tags
的定义,对于其中的每一个标签,我们都可以添加描述信息:
<?php
/**
* @SWG\Tag(
* names="Persons",
* description="Everything you need to handle users and friends"
* )
*/
在绝大部份的 description 中,我们可以使用 GFM (Github Flavored Markdown)语法。
使用符号 |
然后在新行中打一个 tab(注意:YAML 的 tab 是两个空格 ),就可以编辑多行描述,比如:
<?php
/**
* @SWG\Tag(
* names="Persons",
* description="|
* Returns a list containing all items this person is looking for.
The list supports paging.
* "
* )
*/
我们已经知道了用 Schema 来描述参数和属性,有的时候,用示例数据更有表现了。我们可以使用关键字 example
来给原子属性或者对象添加示例数据。
<?php
/**
* @SWG\Property(
* property="firstName",
* description="first name",
* type="string",
* example="John"
* )
*/
如果我们在各个级别(比如参数、对象、定义、响应消息)都添加了示例数据。支持 OpenAPI 规范的各解析工具 都是 以 最高级别 的定义为准。
我们可以通过关键字 deprecated
置为 true 来标记接口的 弃用 状态,比如:
<?php
/**
* @SWG\Get(
* path="/js-less-consumer-persons",
* summary="Creates a person",
* description="For JS-less partners",
* operationId="createUserJS",
* @SWG\Parameter(ref="#/parameters/userAgent"),
* deprecated=true
* tags={"JSLess", "Persons"}
* )
*/
一般来说,项目中不光只有一篇 API 文档,还应该有些描述 application key,测试用例,操作链以及其他内容的文档,这些文档一般是单独成篇的。如果在描述某个接口时,我们想链接这些文档,可以通过关键字 externalDoc
来添加,例如:
<?php
/**
* @SWG\ExternalDocumentation(
* description="Complete documentation describing how to use this API",
* url="http://doc.simple.api/"
* )
*/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。