API 文档

API 文档 #

使用 Swagger 生成 API 文档 #

Swagger 是基于 OpenAPI 规范的 API 文档工具。

OpenAPI 是一个 API 规范,它的前身就是 Swagger 规范,目前最新的 OpenAPI 规范是 OpenAPI 3.0(也就是 Swagger 2.0 规范)。

Swagger 编辑器 #

Swagger 编辑是一个在线的 API 文档编辑器,可以在其中编写 OpenAPI 规范,并实时预览 API 文档。

基于代码自动生成 Swagger 文档 #

Go 生成 Swagger 文档常用的工具有两个,分别是 swaggo-swagger

推荐使用 go-swagger:

  1. go-swagger 提供了更灵活、更多的功能来描述 API,可以生成客户端和服务器端代码。
  2. 使用 swag 的话,每一个 API 都需要有一个冗长的注释,有时候代码注释比代码还要长,但是通过 go-swagger 可以将代码和注释分开编写,可以使代码保持简洁,清晰易读,而且可以把 API 定义放在一个目录中,方便管理。

安装 go-swagger #

$ go get -u github.com/go-swagger/go-swagger/cmd/swagger
$ swagger version
version: v0.30.3
commit: ecf6f05b6ecc1b1725c8569534f133fa27e9de6b

命令格式为 swagger [OPTIONS] <command>

swagger 提供的子命令:

子命令描述
diff对比两个 swagger 文档的差异
expand展开 swagger 定义文档中的 $ref
flatten展平 swagger 文档
generate生成 swagger 文档,客户端,服务端代码
ini初始化一个 swagger 定义文档
mix合并 swagger 文档
serv启动 http 服务,用来查看 swagger 文档
validate验证 swagger 第一文件是否正确

使用 #

go-swagger 通过解析源码中的注释来生成 Swagger 文档。

注释语法:

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

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

package main

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

	"github.com/gin-gonic/gin"

	"github.com/shipengqi/idm/swagger/api"
	// This line is necessary for go-swagger to find your docs!
	_ "github.com/shipengqi/idm/swagger/docs"
)

var users []*api.User

func main() {
	r := gin.Default()
	r.POST("/users", Create)
	r.GET("/users/:name", Get)

	log.Fatal(r.Run(":8081"))
}

// Create a user.
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 user details.
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

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 说明字段是必须的。

另外一个 Go 包中编写带 go-swagger 注释的 API 文档。 先创建一个目录 swagger/docs。在 swagger/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

注意最后以 swagger:meta 注释结束。

编写完 doc.go 文件后, 编写 API 的定义文件 swagger/docs/user.go

package docs

import (
  v1 "github.com/shipengqi/idm/api/apiserver/v1"
  metav1 "github.com/shipengqi/idm/api/meta/v1"
)

// swagger:route GET /users/{name} Users getUserRequest
//
// Get details for specified user.
//
// Get details for specified user according to input parameters.
//
//     Responses:
//       default: errResponse
//       200: getUserResponse

// swagger:route GET /users Users listUserRequest
//
// List users.
//
// List users.
//
//     Responses:
//       default: errResponse
//       200: listUserResponse

// List users request.
// swagger:parameters listUserRequest
type listUserRequestParamsWrapper struct {
  // in:query
  metav1.ListOptions
}

// List users response.
// swagger:response listUserResponse
type listUserResponseWrapper struct {
  // in:body
  Body v1.UserList
}

// User response.
// swagger:response getUserResponse
type getUserResponseWrapper struct {
  // in:body
  Body v1.User
}

// swagger:parameters createUserRequest updateUserRequest
type userRequestParamsWrapper struct {
  // User information.
  // in:body
  Body v1.User
}

// swagger:parameters deleteUserRequest getUserRequest updateUserRequest
type userNameParamsWrapper struct {
  // Username.
  // in:path
  Name string `json:"name"`
}

// ErrResponse defines the return messages when an error occurred.
// swagger:response errResponse
type errResponseWrapper struct {
  // in:body
  Body response.Response
}

// Return nil json object.
// swagger:response okResponse
type okResponseWrapper struct{}
  • swagger:route:描述一个 API,格式为 swagger:route [method] [url path pattern] [?tag1 tag2 tag3] [operation id],tag 可以是多个,相同 tag 的 API 在 Swagger 文档中会被分为一组。operation id 会和 swagger:parameters 的定义进行匹配,就是该 API 的请求参数。 swagger:route 下面的一行是该 API 的描述,需要以 . 为结尾。responses: 定义了 API 的返回参数,例如当 HTTP 状态码是 200 时,返回 createUserResponsecreateUserResponse 会和 swagger:response 的定义进行匹配,匹配成功的 swagger:response 就是该 API 返回 200 状态码时的返回。
  • swagger:response:定义了 API 的返回,格式为 swagger:response [?response name].例如 getUserResponseWrapper 中有一个 Body 字段,其注释为 // in:body,说明该参数是在 HTTP Body 中返回。 swagger:response 上的注释是 response 的描述。api.User 会被 go-swagger 解析为 Example Value 和 Model,不需要重复编写。
  • swagger:parameters:定义了 API 的请求参数,格式为 swagger:parameters [operationid1 operationid2] 。例如 userRequestParamsWrapperuserRequestParamsWrapper 上的注释是请求参数的描述。

进入 swagger 目录,执行如下命令,生成 Swagger API 文档:

$ swagger generate spec -o swagger.yaml
  • -o:指定要输出的文件名。swagger 会根据文件名后缀 .yaml 或者 .json,决定生成的文件格式为 YAML 或 JSON。

启动 HTTP 服务:

$ swagger serve --no-open -F=redoc --port 36666 swagger.yaml
  • –no-open-–no-open 禁止调用浏览器打开 URL。
  • -F:指定文档的风格,可选 swaggerredocredoc 格式更加易读和清晰。
  • –port:指定启动的 HTTP 服务监听端口。

在浏览器查看 API 文档:

swagger-redoc

还可以使用下面的命令将生成的 swagger.yaml 转换为 swagger.json

$ swagger generate spec -i ./swagger.yaml -o ./swagger.json