- tags: Go
目标
当跟随这篇文章完成后将产出如下内容:
- 代码 http://gitlab.17zuoye.net/vgo/go-swagger-example
- 文档 http://swagger.17zuoye.net/?url=http%3A%2F%2F10.200.242.61%3A9090%2Fswagger.json
准备
创建一个项目
$ mkdir go-swagger-example
$ cd go-swagger-example/
$ go mod init gitlab.17zuoye.net/vgo/go-swagger-example
开始使用
首先在你的 `main.go` 定义 go generate
像下面这样:
//go:generate swagger generate spec -o ./swagger.yml
package main
func main() {
println("Hello world!");
}
此时如果运行 go generate
在项目目录下就会生成一个 swagger.yml
文件:
paths: {}
swagger: "2.0"
使用单独的包托管 swagger 相关定义
在之前实践的过程中发现,如果在多个包中定义了相同名称的结构体会到只一个结构体覆盖另外一个结构体的定义。 所以为了解决这个问题,我把所有 swagger 相关的定义都放在同一个包下来避免相同名字的结构体。
创建 swagger/swagger.go
填充如下内容:
// Package swagger defines API documentation.
//
// Swagger 演示后端接口
//
// Schemes: http
// Host: 10.200.242.35:8080
// BasePath: /api/
// Version: 0.1.0
// Contact: 王会<hui.wang.a@17zuoye.com>
//
// Consumes:
// - application/json
//
// Produces:
// - application/vnd.17zuoye.v1+json
//
// swagger:meta
package swagger
上面文件通过注释来定义了一些接口相关的信息,包括:
Schemes
定义可用的协议Host
定义接口地址BasePath
定义接口基础路径Consumes
定义复杂请求的类型(可以覆盖)Produces
定义接口响应类型(可以覆盖)- 还有一些其他的信息,比如联系人等。
通过 swagger:meta
来结束声明。
此时我们再次运行 go generate
将会得到如下 swagger.yml
定义:
basePath: /api/
consumes:
- application/json
host: 10.200.242.35:8080
info:
contact:
email: hui.wang.a@17zuoye.com
name: 王会
description: Swagger 演示后端接口
title: defines API documentation.
version: 0.1.0
paths: {}
produces:
- application/vnd.17zuoye.v1+json
schemes:
- http
swagger: "2.0"
编写接口文档
准备 service 包
我们打算将接口实现相关代码放在 service
包下,首先来创建 service/service.go
:
package service
import (
"github.com/gin-gonic/gin"
)
var Engine = gin.Default()
var Router = Engine.Group("/api")
func init() {
// Mount handlers to gin here
}
POST 提交信息
假设我们编写一个创建用户信息的接口,需要名字和年龄两个参数。我们在 service
包下创建 user.go
package service
import (
"github.com/gin-gonic/gin"
)
func CreateUser(c *gin.Context) {
}
func init() {
// swagger:route POST /users users-create
//
// 创建用户。
//
// Responses:
// default: DefaultResponse
// 201: UserResponse
Router.POST("/users", CreateUser)
}
通过上面代码中的注释我们创建了一个 Swagger 中的 route
其 ID 是 users-create
。
我们在注释中也声明了响应,但是我们目前还没有定义参数和对应的响应。
Go Swagger 的参数定义是反向的,意思就是你需要定义一个 parameter
然后指明用在哪个 route
或
operation
上(通过对应的 ID)。下面就让我们一起来看一看,我们来创建 swagger/user.go
:
package swagger
// UserCreateForm 用于创建用户的表单,可以供 gin 使用
type UserCreateForm struct {
Username string `json:"username"`
Age int `json:"age"`
}
// UserCreateParams 声明 Swagger 参数生成文档
// swagger:parameters users-create
type UserCreateParams struct {
// in: body
Body UserCreateForm
}
// UserEntity user entity to respond
type UserEntity struct {
ID uint64 `json:"id"`
Username string `json:"username"`
Age int `json:"age"`
}
// UserResponse 声明 Swagger 响应用于文档生成
// swagger:response
type UserResponse struct {
// in: body
Body UserEntity
}
接下来调整 swagger/swagger.go
定义 DefaultResponse
:
// Package swagger defines API documentation.
//
// Swagger 演示后端接口
//
// Schemes: http
// Host: 10.200.242.35:8080
// BasePath: /api/
// Version: 0.1.0
// Contact: 王会<hui.wang.a@17zuoye.com>
//
// Consumes:
// - application/json
//
// Produces:
// - application/vnd.17zuoye.v1+json
//
// swagger:meta
package swagger
// Default default entity
type Default struct {
Code int `json:"code"`
Message string `json:"msg"`
}
// DefaultResponse 默认响应,用于 Swagger 文档生成
// swagger:response
type DefaultResponse struct {
// in: body
Body Default
}
现在我们运行 go generate
将会生成如下 swagger.yml
basePath: /api/
consumes:
- application/json
definitions:
Default:
description: Default default entity
properties:
code:
format: int64
type: integer
x-go-name: Code
msg:
type: string
x-go-name: Message
type: object
x-go-package: gitlab.17zuoye.net/vgo/go-swagger-example/swagger
UserEntity:
description: UserEntity user entity to respond
properties:
age:
format: int64
type: integer
x-go-name: Age
id:
format: uint64
type: integer
x-go-name: ID
username:
type: string
x-go-name: Username
type: object
x-go-package: gitlab.17zuoye.net/vgo/go-swagger-example/swagger
UserCreateForm:
description: UserCreateForm 用于创建用户的表单,可以供 gin 使用
properties:
age:
format: int64
type: integer
x-go-name: Age
username:
type: string
x-go-name: Username
type: object
x-go-package: gitlab.17zuoye.net/vgo/go-swagger-example/swagger
host: 10.200.242.35:8080
info:
contact:
email: hui.wang.a@17zuoye.com
name: 王会
description: Swagger 演示后端接口
title: defines API documentation.
version: 0.1.0
paths:
/users:
post:
operationId: users-create
parameters:
- in: body
name: Body
schema:
$ref: '#/definitions/UserCreateForm'
responses:
"201":
$ref: '#/responses/UserResponse'
default:
$ref: '#/responses/DefaultResponse'
summary: 创建用户。
produces:
- application/vnd.17zuoye.v1+json
responses:
DefaultResponse:
description: DefaultResponse 默认响应,用于 Swagger 文档生成
schema:
$ref: '#/definitions/Default'
UserResponse:
description: UserResponse 声明 Swagger 响应用于文档生成
schema:
$ref: '#/definitions/UserEntity'
schemes:
- http
swagger: "2.0"
运行 swagger serve -F swagger swagger.yml
可查看文档。
查询参数
接下来我们来编写查询接口文档,这次和上面创建稍有不同,参数通过 Query 进行传递,
编辑 service/user.go
package service
import (
"github.com/gin-gonic/gin"
)
func CreateUser(c *gin.Context) {
}
func QueryUsers(c *gin.Context) {
}
func init() {
// swagger:route POST /users users-create
//
// 创建用户。
//
// Responses:
// default: DefaultResponse
// 201: UserResponse
Router.POST("/users", CreateUser)
// swagger:route GET /users users-query
//
// 查询用户。
//
// Respones:
// default: DefaultResponse
// 200: UsersResponse
Router.GET("/users", QueryUsers)
}
接下来调整 swagger/user.go
定义参数和响应:
package swagger
// UserCreateForm 用于创建用户的表单,可以供 gin 使用
type UserCreateForm struct {
Username string `json:"username"`
Age int `json:"age"`
}
// UserCreateParams 声明 Swagger 参数生成文档
// swagger:parameters users-create
type UserCreateParams struct {
// in: body
Body UserCreateForm
}
// UserEntity user entity to respond
type UserEntity struct {
ID uint64 `json:"id"`
Username string `json:"username"`
Age int `json:"age"`
}
// UserResponse 声明 Swagger 响应用于文档生成
// swagger:response
type UserResponse struct {
// in: body
Body UserEntity
}
// UserQueryParams 声明 Swagger 参数生成文档
// swagger:parameters users-query
type UserQueryParams struct {
// json tag 用于 swagger
// in: query
Username string `json:"username" form:"username"`
}
// UsersResponse 用户列表响应
// swagger:response
type UsersResponse struct {
// in: body
Body struct {
Page int `json:"page"`
PerPage int `json:"perPage"`
Total int `json:"total"`
Users []UserEntity `json:"users"`
}
}
运行 go generate && swagger serve -F swagger swagger.yml
可查看效果。
URL 中 Path 参数
如果我们要更新用户信息,按照 RESTful 的设计方式,请求的方式应该是 PATCH /api/users/:id
,
此时 route
已经无法满足,需要借助 operation
编写一些原始的 YAML
来实现,下面是调整
后的 service/user.go
:
package service
import (
"github.com/gin-gonic/gin"
)
func CreateUser(c *gin.Context) {
}
func QueryUsers(c *gin.Context) {
}
func UpdateUser(c *gin.Context) {
}
func init() {
// swagger:route POST /users users-create
//
// 创建用户。
//
// Responses:
// default: DefaultResponse
// 201: UserResponse
Router.POST("/users", CreateUser)
// swagger:route GET /users users-query
//
// 查询用户。
//
// Responses:
// default: DefaultResponse
// 200: UsersResponse
Router.GET("/users", QueryUsers)
// swagger:operation PATCH /users/{userId} users-update
//
// 更新用户信息。
//
// ---
// parameters:
// - in: path
// name: userId
// type: int
// description: 用户 ID
// - in: body
// name: Body
// schema:
// "$ref": "#/definitions/UserCreateForm"
// respones:
// "200":
// "$ref": "#/responses/UserResponse"
// "default":
// "$ref": "#/responses/DefaultResponse"
Router.PATCH("/users/:id", UpdateUser)
}
部署
可以通过 CI/CD 生成 swagger.yml
进行部署,然后将对应的 JSON 地址结合公司 http://swagger.17zuoye.net/
进行部署查看。