OpenAPI/Swagger 编写与导入指南
OpenAPI / Swagger 相较于其他规范完善程度更高,生态更加完整,是目前 CODING 推荐的 API 设计语言。
什么是 OpenAPI / Swagger
OpenAPI 规范(OAS)是一种通用的、和编程语言无关的 API 描述规范,使人类和计算机都可以发现和理解服务的功能,而无需访问源代码、文档或针对接口进行嗅探。正确定义后,使用者可以使用最少的实现逻辑来理解远程服务并与之交互。
OpenAPI 始于 Swagger 规范,Swagger 规范已于 2015 年捐赠给 Linux 基金会后改名为 OpenAPI,并定义最新的规范为 OpenAPI 3.0。
CODING 目前支持以下版本导入:
- OpenAPI v3(OAS 3)
- Swagger v2
最佳实践
通常,设计 API 规范有两个方向,Design-First(设计优先) 或 Code-First(编码优先)
Design-First(设计优先):
即优先设计 API 规范,设计完成后再着手进行代码开发工作。
采用 Design-First 就意味着,将设计 API 路由、参数等工作提前,后续整个软件开发的流程都需要围绕着 API 规范为核心,当然这需要有一定的设计经验的开发人员才能胜任。
Design-First 有很多好处:
-提高开发效率。开发团队将根据 API 规范进行并行开发和对接工作,而无需等待接口逻辑开发完毕。
-降低接口开发的成本,无需修改代码逻辑即可轻松地修改 API 规范,因为 API 描述语言(如:OpenAPI)与编码语言无关
-开发人员更加专注于设计 API 规范,对比 Code-First 可以描写更多 API 的细节,如:校验规则、范例数据等,同时开发人员对 API 的全局一致性、扩展性、规范性也有更好的把控。
-在联调开发的过程中可以提前发现和解决问题,避免问题在开发完毕后修改的成本过高。
-由于 API 描述更加标准化,可以方便做更多的 API 生态延伸,比如基于 API 规范生成 Mock API Server,开发 API 自动化测试代码,接入 API 网关等。点击查看更多参考
Code-First(编码优先):
即通过代码中关于 API 描述特性、注解或注释自动生成 API 描述文件的设计方式,如:JAVA 生态的 SpringFox。
适合倾向于在代码上编写 API 规范,通过自动化设施自动生成文档的团队。
Code-First 的优点:
-节省时间。对于 API 开发者,编码的同时可以获得一份满足基本要求的 API 文档。
-方便接入自动化 CI/CD 流程中。虽然 Code-First 省去了开发者设计 API 描述文件的阶段,提高了 API 开发者的效率,但是从整个团队的角度来看,效率并不一定提升了,反而有可能降低了效率。
不同 API 开发者的经验和习惯的不同,极有可能在编码的过程中对 API 的限制条件考虑不全,又或者框架生成 API 文档的程序完善度不够,种种因素导致最终生成的 API 的描述无法达到理想标准。
而很多 API 开发者习惯开发完成后才推送代码,并生成 API 文档,也导致了团队的进程阻塞于此,拖后了整个团队的开发进程。另一方面,API 在开发完成如果没有测试,很有可能导致 API 对接者在对接的过程中遇到重重阻碍。
CODING 也希望各位开发者重视 API 设计的重要性,如果您喜欢 Code-First 设计方向,我们的建议是:
-选用完善程度比较高的生成组件
-对 API 的描述尽可能的细致、完整
-优先设计路由、请求、响应等规则,详细的逻辑代码在 API 设计完成后再着手开发。
Design-First 和 Code-First 针对不同的场景有着各自的优势,不同团队对两者考虑的方向也不同,但是对 API 描述的完善不管是哪个方向都是最重要的。
下面我们将针对 Design-First 和 Code-First 两个方向的最佳实践作展开。
Design-First
OpenAPI 进化到 v3 版本后,虽然规范非常完善,但对于首次上手的同学来说还是非常复杂,那么带有界面的编辑工具会成为最佳选择。
这里我们推荐使用 OpenAPI-GUI v3 来设计 API 描述文件,如下图所示。
OpenAPI-GUI v3 基本使用
OpenAPI-GUI v3 支持设计大多数常见情况的 API 规则,并且支持本地部署,非常适合新手,下面我们简单介绍一下 OpenAPI-GUI v3 的基本使用方式。
OpenAPI-GUI v3 的导航结构如上图所示。
首先,我们切换到 Header
,给 API 文档定义标题、描述、作者等基础信息。
接着,我们切换至 Servers
配置 API 各环境基础路径地址。
如果您的 API 需要经过鉴权(OAuth、APIKey 等),您可以切换到 Security
配置响应的鉴权信息,如下图所示。
下面将开始设计 API 分组,点击切换到 Tags
,对应的之后设计的接口可与分组相关联,并在文档中展示时,合并到对应分组栏目中。
Main
为最主要的 API 设计界面,需要了解的是,CODING API 文档以 tag 分组,而该界面以路由+请求方法(Method)分组。
Description TAB 用于填写接口的标题、描述和关联 tag 信息,建议全部填写,若未填 Summary,API 文档将显示 API ID(OperationID),未关联 tag 将归属于默认分组。
Parameter 用于定义请求 Query、Header、Cookie 和路由参数规则,Location 可指定参数位置。
而在非 GET 请求方式(Method)方式下,如 POST / PATCH / PUT 等,Request Body 则用于定义请求 Body 的规则,如 Form 表单参数、纯 JSON 方式提交参数定义等。
对应的 Request Body 必须定义 Media-Type,可定义多个 Media-Type,API 服务器将根据该参数识别 Body 内容结构,如上图所示 application/json
即为纯 JSON 方式提交表单参数。
您可以复用实体,下面会讲到,或者直接编辑请求结构,点击后弹出如下图所示,可直接编辑。
需要注意的是,该弹窗弹出位置可能会在显示页面下方,需要滚动到下方才可看到。
切换到 Response TAB,我们可以定义接口响应返回的结构。
在这个界面可以增加多条响应结构,如 200、400、401 多个状态码有多重不同响应返回结构,或者同状态码下不同 Media-Type 不同返回结构。
点击编辑结构和 Request Body 类似,可定义响应数据结构规则。
切换到 Security TAB,可以配置接口的鉴权配置,由于编辑器未完善,您可在设计完成后通过导出 OpenAPI 描述文件,以源文件的形式修改 scope 范围(可参考下方进阶教程)。
由于 Docs / Links / Callbacks 在日常使用时不常用到,CODING API 文档暂不作展示。
在设计完毕后,您可点击右上角 Save 按钮保存修改,并点击 Export JSON
或者 Export YAML
复制或者下载 OpenAPI 描述文件。
接着,在 CODING API 文档导入并发布。
等待发布完毕后,点击详情页的文档地址,查看最终效果。
已有 OpenAPI 描述文件
如果您已有 OpenAPI 描述文件,或者您想先看一下效果,那么您可以切换到 Upload
TAB,将描述文件填入编辑框,或者点击 Demo
加载范例文件,并点击 Load Definition
加载描述文件。
进阶
虽然使用 OpenAPI-GUI v3 设计非常方便,但是对 OpenAPI 支持的并不完整,比如 Request & Response Example 无法在线编辑,这种场景下,我们就需要针对 OpenAPI 描述源文件进行编辑。
这里我们推荐使用 Swagger Editor 进行编辑,如下图所示:
Swagger Editor 是所见即所得的,并且如果写的语法有问题,会在对应段落显示错误提示,并说明错误原因,如下图所示:
相对于纯手写 OpenAPI 描述文件,Swagger Editor 有个贴心的功能,您可以很方便的通过可视化界面插入描述(如:path、operation 等)至编辑框中,如下图所示,
为了方便理解 OpenAPI v3 规范,我们在下方给出了 OpenAPI v3 的一般结构范例,对应的右边给出了字段的备注信息,方便对照:
该范例 CODING API 文档均支持展示
openapi: 3.0.0 # 代表使用 OpenAPI v3 规范
info: # 这里写 API 文档基础信息
title: 宠物商店 API 文档 # API 文档标题
version: '1.0.0' # API 文档版本
description: 这是一篇关于宠物商店的 DEMO API 文档,仅做参考。 # API 文档描述
contact: # 联系信息
name: CODING
url: 'https://coding.net'
email: support@coding.net
servers: # 这里写 API 服务器地址,多条可代表不同环境
- url: 'https://petstore.com/api/v1'
description: 生产环境
- url: 'http://test.petstore.com/api/v1'
description: 测试环境
tags: # 标签,CODING 中可作为分组依据,name 作为分组名称
- name: 宠物
description: 所有关于宠物的内容
- name: 会话
description: 关于用户的注册、登录和登出
paths: # 这里写具体的 API 的相关信息(API 名称、请求类型、摘要、请求参数、响应等)
/pets/{petId}: # 第一层,API 路由
get: # 第二层,请求方法(Method),如: get, post, delete 等
tags: # 标签,与上方全局的标签对应,将该接口归于对应 tag 的组中
- 宠物
summary: 宠物详情 # 接口摘要,CODING 中可作为接口标题
operationId: get-pet # 接口 ID(必填),接口的唯一识别方式
description: 获取宠物信息 # 接口描述信息
parameters: # 接口参数,可多条,支持路由、Query、Header 参数
- schema: # 参数结构
type: string # 参数类型,integer / string / array 等
name: petId # 参数 key
in: path # 路由(path)类型的参数
required: true # 是否必填
description: 宠物 ID # 参数描述
- schema:
type: string
enum: # 参数枚举
- application/json
- application/xml
default: application/json
in: header # header 类型的参数
name: Accept
description: 返回数据媒体类型
- schema:
type: integer
default: 1 # 参数默认值
in: query # Query 类型的参数
name: page
description: 页码
security: # 接口授权信息,此处与全局 components/securitySchemes 对应
- petstore_auth: # 此处与 components/securitySchemes/petstore_auth 对应
- 'read:pets' # 此处代表接口 OAuth Scope 信息
responses: # 接口响应信息
'200': # 响应状态码
description: 以数组形式返回宠物信息 # 响应描述信息
headers:
X-PAGE:
schema:
type: integer
description: 当前页码
content: # 响应内容规范描述
application/json: # 响应内容 Media Type
schema: # 响应内容结构
type: array
description: 宠物信息
items:
$ref: '#/components/schemas/Pet' # $ref 可用来引用 components/schemas 中的各种实体声明,减少相同数据结构冗余,增加复用性
examples: # 响应范例,建议填写,可用于 CODING API 文档范例显示以及 Mock API 模拟数据
请求成功范例: # 范例名称
value: # 范例,展示时将转换成 JSON 形式
- id: 1
name: doggie
'400':
description: 请求失败
patch: # patch 请求方法
tags:
- 宠物
summary: 单独更新宠物属性
operationId: patch-pet
description: 单独更新宠物属性
deprecated: true # 接口是否已废弃
requestBody: # post / put / patch 等请求需要传输 body 时,请指定 requestBody,Path / Query / Header 请写在 parameters 中
content:
application/x-www-form-urlencoded: # 媒体类型 Media Type
schema: # 参数结构与上述 parameters 中相同
type: object
properties:
categoryId:
type: integer
description: 分类 ID
status:
type: string
description: 状态
enum:
- available
- pending
- sold
photoUrls:
type: array
description: 照片地址列表
items:
type: string
description: 照片地址
name:
type: string
description: 宠物名称
isHealthy:
type: boolean
description: 是否健康
tagIds:
type: string
description: 标签 ID 数组
examples: # 请求 Body 范例,将在 CODING API 文档右侧范例区展示
更新范例: # 范例名称
value: # 范例内容,以下将以 JSON 形式展示
status: sold
responses:
'200':
description: 更新成功
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
components: # 通用组件,可定义复用的实体、响应、请求以及授权信息等
schemas: # 实体信息
Pet: # 实体 key
title: Pet # 名称
type: object # 实体数据结构类型
description: 宠物实体信息 # 描述信息
properties: # 实体参数,可多条
id:
type: integer
format: int64
description: ID
name:
type: string
example: doggie
description: 名称
securitySchemes: # API 所使用的授权方式,可多条,CODING 会展示所有授权方式
petstore_auth: # 授权方式 Key
type: oauth2 # 授权类型,可为:apiKey、http、oauth2、openIdConnect
flows: # OAuth2 所需要的流程信息
authorizationCode: # OAuth2 授权类型,可为:implicit、password、clientCredentials、authorizationCode
authorizationUrl: 'https://petstore.com/oauth/authorize' # 跳转授权地址
tokenUrl: 'https://petstore.com/oauth/token?grant_type=authorization_code' # 获取 token 地址
scopes: # 所有 scope 列表
'write:pets': 修改宠物信息权限
'read:pets': 查看宠物信息权限
refreshUrl: 'https://petstore.com/oauth/token?grant_type=refresh_token' # 刷新 token 地址
description: OAuth2 授权模式 # 描述信息
更详细的规范可参考:
Code-First
通过自动生成 OpenAPI 描述文件,我们可以很简单的接入 CODING API 文档的自动化流程,方便开发同学提交代码自动发布 API 文档。
此处我们以 Java 语言的 Springfox 为例
其他语言可参考以下方案:
- PHP:Swagger PHP
- Go:Go Swagger
- Python Flask:Flask RestPlus
- NodeJS Express:Swagger Node Express
- 点击查看
首先我们新建一个 SpringBoot 项目,添加 springfox 依赖:
dependencies {
compile ('io.springfox:springfox-swagger2:2.9.2') {
exclude(module: 'io.swagger:swagger-annotations')
exclude(module: 'io.swagger:swagger-models')
}
compile("io.swagger:swagger-annotations:1.6.0")
compile("io.swagger:swagger-models:1.6.0")
}
上面 swagger-annotations 指定新版本操作,由于在非 String 的请求参数(如 Integer) @RequestParam 中指定 defaultValue 时,springfox 2.9.2 依赖的 swagger 包较旧,导致编译时报类型转换错误的问题,在新版已解决。
然后我们在 Application 中针对 Springfox 进行初始化配置操作,并定义 Swagger API 文档的全局描述信息:
@Bean
public Docket docket(ServletContext servletContext) {
ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2) // 定义 Swagger 2.0 规范
.apiInfo(apiInfo()) // 定义全局描述信息
.tags(new Tag("宠物", "所有关于宠物的内容"), // 定义 tag,对应 CODING API 文档的菜单 API 分组
new Tag("会话", "关于用户的注册、登录和登出"))
.host("petstore.com") // 定义 API 基础域名
.pathProvider(new RelativePathProvider(servletContext) { // 定义 API 基础路径前缀,与 host 配合组成 petstore.com/api/v1
@Override
public String getApplicationBasePath() {
return "/api/v1";
}
})
.select();
builder.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class)); // 指定只扫描 @RestController 注解的 Controller 的 API
return builder.build()
.ignoredParameterTypes(RequestAttribute.class) // 排除扫描 @RequestAttribute 注解
.ignoredParameterTypes(Errors.class); // 排除扫描 @Errors 注解
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("宠物商店 API 文档") // 定义文档标题
.description("这是一篇关于宠物商店的 DEMO API 文档,仅做参考。") // 定义文档描述
.termsOfServiceUrl("http://swagger.io/terms/") // 定义服务条款
.contact(new Contact("CODING", "https://coding.net", "support@coding.net")) // 定义联系信息
.license("Apache 2.0") // 定义 License
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html") // 定义 License URL
.version("1.0.0") // 定义 API 版本
.build();
}
接下来,我们可以开始设计 API 规则,以 PetController 为例,即 /pets
路由:
@Api(tags = {"宠物"})
@RestController
@RequestMapping("/pets")
public class PetController {
@ApiOperation(value = "宠物列表", notes = "列出所有宠物信息")
@GetMapping("")
public ResponseEntity> all(@ApiParam(value="限制每页返回数量(最大限制:100)") @RequestParam(required = false, defaultValue = "20") Integer pagesize,
@ApiParam(value="页码") @RequestParam(required = false, defaultValue = "1") Integer page) {
return ResponseEntity.ok(new ArrayList<>());
}
@ApiOperation(value = "宠物详情", notes = "获取指定 ID 的宠物详情")
@GetMapping("{petId}")
public ResponseEntity detail(@ApiParam(value="宠物 ID", required = true) @PathVariable Integer petId,
@ApiParam(value = "媒体类型", required = true, allowableValues = "application/json,application/xml", defaultValue = "application/json") @RequestHeader String Accept) {
return ResponseEntity.ok(null);
}
}
这里我们定义了两个路由,分别为:
GET /pets 宠物列表
Query 请求参数
-pagesize int 选填
限制每页返回数量(最大限制:100)
默认值 20
-page int 选填
页码
默认值 1
响应结构
-Pet 实体数组 array
GET /pets/{petId} 宠物详情
Header 参数
-Accept string
可选值
application/json
application/xml
,默认值application/json
。媒体类型
路由参数
-petId int
宠物 ID
响应结构
-Pet 实体 object
至此,我们完成了上述两个 API 的设计,无需提前实现代码逻辑,我们即可获得 Swagger 2.0 的描述文件,步骤如下:
该范例使用 Jetty 作为 Server
启动应用
./gradlew bootRun
打开浏览器访问
其中 {port} 为配置的服务 WEB 端口
返回的内容即为 Swagger 2.0 描述文件,如下图所示:
右键选择「存储为」保存至本地,可命名为 xxx.json
,将该 Swagger 2.0 描述文件导入至 CODING 即可生成 API 文档,如下图所示:
生成的 API 文档效果如下图所示:
点击查看该范例的源代码。
更多 Springfox 的用法可参考官方文档
来源:https://help.coding.net/docs/management/api/import/openapi.html
评论