赞
踩
Swagger 是一套围绕 OpenAPI 规范构建的开源工具(本质上是一种用于描述使用 JSON 格式表示的 RESTful API 的接口描述语言),可以用于设计、构建、编写和使用 RESTful API,Swagger 主要的工具如下:
Swagger 编辑器 :基于浏览器的编辑器,可以在其中编写 OpenAPI 规范,并实时预览 API 文档。
Swagger UI :将 OpenAPI 规范呈现为交互式 API 文档,并可以在浏览器中尝试 API 调用。
Swagger Codegen :根据 OpenAPI 规范,生成服务器存根和客户端代码库。
OpenAPI 是一个 API 规范,它的前身叫做 Swagger 规范 ,通过定义一种用来描述 API 格式或 API 定义的语言来规范 RESTful 服务开发过程,目前最新的 OpenAPI 规范是 OpenAPI 3.0(即 Swagger 2.0 规范)。
OpenAPI 规定了一个 API 必须包含的基本信息,这些信息包括如下内容:
对 API 的描述,介绍 API 可以实现的功能。
每个 API 上可用的路径(/users)和操作(GET /users ,POST /users)。
每个 API 的输入/返回的参数。
验证方法。
联系信息、许可证、使用条款和其其它信息。
总结:OpenAPI 是一个 API 规范,Swagger 则是实现规范的工具。
在 Go 项目开发中,可以通过下面两种方式生成 Swagger API 文档:
第一,根据 Swagger 语法(参考 Swagger 官方提供的 OpenAPI Specification )可以直接编写 JSON/YAML 格式的 Swagger 文档。
第二,通过工具生成 Swagger 文档,目前可以通过 swag 和 go-swagger 两个工具来生成。
对比这两种方法,直接编写 Swagger 文档不如使用 go-swagger 工具基于代码注释来自动生成 Swagger 文档高效。
go-swagger 相对于 swag 的优势:
go-swagger 比 swag 功能更强大:go-swagger 提供了更灵活、更多的功能来描述项目中的 API 。
使用 swag 需每一个 API 都要有一个冗长的注释,有时候代码注释比代码还要长。
go-swagger 可以将代码和注释分开编写,一方面可以使程序代码保持简洁,清晰易读;另一方面可以在另外一个包中,统一管理这些 Swagger API 文档定义。
更好的社区支持:go-swagger 目前有非常多的 Github star 数,出现 Bug 的概率很小,并且处在一个频繁更新的活跃状态。
go-swagger 有如下的特性:
根据 Swagger 定义文件生成服务端代码。
根据 Swagger 定义文件生成客户端代码。
校验 Swagger 定义文件是否正确。
启动一个 HTTP 服务器,可以通过浏览器访问 API 文档。
根据 Swagger 文档定义的参数生成 Go model 结构体定义。
go-swagger 是一个功能强大的、高性能的、可以根据代码注释生成 Swagger API 文档的工具,可以减少编写文档的时间,提高开发效率,并能保证文档的及时性和准确性。
go-swagger 通过 swagger 命令行 工具来完成其功能,安装的方式如下:
alias swagger='docker run --rm -it --user $(id -u):$(id -g) -e GOPATH=$(go env GOPATH):/go -v $HOME:$HOME -w $(pwd) quay.io/goswagger/swagger'
或者依次输入如下命令:
sudo apt update
sudo apt install -y apt-transport-https gnupg curl
curl -1sLf 'https://dl.cloudsmith.io/public/go-swagger/go-swagger/gpg.2F8CB673971B5C9E.key' | sudo apt-key add -
curl -1sLf 'https://dl.cloudsmith.io/public/go-swagger/go-swagger/config.deb.txt?distro=debian&codename=any-version' |
sudo tee /etc/apt/sources.list.d/go-swagger-go-swagger.list
sudo apt update
sudo apt install swagger -y
验证是否安装成功,输入 swagger version
命令并输出如下内容表示安装成功:
version: v0.30.4
commit: df6da9b77aa9751f06bedb17fcf92b1ab67a7a47
go-swagger 命令格式为 swagger [OPTIONS] <command>
,可以通过swagger -h
命令来查看 swagger 的使用帮助,swagger 提供的子命令及功能见下表:
子命令 | 功能说明 |
---|---|
diff | 对比两个 Swagger 文档的差异 |
expand | 展开 Swagger 定义文档中的 $ref |
init | 初始化一个 Swagger 定义文档,可指定一些配置 |
serv | 启动 HTTP 服务 |
mix | 合并 Swagger 文档 |
version | 查看 Swagger 版本信息 |
flatten | 展平 Swagger 文档 |
generate | 生成 Swagger 文档、客户端代码、服务端代码等 |
validate | 验证 Swagger 定义的文件是否正确 |
go-swagger 通过解析源码中的注释来生成 Swagger 文档,go-swagger 的详细注释语法可参考官方文档https://goswagger.io/,常用的有如下几类注释语法:
注释语法 | 说明 |
---|---|
swagger:meta | 定义 API 接口全局基本信息 |
swagger:route | 定义路由信息 |
swagger:parameter | 定义 API 请求参数 |
swagger:response | 定义 API 响应参数 |
swagger:model | 定义可以复用的 Go 数据结构 |
swagger:allOf | 嵌入其它 Go 结构体 |
swagger:strfmt | 定义格式化的字符串 |
swagger:ignore | 定义需要忽略的结构体 |
swagger generate
命令会找到项目中的 main 函数 ,然后遍历所有源码文件,解析源码中与 Swagger 相关的注释,最后自动生成 swagger.json 或 swagger.yaml 文件。
例如创建一个 swagger
目录,在该目录下编写一个定义 API 接口名为 main.go
的程序,该程序的具体内容如下:
package main
import (
"fmt"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/marmotedu/gopractise-demo/swagger/api"
// This line is necessary for go-swagger to find your docs!
_ "github.com/marmotedu/gopractise-demo/swagger/docs"
)
var users []*api.User
func main() {
r := gin.Default()
r.POST("/users", Create)
r.GET("/users/:name", Get)
log.Fatal(r.Run(":5555"))
}
// Create create a user in memory.
func Create(c *gin.Context) {
var user api.User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": err.Error(), "code": 10001})
return
}
for _, u := range users {
if u.Name == user.Name {
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("user %s already exist", user.Name), "code": 10001})
return
}
}
users = append(users, &user)
c.JSON(http.StatusOK, user)
}
// Get return the detail information for a user.
func Get(c *gin.Context) {
username := c.Param("name")
for _, u := range users {
if u.Name == username {
c.JSON(http.StatusOK, u)
return
}
}
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("user %s not exist", username), "code": 10002})
}
编写一个引入位于 swagger/api
目录下 user.go
文件中的 的 User struct 的程序,该程序的具体代码如下:
// Package api defines the user model.
package api
// User represents body of User request and response.
type User struct {
// User's name.
// Required: true
Name string `json:"name"`
// User's nickname.
// Required: true
Nickname string `json:"nickname"`
// User's address.
Address string `json:"address"`
// User's email.
Email string `json:"email"`
}
// Required: true 说明字段是必须的,生成 Swagger 文档时,也会在文档中声明该字段是必须字段。
为了使代码保持简洁,在另外一个Go 包中编写带 go-swagger 注释的 API 文档,假设该 Go 包名为 docs ,在开始编写 Go API 注释之前,需要在 main.go
文件中导入 docs
包:
_ "github.com/marmotedu/gopractise-demo/swagger/docs"
通过导入 docs 包,可以使 go-swagger 在递归解析依赖包时,找到 docs 包,并解析包中的注释。
在 swagger
目录下,创建 docs
文件夹,在 docs
目录下,创建一个 doc.go
文件,在该文件中提供 API 接口的基本信息,该文件的具体内容如下:
// Package docs awesome.
//
// Documentation of our awesome API.
//
// Schemes: http, https
// BasePath: /
// Version: 0.1.0
// Host: some-url.com
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Security:
// - basic
//
// SecurityDefinitions:
// basic:
// type: basic
//
// swagger:meta
package docs
说明:Package docs 后面的字符串 awesome 代表 HTTP 服务名, Documentation of our awesome API 是 程序中 API 的描述,其它都是 go-swagger 可识别的注释,代表一定的意义,最后以 swagger:meta 注释结束。
编写完 doc.go
文件后,进入 swagger
目录,生成 Swagger API 文档并启动 HTTP 服务,输入如下的命令:
swagger generate spec -o swagger.yaml
swagger serve --no-open -F=swagger --port 36666 swagger.yaml
-o :指定要输出的文件名,swagger 会根据文件名后缀 .yaml
或者 .json
,决定生成的文件格式为 YAML 或 JSON 。
-no-open :因为是在 Linux 服务器下执行命令,没有安装浏览器,所以使 -no-open 禁止调用浏览器打开 URL 。
-F :指定文档的风格,可选 swagger 或 redoc 。
-port :指定启动的 HTTP 服务监听端口。
打开浏览器,访问网址 http://localhost:36666/docs ,浏览到如下页面:
如果需要生成 JSON 格式的 Swagger 文档,需将生成的 swagger.yaml
转换为 swagger.json
,输入如下的命令:
swagger generate spec -i ./swagger.yaml -o ./swagger.json
接下来,编写 API 接口的定义文件(user.go
),该程序具体的代码如下:
package docs
import (
"github.com/marmotedu/gopractise-demo/swagger/api"
)
// swagger:route POST /users user createUserRequest
// Create a user in memory.
// responses:
// 200: createUserResponse
// default: errResponse
// swagger:route GET /users/{name} user getUserRequest
// Get a user from memory.
// responses:
// 200: getUserResponse
// default: errResponse
// swagger:parameters createUserRequest
type userParamsWrapper struct {
// This text will appear as description of your request body.
// in:body
Body api.User
}
// This text will appear as description of your request url path.
// swagger:parameters getUserRequest
type getUserParamsWrapper struct {
// in:path
Name string `json:"name"`
}
// This text will appear as description of your response body.
// swagger:response createUserResponse
type createUserResponseWrapper struct {
// in:body
Body api.User
}
// This text will appear as description of your response body.
// swagger:response getUserResponse
type getUserResponseWrapper struct {
// in:body
Body api.User
}
// This text will appear as description of your error response body.
// swagger:response errResponse
type errResponseWrapper struct {
// Error code.
Code int `json:"code"`
// Error message.
Message string `json:"message"`
}
user.go
程序文件说明:
swagger:route:swagger:route 代表 API 接口描述的开始,后面的字符串格式为 HTTP 方法 URL Tag ID,可以填写多个 tag ,相同 tag 的 API 接口在 Swagger 文档中会被分为一组。
ID 是一个标识符, swagger:parameters 是具有相同 ID 的 swagger:route 的请求参数,swagger:route下面的一行是该 API 接口的描述,需要以英文点号为结尾。
responses:定义了API接口的返回参数,例如当 HTTP 状态码是 200 时,返回 createUserResponse , createUserResponse 会跟 swagger:response 进行匹配,匹配成功的 swagger:response 就是该 API 接口返回 200 状态码时的返回。
swagger:response:swagger:response 定义了 API 接口的返回,例如 getUserResponseWrapper ,关于名字可以根据需要自由命名,并不会带来任何不同,getUserResponseWrapper 中有一个 Body 字段,其注释为 // in:body ,说明该参数是在 HTTP Body 中返回,swagger:response 之上的注释会被解析为返回参数的描述,api.User 自动被 go-swagger 解析为 Example Value 和 Model ,不用再去编写重复的返回字段,只需要引用已有的 Go 结构体即可。
swagger:parameters:swagger:parameters 定义了 API 接口的请求参数,例如 userParamsWrapper ,userParamsWrapper 之上的注释会被解析为请求参数的描述,// in:body 代表该参数是位于 HTTP Body 中。同样,userParamsWrapper 结构体名我们也可以随意命名,不会带来任何不同。swagger:parameters之后的 createUserRequest 会跟 swagger:route 的 ID 进行匹配,匹配成功则说明是该 ID 所在 API 接口的请求参数。
生成 Swagger API 文档并启动 HTTP 服务,进入 swagger
目录,输入如下的命令:
swagger generate spec -o swagger.yaml
swagger serve --no-open -F=swagger --port 36666 swagger.yaml
打开浏览器,访问网址 http://localhost:36666/docs ,浏览到如下页面:
上面生成了 swagger 风格的 UI 界面,也可以使用 redoc 风格的 UI 界面,如下图所示:
上面介绍了 swagger 最常用的 generate、serve 命令,关于 swagger 其它的命令的使用,具体如下所示:
swagger diff -d change.log swagger.new.yaml swagger.old.yaml
先定义 Swagger 接口文档,再用 swagger 命令,基于 Swagger 接口文档生成服务端代码。假设应用名为 go-user ,进入 swagger
目录,创建 go-user
目录,并生成服务端代码:
swagger generate server -f ../swagger.yaml -A go-user
上述命令会在当前目录生成 cmd、restapi、models 文件夹,可执行如下命令查看 server 组件启动方式:
go run cmd/go-user-server/main.go -h
在 go-user
目录下执行如下命令:
swagger generate client -f ../swagger.yaml -A go-user
swagger validate swagger.yaml
swagger mixin swagger_part1.yaml swagger_part2.yaml
以 gin 框架为例,使用 gin-swagger 库以使用 Swagger 2.0 自动生成 RESTful API 文档。
用 gin-swagger 为代码自动生成接口文档,一般需要下面三个步骤:
(1) 按照 swagger 要求给接口代码添加声明式注释,具体参照 声明式注释格式 。
(2) 使用 swag 工具扫描代码自动生成 API 接口文档数据。
(3) 使用 gin-swagger 渲染在线接口文档页面。
swag 工具的安装,依次执行如下命令:
mkdir -p $GOPATH/src/github.com/swaggo
cd $GOPATH/src/github.com/swaggo
git clone https://github.com/swaggo/swag
cd swag/cmd/swag/
go install -v
因为该包引用
golang.org
中的包,而网络环境原因,一般很难连上golang.org
,所以这里不采用go get
的方式进行安装。
初始化项目,进入 xxx
项目的根目录执行 swag init
命令,例如如下形式:
cd $GOPATH/src/apiserver
swag init
gin-swagger 的安装,依次执行如下命令:
cd $GOPATH/src/github.com/swaggo
git clone https://github.com/swaggo/gin-swagger
编写 API 注释,Swagger 中需要将相应的注释或注解编写到方法上,再利用生成器自动生成说明文件,这里用创建用户 API 来举例:
package user
import (
...
)
// @Summary Add new user to the database
// @Description Add a new user
// @Tags user
// @Accept json
// @Produce json
// @Param user body user.CreateRequest true "Create a new user"
// @Success 200 {object} user.CreateResponse "{"code":0,"message":"OK","data":{"username":"kong"}}"
// @Router /user [post]
func Create(c *gin.Context) {
...
}
执行 swag init
命令,在 apiserver 根目录下生成 docs
目录
文档语法说明
:
Summary:简单阐述 API 的功能;
Description:API 详细描述;
Tags:API 所属分类;
Accept:API 接收参数的格式;
Produce:输出的数据格式,这里是 JSON 格式;
Param:参数,分为 6 个字段,其中第 6 个字段是可选的,各字段含义为:
参数名称;
参数在 HTTP 请求中的位置(body、path、query);
参数类型(string、int、bool 等) ;
是否必须(true、false);
参数描述;
选项,这里用的是 default()
用来指定默认值;
Success:成功返回数据格式,分为 4 个字段;
HTTP 返回 Code;
返回数据类型
返回数据模型;
说明;
路由格式,分为 2 个字段:
API 路径;
HTTP 方法;
API 文档编写规则请参考 See Declarative Comments Format。
API 文档有更新时,需重新执行 swag init
命令并重新编译项目文件。
具体示例如下:
在程序入口 main
函数上以注释的方式写下项目相关介绍信息,例如如下的程序代码部分:
package main
// @title 这里写标题
// @version 1.0
// @description 这里写描述信息
// @termsOfService http://swagger.io/terms/
// @contact.name 这里写联系人信息
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host 这里写接口服务的host
// @BasePath 这里写base path
func main() {
r := gin.New()
r.Run()
}
在代码中处理请求的接口函数(通常位于controller层)按如下方式写上注释:
// GetPostListHandler2 升级版帖子列表接口
// @Summary 升级版帖子列表接口
// @Description 可按社区按时间或分数排序查询帖子列表接口
// @Tags 帖子相关接口
// @Accept application/json
// @Produce application/json
// @Param Authorization header string false "Bearer 用户令牌"
// @Param object query models.ParamPostList false "查询参数"
// @Security ApiKeyAuth
// @Success 200 {object} _ResponsePostList
// @Router /posts2 [get]
func GetPostListHandler2(c *gin.Context) {
// GET请求参数(query string):/api/v1/posts2?page=1&size=10&order=time
// 初始化结构体时指定初始参数
p := &models.ParamPostList{
Page: 1,
Size: 10,
Order: models.OrderTime,
}
if err := c.ShouldBindQuery(p); err != nil {
zap.L().Error("GetPostListHandler2 with invalid params", zap.Error(err))
ResponseError(c, CodeInvalidParam)
return
}
data, err := logic.GetPostListNew(p)
// 获取数据
if err != nil {
zap.L().Error("logic.GetPostList() failed", zap.Error(err))
ResponseError(c, CodeServerBusy)
return
}
ResponseSuccess(c, data)
// 返回响应
}
上面注释中参数类型使用了 object
,models.ParamPostList
具体定义如下:
// bluebell/models/params.go
// ParamPostList 获取帖子列表query string参数
type ParamPostList struct {
CommunityID int64 `json:"community_id" form:"community_id"` // 可以为空
Page int64 `json:"page" form:"page" example:"1"` // 页码
Size int64 `json:"size" form:"size" example:"10"` // 每页数据量
Order string `json:"order" form:"order" example:"score"` // 排序依据
}
响应数据类型也使用的 object
,在 controller 层专门定义一个 docs_models.go
文件来存储文档中使用的响应数据 model ,例如如下的程序代码部分:
// /controller/docs_models.go
// _ResponsePostList 帖子列表接口响应数据
type _ResponsePostList struct {
Code ResCode `json:"code"` // 业务响应状态码
Message string `json:"message"` // 提示信息
Data []*models.ApiPostDetail `json:"data"` // 数据
}
编写完注释后,安装 swag 工具,输入如下的命令:
go get -u github.com/swaggo/swag/cmd/swag
在项目根目录使用 swag 工具生成接口文档数据,输入如下的命令:
swag init
执行完上述命令后,如果写的注释格式没问题,此时项目根目录下会多出一个docs
文件夹,该文件夹结构如下:
./docs
├── docs.go
├── swagger.json
└── swagger.yaml
在项目代码中注册路由的地方按如下方式引入 gin-swagger
相关内容:
import (
_ "bluebell/docs" // 千万不要忘了导入把上一步生成的docs
gs "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
"github.com/gin-gonic/gin"
)
注册 Swagger API 相关路由,关键的程序代码如下:
r.GET("/swagger/*any", gs.WrapHandler(swaggerFiles.Handler))
把项目程序运行起来,打开浏览器访问http://localhost:8080/swagger/index.html 就能浏览到如下 Swagger 2.0 API 文档页面:
gin-swagger
同时还提供了 DisablingWrapHandler()
函数,通过设置某些环境变量来禁用 Swagger ,例如如下关键的程序代码部分:
r.GET("/swagger/*any", gs.DisablingWrapHandler(swaggerFiles.Handler, "NAME_OF_ENV_VARIABLE"))
此时如果将环境变量 NAME_OF_ENV_VARIABLE
设置为任意值,则 /swagger/*any
将返回404响应,就像未指定路由时一样。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。