Swagger 生成 API 文档

文章目录

  • 初识 Swagger
    • Swagger 简介
    • Swagger 与 OpenAPI 的区别
  • 生成 Swagger API 文档
    • 实现方式
    • go-swagger
      • 安装 go-wagger 工具
      • go-swgger 的使用
      • go-swagger 常用功能
    • swag
      • 安装 swag 工具
      • 下载 gin-swagger
      • gin-swagger 的使用


初识 Swagger


Swagger 简介

Swagger 是一套围绕 OpenAPI 规范构建的开源工具(本质上是一种用于描述使用 JSON 格式表示的 RESTful API 的接口描述语言),可以用于设计、构建、编写和使用 RESTful API,Swagger 主要的工具如下:

  • Swagger 编辑器 :基于浏览器的编辑器,可以在其中编写 OpenAPI 规范,并实时预览 API 文档。

  • Swagger UI :将 OpenAPI 规范呈现为交互式 API 文档,并可以在浏览器中尝试 API 调用。

  • Swagger Codegen :根据 OpenAPI 规范,生成服务器存根和客户端代码库。


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 则是实现规范的工具。


生成 Swagger API 文档


实现方式

在 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


安装 go-wagger 工具

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-swgger 的使用

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 文档

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 界面,如下图所示:


go-swagger 常用功能

上面介绍了 swagger 最常用的 generate、serve 命令,关于 swagger 其它的命令的使用,具体如下所示:

  • 对比 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 文档是否合法
swagger validate swagger.yaml
  • 合并 Swagger 文档
swagger mixin swagger_part1.yaml swagger_part2.yaml

以 gin 框架为例,使用 gin-swagger 库以使用 Swagger 2.0 自动生成 RESTful API 文档。


swag


gin-swagger 为代码自动生成接口文档,一般需要下面三个步骤:

(1) 按照 swagger 要求给接口代码添加声明式注释,具体参照 声明式注释格式

(2) 使用 swag 工具扫描代码自动生成 API 接口文档数据。

(3) 使用 gin-swagger 渲染在线接口文档页面。


安装 swag 工具

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

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 个字段是可选的,各字段含义为:

    1. 参数名称;

    2. 参数在 HTTP 请求中的位置(body、path、query);

    3. 参数类型(string、int、bool 等) ;

    4. 是否必须(true、false);

    5. 参数描述;

    6. 选项,这里用的是 default() 用来指定默认值;

  • Success:成功返回数据格式,分为 4 个字段;

    1. HTTP 返回 Code;

    2. 返回数据类型

    3. 返回数据模型;

    4. 说明;

  • 路由格式,分为 2 个字段:

    1. API 路径;

    2. HTTP 方法;

API 文档编写规则请参考 See Declarative Comments Format

API 文档有更新时,需重新执行 swag init 命令并重新编译项目文件。


gin-swagger 的使用

具体示例如下:

  • 添加注释

在程序入口 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)
		// 返回响应
}

上面注释中参数类型使用了 objectmodels.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 渲染文档数据

在项目代码中注册路由的地方按如下方式引入 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文档

gin-swagger 同时还提供了 DisablingWrapHandler() 函数,通过设置某些环境变量来禁用 Swagger ,例如如下关键的程序代码部分:

r.GET("/swagger/*any", gs.DisablingWrapHandler(swaggerFiles.Handler, "NAME_OF_ENV_VARIABLE"))

此时如果将环境变量 NAME_OF_ENV_VARIABLE 设置为任意值,则 /swagger/*any 将返回404响应,就像未指定路由时一样。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

물の韜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值