Swagger能够帮助生成易于共享且具有足够描述性的API文档。

使用go-swagger生成swagger文档

go-swagger能够基于注释来自动生成swagger文档。

安装

$ go get -u github.com/go-swagger/go-swagger/cmd/swagger
$ swagger version
dev

注释语法

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

生成文档

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

在项目目录下的main.go文件中,定义了如下API接口

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})
}

通过在一个单独的目录下编写带go-swagger注释的API文档,这里的报名为docs。那么需要在main.go文件中导入这个包,以便go-swagger解析main的依赖包的时候,可以找到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注释结束==。

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

// 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文档也会标注为必须的。

在一个单独的包下编写API接口的定义文件docs/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"`
}

以下面代码块为示例

// swagger:route POST /users user createUserRequest
// Create a user in memory.
// responses:
//   200: createUserResponse
//   default: errResponse
  • swagger:route:代表API接口描述的开始。后面依次跟着的就是HTTP方法 URL Tag ID,可以由多个Tag,具有相同Tag的API接口会分为一组。ID 是一个标识符,swagger:parameters是具有相同 ID 的swagger:route的请求参数。下面一行是对API接口的描述,以英文点号结尾。再下面的responses定义了API接口的返回参数,状态码为200时的响应体,以及其他情况下的响应体。
// swagger:parameters createUserRequest
type userParamsWrapper struct {
    // This text will appear as description of your request body.
    // in:body
    Body api.User
}
  • swagger:paramters:定义接口的请求参数,其后跟着的id与某个swagger:route的id一致,那么就作为对应请求的参数。内部的//in:body表示该参数在HTTP Body中返回。swagger会自动将类型解析为对应的值和model。
// This text will appear as description of your response body.
// swagger:response createUserResponse
type createUserResponseWrapper struct {
    // in:body
    Body api.User
}
  • swagger:responses:定义接口的返回。其后跟着的名称与哪个swagger:route中的responses下的名称保持一致,那么就作为哪个API的返回参数。//in:body与前面的作用一致

其实go-swagger的关键就是可以利用已有的go结构体即可,方便生成文档。

编写完成之后,就可以生成文档并且可以启动HTTP服务在浏览器查看swagger

swagger generate spec -o swagger.yaml
swagger generate --no-open -F=swagger --port 36666 swagger.yaml

额外功能

  1. 对比swagger文档
$ 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. 生成服务端代码 指定swagger文件和应用名称,就可以生成服务端代码。
$ mkdir go-user
$ cd go-user
$ swagger generate server -f ../swagger.yaml -A go-user

上述命令会在当前目录生成cmd、restapi、models文件夹,例如现在就可以执行服务端代码

$ go run cmd/go-user-server/main.go -h
  1. 生成客户端代码
$ swagger generate client -f ../swagger.yaml -A go-user

会在当前目录生成client 4. 验证swagger文档是否合法

$ swagger validate swagger.yaml
2020/10/21 09:53:18
The swagger spec at "swagger.yaml" is valid against swagger specification 2.0
  1. 合并swagger文档
$ swagger mixin swagger_part1.yaml swagger_part2.yaml