go 使用 swagger 自动api文档
传送门:https://github.com/swaggo/swag/blob/master/README_zh-CN.md
go-swagger
想要使用go-swagger
为代码自动生成接口文档,一般需要下面几个步骤:
-
安装swag工具
-
按照swagger要求给接口代码添加声明式注释,具体参照声明式注释格式。
-
使用swag工具扫描代码自动生成API接口文档数据
-
使用gin-swagger渲染在线接口文档页面
1. 安装swag工具
使用以下命令安装swag工具:
go get -u github.com/swaggo/swag/cmd/swag
swag cli
swag init -h
NAME:
swag init - Create docs.go
USAGE:
swag init [command options] [arguments...]
OPTIONS:
--generalInfo value, -g value API通用信息所在的go源文件路径,如果是相对路径则基于API解析目录 (默认: "main.go")
--dir value, -d value API解析目录 (默认: "./")
--propertyStrategy value, -p value 结构体字段命名规则,三种:snakecase,camelcase,pascalcase (默认: "camelcase")
--output value, -o value 文件(swagger.json, swagger.yaml and doc.go)输出目录 (默认: "./docs")
--parseVendor 是否解析vendor目录里的go源文件,默认不
--parseDependency 是否解析依赖目录中的go源文件,默认不
--markdownFiles value, --md value 指定API的描述信息所使用的markdown文件所在的目录
--generatedTime 是否输出时间到输出文件docs.go的顶部,默认是
2. 添加注释
添加通用的API注释
在程序入口main函数上以注释的方式写下项目相关介绍信息
package main
// @title xxx API (必填,缺少会有警告)
// @version 1.0 (必填)
// @description This is cxy api docs.
// @license.name Apache 2.0
// @contact.name go-swagger帮助文档
// @contact.url https://github.com/swaggo/swag/blob/master/README_zh-CN.md
// @host localhost:8080
// @BasePath /api/v1
// ... 其他选项看需求
func main() {
r := gin.New()
...
r.Run()
}
添加接口的注释
在你代码中处理请求的接口函数按如下方式写上注释:
// ShowAccount godoc
// @Summary Show an account
// @Description get string by ID
// @Tags accounts
// @Accept json
// @Produce json
// @Param id path int true "Account ID"
// @Success 200 {object} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Router /accounts/{id} [get]
func (c *Controller) ShowAccount(ctx *gin.Context) {
id := ctx.Param("id")
aid, err := strconv.Atoi(id)
if err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}
account, err := model.AccountOne(aid)
if err != nil {
httputil.NewError(ctx, http.StatusNotFound, err)
return
}
ctx.JSON(http.StatusOK, account)
}
3. 生成接口文档数据
在项目根目录执行 swag init 命令,使用swag工具生成接口文档数据
报错:
2021/11/25 11:20:28 Generate swagger docs....
2021/11/25 11:20:28 Generate general API Info, search dir:./
2021/11/25 11:20:28 warning: failed to get package name in dir: ./, error: execute go list command, exit status 1, stdout:, stderr:no Go files in /home/song/appDisk/hupeng/Src/apollo
2021/11/25 11:20:28 cannot parse source files /home/song/appDisk/hupeng/Src/apollo/main.go: open /home/song/appDisk/hupeng/Src/apollo/main.go: no such file or directory
在包含main.go
文件的项目根目录运行 swag init 这将会解析注释并生成需要的文件。
执行完上述命令后,如果写的注释格式没问题,此时在包含mai.go的目录下会多出一个docs
文件夹。
./docs
├── docs.go
├── swagger.json
└── swagger.yaml
4. 引入gin-swagger渲染文档数据
然后在项目代码引入gin-swagger
相关内容:
apollo 项目的 ugc_server 的 main.go 文件
import (
"apollo/docs" // 导入swagger文档用的
gs "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
)
在注册http路由的地方,注册swagger api相关路由
r.GET("/swagger/*any", gs.WrapHandler(swaggerFiles.Handler))
有些通用的API注释,可以动态设置
生成的代码包docs
导出SwaggerInfo
变量,使用该变量可以通过编码的方式设置,这种方式可以更灵活比如可以根据环境写不同// @host的值等等
一般都是测试环境和本地使用,生产环境禁用Swagger
这样我们可以封装一个方法来调用:
func handleSwagger(r *gin.Engine, conf *config.Configuration) {
if conf.Server.Env == config.EnvTest {
docs.SwaggerInfo.Host = conf.Server.Host + conf.Server.Addr
r.GET("/swagger/*any", gs.WrapHandler(swaggerFiles.Handler))
}
}
注:如果把 // @version, // @title 也用 SwaggerInfo
变量 来写,会有个警告。
把你的项目程序运行起来,使用该服务自己的端口,打开浏览器访问[http://ip:port/swagger/index.html#/]就能看到Swagger 2.0 Api文档了。

遇到的问题1:No operations defined in spec!
问题解决:到项目根目录下执行
swag init -g cmd/ugc_server/main.go
https://github.com/swaggo/gin-swagger/issues/99
swagger生成的接口文档页面示例

遇到的问题2:把gin库从v1.6.3升级到了v1.7.4
问题解决:
go mod edit -replace=github.com/gin-gonic/gin@v1.7.4=github.com/gin-gonic/gin@v1.6.3
遇到的问题3:ParseComment error
ParseComment error in file /home/song/appDisk/hupeng/Src/apollo/internal/handler/device/laser_cutter_list.go :cannot find type definition: common.OSLang
依赖的vendor里面的类型不识别
问题解决:
加上 --parseVendor 参数,解析vendor目录里的go源文件
swag init --parseVendor -g cmd/ugc_server/main.go
ParseComment error in file /home/song/appDisk/hupeng/Src/apollo/internal/handler/device/laser_cutter_list.go :cannot find type definition: reflect.Kind
go语言本身的类型不识别
问题解决:
加上 --parseDependency 参数
时间太久加上 --parseDepth 1 参数,依赖解析深度1减少解析时间
swag init --parseVendor --parseInternal --parseDependency --parseDepth 1 -g cmd/ugc_server/main.go
时间减少很多,每次在10秒左右,
还有一种解决方法:使用swaggertype
标签更改字段类型
ValueType reflect.Kind `json:"valueType" bson:"value_type" swaggertype:"integer"`
swag init --parseVendor -g cmd/ugc_server/main.go
时间快很多,大概在3秒左右
注解说明
@Tags: 分类信息,用逗号分割多个,写多个的话,在api文档里面会生成多个接口
@Summary: 操作的简短摘要。
@Accept json
@Produce json
@Param:参数信息,用空格分隔的参数。
param name,param type,data type,is mandatory?,comment,attribute(optional)
1.参数名,2.参数类型,3.参数数据类型,4.是否必须,5.参数描述,6.其他属性
1.参数名
参数名就是我们解释参数的名字。
2.参数类型,可以有的值是 query、path、body、header,formData
-
query 表示带在 url 之后的参数
-
path 表示请求路径上得参数
-
body 表示是一个 raw 数据请求,当
Accept
是JSON
格式时,我们使用该字段指定接收的JSON类型 -
header 表示带在 header 信息中得参数
-
formData 表示是 post 请求的数据
3.参数数据类型
数据类型主要支持以下几种:
-
string (string)
-
integer (int, uint, uint32, uint64)
-
number (float32)
-
boolean (bool)
-
自定义struct
4.是否必须
5.参数描述
就是参数的一些说明,注释
6.其他属性
除了上面这些属性外,我们还可以为该参数填写一些额外的属性,如枚举,默认值,值范围等。
例如:
枚举
// @Param enumstring query string false "string enums" Enums(A, B, C)
// @Param enumint query int false "int enums" Enums(1, 2, 3)
// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3)
值添加范围
// @Param string query string false "string valid" minlength(5) maxlength(10)
// @Param int query int false "int valid" mininum(1) maxinum(10)
设置默认值
// @Param default query string false "string default" default(A)</pre>
代码示例
// @Summary 分类下的模型组列表
// @Tags 模型-V3版
// @Accept json
// @Produce json
// @Param cursor body string false "游标"
// @Param limit body integer true "每页请求数"
// @Param categoryId body string true "分类id"
// @Param filterType body integer false "过滤类型"
// @Response 200 {object} httpjson.Response
// @Router /api/cxy/v3/model/listCategory [post]
func ListCategory(ctx context.Context, originReq httpjson.IRequest) (interface{}, error){
}
评:这是方式行不通,每个字段都是一个json,测试时发现请求出去的数据就使用了最后一个字段的值
curl -X 'POST' \
'http://172.29.99.145:20087/api/cxy/v3/model/listCategory' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '0'
// @Summary 分类下的模型组列表
// @Tags 模型-V3版
// @Accept json
// @Produce json
// @Param cursor formData string false "游标"
// @Param limit formData integer true "每页请求数"
// @Param categoryId formData string true "分类id"
// @Param filterType formData integer false "过滤类型"
// @Response 200 {object} httpjson.Response
// @Router /api/cxy/v3/model/listCategory [post]
func ListCategory(ctx context.Context, originReq httpjson.IRequest) (interface{}, error){
}
评:这是方式行不通,这是表单提交的格式数据
curl -X 'POST' \
'http://172.29.99.145:20087/api/cxy/v3/model/listCategory' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d 'cursor=%22%22&limit=10&categoryId=%221%22&filterType=0'
// @Summary 分类下的模型组列表
// @Tags 模型-V3版
// @Accept json
// @Produce json
// @Param req body ListCategoryReq true "req"
// @Response 200 {object} httpjson.Response
// @Router /api/cxy/v3/model/listCategory [post]
func ListCategory(ctx context.Context, originReq httpjson.IRequest) (interface{}, error){
}
评:这个可以顺利通过测试
curl -X 'POST' \
'http://172.29.99.145:20087/api/cxy/v3/model/listCategory' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"categoryId": "1",
"cursor": "",
"filterType": 0,
"limit": 10,
"reverse": false
}'
优点:
请求参数不用一个个的写了,基本上来说是一个固定格式
缺点:
1.请求参数里面body里面的数据不能指定每个字段的是否必填,只能通过注释来显示
2.返回参数不能体现真实的返回具体数据,只是一个外壳数据,解决方法如下
type ListCategoryRspData struct {
List []*model3.ModelListItem `json:"list" `
NextCursor string `json:"nextCursor" `
}
// @Summary 分类下的模型组列表
// @Tags 模型-V3版
// @Accept json
// @Produce json
// @Param req body ListCategoryReq true "req"
// @Response 200 {object} httpjson.Response{result=ListCategoryRspData}
// @Router /api/cxy/v3/model/listCategory [post]
func ListCategory(ctx context.Context, originReq httpjson.IRequest) (interface{}, error){
}
评:这个可以顺利通过测试
优点:
请求参数不用一个个的写了,基本上来说是一个固定格式
能够返回每个接口的具体数据了
缺点:
1.请求参数里面body里面的数据不能指定每个字段的是否必填,只能通过注释来显示
swagger 还有一个缺点就是前后台接口都在一个文档中,没法分文档
swagger 优点不用手动维护接口文档了
权限问题
swagger除了可以查看文档外,还可以直接try it out 调用接口,有些接口是需要登录后才能调用有个token鉴权。
校验token 的api接口 header中必须有正确的 _CXY_TOKEN 和 ** _CXY_UID** 值
可以通过添加通用的API注释来设置全局接口的header值
// @securityDefinitions.apikey cxy_token
// @name __CXY_TOKEN_
// @in header
// @securityDefinitions.apikey cxy_uid
// @name __CXY_UID_
// @in header
然后在需要登录的接口注释上增加
// @Security cxy_token
// @Security cxy_uid</pre>
这样就可以在调用接口前先设置token和uid了

安装插件 Swagger Viewer
- 在线安装
直接在vscode插件管理器中搜 Swagger Viewer 找到点击安装即可
离线安装方法:
第一步,将扩展文件*.vsix放置在VS Code安装目录下的bin目录中,并在此目录Shift+鼠标右键,打开命令窗口
第二步:输入下面code --install ... 安装即可
C:\Users\Administrator\AppData\Local\Programs\Microsoft VS Code\bin>
code --install-extension Arjun.swagger-viewer-3.1.2.vsix</pre>