导读:相信无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新。其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是随着时间推移,版本迭代,接口文档往往很容易就跟不上代码了,更会出现之前的同学没有把接口文档交接清楚就离职,留下一个繁重复杂的项目,重新啃起来异常艰难,不亚于自己从头写一遍。因此仅仅只通过强制来规范大家是不够的。我们研究了 Swagger 到 Yapi 的打通方法。有了它之后,我们可以做到每次写完代码,只需要顺便修改注释,然后提交,Yapi 上就能自动更改我们的接口文档。
全文 8199 字,预计阅读时间 21 分钟。
一、Swagger 简介
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格 Web 服务。你只需要按照它的规范去定义接口及接口相关的信息。再通过 Swagger 衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,生成多种语言的客户端和服务端的代码,以及在线接口调试页面等等。
这样,如果按照新的开发模式,在开发新版本或者迭代版本的时候,只需要更新 Swagger 描述文件,就可以自动生成接口文档和客户端服务端代码,做到调用端代码、服务端代码以及接口文档的一致性。
二、Swagger 搭建
以下步骤建立在已经有能够成功运行 go 的环境上。
在 go-swagger 官方教程上能看到最全的教程,有能力的可以直接走官方教程。
2.1►安装
上面的所有下载方式都可行,但是为了兼容我们的所有情况,所以选择这种,直接下载代码。
把命令精简一下,其实就干了两件事:
1、clone go-swagger 的代码;
2、把 swaager 加进 GOROOT。
mkdir DownLoad
cd DownLoad
git clone https://github.com/go-swagger/go-swagger
cd DownLoad/go-swagger-master/cmd/swagger/
go install .
复制代码
验证一下是否成功:
[work@hangchuang /]$ swagger -h
Usage:
swagger [OPTIONS] <command>
Swagger tries to support you as best as possible when building APIs.
It aims to represent the contract of your API with a language agnostic description of your application in json or yaml.
Application Options:
-q, --quiet silence logs
--log-output=LOG-FILE redirect logs to file
Help Options:
-h, --help Show this help message
Available commands:
diff diff swagger documents
expand expand $ref fields in a swagger spec
flatten flattens a swagger document
generate generate go code
init initialize a spec document
mixin merge swagger documents
serve serve spec and docs
validate validate the swagger document
version print the version
复制代码
2.2►搭建
三、Swagger 规范
Swagger 注释的规范以及用法如下。
3.1►swagger:meta
简介:swagger:meta 是你的所有的 API 的概要,我们用它来形成我们的 API 文档的开头介绍。
// Go-Swagger API.(title)
//
// 这是我们的测试API (description)
//
// Terms Of Service:
// there are no TOS at this moment, use at your own risk we take no responsibility
//
// Schemes: http, https
// Host: localhost
// BasePath: /go-swagger/test
// Version: 0.0.1
// License: MIT http://opensource.org/licenses/MIT
// Contact: Zhubangzheng<zhubangzheng@baidu.com> zhubangzheng@baidu.com
//
// Consumes:
// - application/json
// - application/xml
//
// Produces:
// - application/json
// - application/xml
//
// Security:
// - api_key:
//
// SecurityDefinitions:
// api_key:
// type: apiKey
// name: KEY
// in: header
// oauth2:
// type: oauth2
// authorizationUrl: /oauth2/auth
// tokenUrl: /oauth2/token
// in: header
// scopes:
// bar: foo
// flow: accessCode
//
// Extensions:
// x-meta-value: value
// x-meta-array:
// - value1
// - value2
// x-meta-array-obj:
// - name: obj
// value: field
//
// swagger:meta
package test
复制代码
注意:注释的结尾 swagger:meta 和 package 之间不能有空行,否则无法被 swagger 识别。
注解用法:
3.2►swagger:route
swagger:route 是最主要的一个注释参数,是你的单个 API 接口的详细信息。
格式:swagger:route [method] [path pattern] [tag1 tag2 tag3] [operation id]
[method]和 [path pattern]必选,后面的[tag]根据你自己决定,首先是你当前接口的 tag,然后再考虑加上其他。最后的[operation id]是你的方法的唯一标识,如果仅是作为一个接口文档可以不填,但是它在很多地方都被用作方法名。例如用于客户端生成的方法。
// ServeAPI serves the API for this record store
func ServeAPI(host, basePath string, schemes []string) error {
// swagger:route GET /{id}/checkout SwaggerTest swagger_test_checkout
//
// Swagger测试接口
//
// 用于Swagger测试
//
// Consumes:
// - application/json
// - application/x-protobuf
//
// Produces:
// - application/json
// - application/x-protobuf
//
// Schemes: http, https, ws, wss
//
// Deprecated: true
//
// Security:
// api_key:
// oauth: read, write
//
// Responses:
// default: genericError
// 200: someResponse
// 422: validationError
mountItem("GET", basePath+"/{id}/checkout", nil)
}
复制代码
Response 和后面定义的 swagger:response 对应。
3.3►swagger:patameters
swagger:parameters 是接口的参数注释
格式:swagger:parameters [operationid1 operationid2],parameters 通过[operation id]和 route 绑定。
参数的玩法很多,但是基本都用不上详细可以在官方文档查看 swagger:parameters。
因为我们主要使用 GET、POST,而官网只介绍了 GET 方法,内网外网对于 swagger:parameters 注解的 POST 的用法介绍甚少,因此在这里介绍 GET 和 POST 的主要玩法即可。
// swagger:parameters swagger_test_checkout
type SwaggerTest struct {
// SwaggerTest接口测试参数1 (description)
// required: true(是否必须)
// in: query(参数所在的位置)
ID uinat64 `json:"id"`
}
复制代码
重点:GET 方法的 in 注释 可接 query、header、cookie、path ,不同情况自定。
以上就是我们声明一个 GET 的参数的必要注释,其他都是非必要,如下图,可以根据自己的具体情况添加。
// swagger:parameters swagger_test_checkout
type SwaggerTest struct {
// SwaggerTest接口测试参数1 (description)
// required: true(是否必须)
// in: formData(参数所在的位置)
ID uinat64 `json:"id"`
}
复制代码
POST 方法,参数的位置不在 body,换句话说不能 in:body,而是要使用 in:formData,这样的格式导入到 yapi 之后才会出现在 body 里,且在本地的 swagger ui 中也才会正确显示。还有一点需要注意就是建议把 swagger:route 处的 Consumes 设置成 multipart/form-data,即:
// ServeAPI serves the API for this record store
func ServeAPI(host, basePath string, schemes []string) error {
// swagger:route GET /{id}/checkout SwaggerTest swagger_test_checkout
//
// Swagger测试接口
//
// 用于Swagger测试
//
// Consumes:
// multipart/form-data
//
// ......
mountItem("GET", basePath+"/{id}/checkout", nil)
}
复制代码
格式:swagger:response [response name] ,response 通过[response name]和 route 处定义的 response 绑定。
响应注释和参数的用法基本一样 swagger:response,这里不需要赘述,直接举例。
// A ValidationError is an error that is used when the required input fails validation.
// swagger:response validationError
type ValidationError struct {
// The error message
// in: body
Body struct {
// The validation message
//
// Required: true
// Example: Expected type int
Message string
// An optional field name to which this validation applies
FieldName string
}
}
复制代码
重点:以上的用法是 response 的基础用法,但是实际上并不符合很多公司内部的结构定义,所以下面会讲真正的灵活且实用的用法。
1、swagger:response 可以出现在任意结构体上。 不需要专门找到我们的 response 层,或者甚至没有 response 层的,而是每一个接口都定义了一个专门的 response,最后再统一用 interface 处理,从而导致我们在历史项目里加上 swagger 异常困难。
例如:
// SwaggerTestResponse
// swagger:response test_res
type SwaggerTestResponse struct {
// The error message
// in: body
Body struct {
// The validation message
//
// Required: true
// Example: Expected type int
Message string
// An optional field name to which this validation applies
FieldName string
}
}
复制代码
注意::必须严格按照格式,Response 结构体下嵌套一个 Body 结构体,也就是说如果是我们的历史项目,就得在 Response 外再包一层。例如:这是一正在是用的项目的 Response 返回,我们在上方加上 swagger:response,后面跟上它的唯一 id,test,在接口的返回处使用。
// Test
// swagger:response old_api_resp
type OldAPIRes struct {
// Test
// in: body
ID uint64
Name string
Time string
}
复制代码
// ServeAPI serves the API for this record store
func ServeAPI(host, basePath string, schemes []string) error {
// swagger:route GET /{id}/checkout SwaggerTest swagger_test_checkout
//
// Swagger测试接口
//
// 用于Swagger测试
//
// Consumes:
// - multipart/form-data
// Schemes: http
// Responses:
// 200: old_api_resp
mountItem("GET", basePath+"/{id}/checkout", nil)
}
复制代码
然后我们生成 swagger.json,发现所有参数都被定义在了 header 下,而这些返回参数实际上应该位于 body 中,否则就无法被 swagger ui 和 yapi 识别。
换句话说 swagger 的 in:body 只识别结构体内嵌套的结构体,为了迎合 swagger 的识别要求,我们对结构进行改造,换成下面这种写法,就可以被识别在 body 里了。
// Test
// swagger:response old_api_resp
type OldAPIRes struct {
// Test
// in: body
Body struct {
ID uint64
Name string
Time string
}
}
复制代码
上面这种写法其实很不方便,所有的接口的 Response 下都要多加一层 Body,这是不合理的,Swagger 只是注释,不应该侵入代码,除非原有的结构就是如此,否则不推荐上面的格式。
2、进阶版 swagger:model: 解决了上面的痛点,真正做到了灵活好用。
swagger:model 其实也是一个 swagger 规范,用法非常灵活,详细的用法会在后面介绍,这里就提出用 model 解决 response 的方法。
Response 的注释修改:
// swagger:model old_api_resp
type OldAPIRes struct {
ID uint64
Name string
Time string
}
复制代码
Route 注释修改:
// ServeAPI serves the API for this record store
func ServeAPI(host, basePath string, schemes []string) error {
// swagger:route GET /{id}/checkout SwaggerTest swagger_test_checkout
//
// Swagger测试接口
//
// 用于Swagger测试
//
// Consumes:
// - multipart/form-data
// Produces:
// - application/json
// Schemes: http
// Responses:
// 200: body:old_api_resp
mountItem("GET", basePath+"/{id}/checkout", nil)
复制代码
命令修改,-m 是扫描 model:
swagger generate spec -m -o ./swagger.json
复制代码
重新生成,然后搞定。
四、Swagger-Yapi
Yapi 一个高效、易用、功能强大的 API 管理平台。
为什么要打通 Swagger 到 Yapi 呢?理由很简单。Swagger 的 SwaggerUI 远没有 Yapi 功能全面,而 Yapi 能支持导入 Swagger.json 格式的接口文档,Swagger 的便利性和 Yapi 的全面性,我们把二者结合,从而实现更优的结果。
4.1►Nginx 搭建
经过上面的步骤我们应该已经在本地生成了我们接口的 Swagger.json,而 Yapi 已经支持了手动导入和自动导入两种方式。
手动导入:
自动导入:
我们需要的是什么?我们需要的是每次 Swagger 更新之后,Yapi 都会自动更新我们的接口,那么我们自然需要使用 Yapi 的自动导入,因此我们只需要在自己的机器上搭建一个 Nginx 来做静态文件代理,就能实现。
下载并安装 nginx:
sudo yum install nginx -y
复制代码
安装完成后查看:
启动 nginx
sudo systemctl start nginx
复制代码
或是
查看 nginx 状态
sudo systemctl status nginx
复制代码
或是
sudo service nginx status
复制代码
4.2►代理文件
进入 nginx 目录
在 conf.d 目录下新增需要代理的端口
data 映射的目录根据自己的实际情况,即 swagger.json 所在目录的位置而定。
server {
listen 8888;
server_name localhost;
location /data/ {
alias '/home/work/Swagger/swagger-yapi/swagger-json/';
}
}
复制代码
重启 nginx
sudo systemctl restart nginx
复制代码
或
sudo service nginx restart
复制代码
4.3►Yapi 自动同步
ip 对应你自己的机器 ip。
ip 地址可以通过以下命令查看:
如果被提示了:
说明路径不对,可以把地址输入浏览器访问,自己调整到正确即可。
五、结语
Swagger 还有很多的用法,光是通过 swagger -h 命令就能看到很多用法,而它的注释的用法也有很多,针对不同语言也有不同的写法。同理,Yapi 作为一款功能强大的 API 管理平台也是一样的有很多的用法,比如在线 mock 接口等等。本文仅作为一个快速上手入门 swagger 到 yapi 的方法,通道搭建好之后,更多的用法就可以各位同学自己去挖掘。
————————END————————
推荐阅读:
百度直播iOS SDK平台化输出改造
百度APP 基于Pipeline as Code的持续集成实践
Go 语言使用 MySQL 的常见故障分析和应对方法
百度交易中台之钱包系统架构浅析
基于宽表的数据建模应用
百度评论中台的设计与探索
基于模板配置的数据可视化平台
评论