不会飞的章鱼

熟能生巧,勤能补拙;念念不忘,必有回响。

如何使用Swagger生成API文档

Swagger 介绍

Swagger 是一套围绕 OpenAPI 规范构建的开源工具,可以设计、构建、编写和使用REST API。Swagger 包含很多工具,其中主要的 Swagger 工具包括:

  • Swagger 编辑器:基于浏览器的编辑器,可以在其中编写 OpenAPI 规范,并实时预览API 文档。https://editor.swagger.io 就是一个 Swagger 编辑器,你可以尝试在其中编辑和预览 API 文档。
  • Swagger UI:将 OpenAPI 规范呈现为交互式 API 文档,并可以在浏览器中尝试 API调用。
  • Swagger Codegen:根据 OpenAPI 规范,生成服务器存根和客户端代码库,目前已涵盖了 40 多种语言。

Swagger 和 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来生成Swagger API文档

安装 Swagger 工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ go get -u github.com/go-swagger/go-swagger/cmd/swagger
$ swagger version
version: v0.28.0
$ 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

swagger 命令行工具介绍

子命令 功能
diff 对比两个swagger文档的差异
expand 展开swagger定义文档中的@ref
flatten 展开swagger文档
generate 生成swagger文档、客户端代码、服务端代码等
ini 初始化一个swagger定义文档,初始化时可以指定一些配置
mix 合并swagger文档
serv 启动http服务,以查看swagger文档
validate 验证Swagger定义文件是否正确
version 打开swagger命令版本

如何使用 swagger 命令生成 Swagger 文档?

go-swagger 通过解析源码中的注释来生成 Swagger 文档,go-swagger 的详细注释语法可参考官方文档。常用的有如下几类注释语法:

注释语法 功能
swagger:meta 定义API接口全局基本信息
swagger:route 定义路由信息
swagger:parameters 定义API请求参数
swagger:operation
swagger:response 定义API响应参数
swagger:model 定义可以复用的Go数据结构
swagger:allOf 嵌入其他的Go结构体
swagger:strfmt 定义格式化的字符串
swagger:discriminated
swagger:ignore 定义需要忽略的结构体

解析注释生成 Swagger 文档

swagger generate 命令会找到 main 函数,然后遍历所有源码文件,解析源码中与Swagger 相关的注释,然后自动生成 swagger.json/swagger.yaml 文件。

这一过程的示例代码为swagger。目录下有一个 main.go 文件,定义了如下 API 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main

import (
"fmt"
"log"
"net/http"

"swagger-demo/api"

"github.com/gin-gonic/gin"

// This line is necessary for go-swagger to find your docs!
_ "swagger-demo/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})
}

main 包中引入的 User struct 位于 swagger-demo/api 目录下的user.go文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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 包:

1
_ "swagger-demo/docs"

在 swagger-demo 目录下,创建 docs 文件夹:

1
2
$ mkdir docs
$ cd docs

在 docs 目录下,创建一个 doc.go 文件,在该文件中提供 API 接口的基本信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

// 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-demo 目录,执行如下命令,生成 Swagger API 文档,并启动 HTTP 服务,在浏览器查看 Swagger:

1
2
3
4
$ swagger generate spec -o swagger.yaml
$ swagger serve --no-open -F=swagger --port 36666 swagger.yaml

2022/1/16 23:16:47 serving docs at http://localhost:36666/docs
  • o:指定要输出的文件名。swagger 会根据文件名后缀.yaml 或者.json,决定生成的文件格式为 YAML 或 JSON。
  • –no-open:因为是在 Linux 服务器下执行命令,没有安装浏览器,所以使–no-open 禁止调用浏览器打开 URL。
  • -F:指定文档的风格,可选 swagger 和 redoc。我选用了 redoc,因为觉得 redoc 格式更加易读和清晰。
  • –port:指定启动的 HTTP 服务监听端口。

打开浏览器,访问http://localhost:36666/docs ,就可以看到效果了。

接下来,我们就可以编写 API 接口的定义文件,位于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

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 文档的魅力所在。
  • swagger:parameters:swagger:parameters定义了 API 接口的请求参数,例如 userParamsWrapper。userParamsWrapper 之上的注释会被解析为请求参数的描述,// in:body代表该参数是位于 HTTP Body 中。同样,userParamsWrapper 结构体名我们也可以随意命名,不会带来任何不同。swagger:parameters之后的 createUserRequest 会跟swagger:route的 ID 进行匹配,匹配成功则说明是该 ID 所在 API 接口的请求参数。

进入 swagger-demo 目录,执行如下命令,生成 Swagger API 文档,并启动 HTTP 服务,在浏览器查看 Swagger:

1
2
3
$ swagger generate spec -o swagger.yaml
$ swagger serve --no-open -F=swagger --port 36666 swagger.yaml
2021/1/27 23:50:30 serving docs at http://localhost:36666/docs

打开浏览器,访问 http://localhost:36666/docs ,如下图所示:

go-swagger 其他常用功能介绍

对比 Swagger 文档

1
2
3
4
5
6
7
$ swagger diff -d change.log swagger.new.yaml swagger.old.yaml
$ cat change.log

BREAKING CHANGES:
=================
/users:post Request - Body.Body.nickname.address.email.name.Body : User - Deleted property
compatibility test FAILED: 1 breaking changes detected

生成服务端代码

1
2
3
$ mkdir go-user
$ cd go-user
$ swagger generate server -f ../swagger.yaml -A go-user

生成客户端代码

在 go-user 目录下执行如下命令:

1
$ swagger generate client -f ../swagger.yaml -A go-user

验证 Swagger 文档是否合法

1
2
3
$ swagger validate swagger.yaml
2020/10/21 09:53:18
The swagger spec at "swagger.yaml" is valid against swagger specification 2.0

合并 Swagger 文档

1
$ swagger mixin swagger_part1.yaml swagger_part2.yaml

注:以上所有代码的Github地址

------ 本文结束------
如果本篇文章对你有帮助,可以给作者加个鸡腿~(*^__^*),感谢鼓励与支持!