go 使用 swagger 自动api文档

传送门:https://github.com/swaggo/swag/blob/master/README_zh-CN.md

go-swagger

想要使用go-swagger为代码自动生成接口文档,一般需要下面几个步骤:

  1. 安装swag工具

  2. 按照swagger要求给接口代码添加声明式注释,具体参照声明式注释格式。

  3. 使用swag工具扫描代码自动生成API接口文档数据

  4. 使用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文档了。

image-20211125133216203.png

遇到的问题1:No operations defined in spec!

问题解决:到项目根目录下执行

swag init -g cmd/ugc_server/main.go

https://github.com/swaggo/gin-swagger/issues/99

swagger生成的接口文档页面示例

image-20211119210138366.png

遇到的问题2:把gin库从v1.6.3升级到了v1.7.4

问题解决:

go mod edit -replace=github.com/gin-gonic/[email protected]=github.com/gin-gonic/[email protected]

遇到的问题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 数据请求,当AcceptJSON格式时,我们使用该字段指定接收的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)

代码示例

// @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

这样就可以在调用接口前先设置token和uid了

image-20211125135401467.png

安装插件 Swagger Viewer

  1. 在线安装

直接在vscode插件管理器中搜 Swagger Viewer 找到点击安装即可

  1. 离线包下载地址: https://marketplace.visualstudio.com/vscode

离线安装方法:

第一步,将扩展文件*.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

你可能感兴趣的:(go 使用 swagger 自动api文档)