swag
swaggo/swag 是 Swagger API 2.0 在 go 語言中的一個實現,通過在書寫指定格式的注釋就可以生成swagger.json和swagger.yaml類型的接口文檔,方便導出和導入。
倉庫:swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
文檔:swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
swag 默認支持的 web 框架如下所示,本文以 gin 為例子,來演示 gin 結合 swagger 快速生成接口文檔的例子。
TIP
如果不熟悉 swagger 語法,可以前往About Swagger Specification | Documentation | Swagger
安裝
首先下載 swagger 命令行工具
go install github.com/swaggo/swag/cmd/swag@latest然後下載 swagger 源碼依賴
go get github.com/swaggo/swagTIP
為避免出現問題,兩者版本必須保持一致。
然後下載 swagger 的靜態文件庫,html,css,js 之類的,都被嵌到了 go 代碼中。
go get github.com/swaggo/files@latest最後下載 swagger 的 gin 適配庫
go get github.com/swaggo/gin-swagger@latest因為本文是只用 gin 做示例,其他 web 框架的適配器請自行了解,基本都是大同小異。
使用
使用 go mod 創建一個最基本的 go 項目,新建main.go,寫入如下內容。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
// @title Swagger Example API
// @version 1.0
// @description This is a sample server celler server.
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @BasePath /api/v1
func main() {
engine := gin.Default()
engine.GET("/api/v1/ping", Ping)
engine.Run(":80")
}
// Ping godoc
// @Summary say hello world
// @Description return hello world json format content
// @param name query string true "name"
// @Tags system
// @Produce json
// @Router /ping [get]
func Ping(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": fmt.Sprintf("Hello World!%s", ctx.Query("name")),
})
}這是一個很簡單的 gin web 例子,main 函數上的注釋是文檔的基本信息,Ping 函數則是一個普通的接口。接下來執行命令生成文檔,默認是在main.go同級的 docs 目錄下
swag init修改main.go代碼
package main
import (
"fmt"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
// 匿名導入生成的接口文檔包
_ "golearn/docs"
)
// @title Swagger Example API
// @version 1.0
// @description This is a sample server celler server.
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @BasePath /api/v1
func main() {
engine := gin.Default()
// 注冊swagger靜態文件路由
engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
engine.GET("/api/v1/ping", Ping)
engine.Run(":80")
}
// Ping godoc
// @Summary say hello world
// @Description return hello world json format content
// @param name query string true "name"
// @Tags system
// @Produce json
// @Router /ping [get]
func Ping(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": fmt.Sprintf("Hello World!%s", ctx.Query("name")),
})
}運行程序,訪問127.0.0.1/swagger/index.html,界面如下

如此便運行起了一個基本的接口文檔。接下來除了一些特別要注意的點,基本上和其他語言使用起來沒有什麼太大的差別。
參數
定義參數的格式為
@param name paramtype datatype isRequired comment一個例子如下
@param userId query int true "user unique id"其中支持的參數類型有
- query
- path
- header
- body
- formData
數據類型有
- string (string)
- integer (int, uint, uint32, uint64)
- number (float32)
- boolean (bool)
- user defined struct
參數類型也可以是你自己的類型,前提是能夠被 swagger 掃描到。
響應
定義接口響應的基本格式如下
// @Success 200 {array} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError由狀態碼,基本類型,數據類型組成。{array}表示是一個數組,會展現數據類型的數組形式,{object}就會展現數據類型的原有形式。比如一般我們會定義一個統一的響應體
type JSONResult struct {
Code int `json:"code" `
Message string `json:"message"`
Data interface{} `json:"data"`
}Data字段的類型是不確定的,在描述響應用例時,可以將其組合,如下
// 組合
@success 200 {object} jsonresult.JSONResult{data=Account} "desc"
// 數組
@success 200 {object} jsonresult.JSONResult{data=[]Account} "desc"模型
給結構體字段加注釋會被被 swagger 掃描為模型字段注釋
package model
type Account struct {
// account id
ID int `json:"id" example:"1"`
// username
Name string `json:"name" example:"account name"`
}其中example標簽的值會被作為示例值在頁面中展示,當然它還支持字段限制
type Foo struct {
Bar string `minLength:"4" maxLength:"16"`
Baz int `minimum:"10" maximum:"20" default:"15"`
Qux []string `enums:"foo,bar,baz"`
}所有的模型在使用時都要確保能被 swagger 掃描到,否則不會起作用。
認證
在認證這塊支持
- Basic Auth
- API Key
- OAuth2 app auth
- OAuth2 implicit auth
- OAuth2 password auth
- OAuth2 access code auth
假如接口認證用的是 JWT,存放在 header 中的Authorization字段中,我們可以如下定義
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
本質上這只是一個 apikey,如果你傳入的是 bearer token 的話,需要自己手動加上 Bearer 前綴。

然後在你需要認證的接口上加上如下注釋
// @security Bearer它的值是你的securityDefinitions定義的名稱。
配置
swag 實際上是將多個不同的 swagger 實例存放在一個 map 中,ginSwagger 作為適配器從實例中讀取doc.json也就是 API 接口的定義文件,swaggerFiles 提供靜態的 HTML 文件用於展示網頁,解析 API 定義並生成界面,整個流程明白以後,就可以進行自定義的操作了。
// Name is a unique name be used to register swag instance.
// 默認實例名稱
const Name = "swagger"
var (
swaggerMu sync.RWMutex
// 實例表
swags map[string]Swagger
)func CustomWrapHandler(config *Config, handler *webdav.Handler) gin.HandlerFunc {
var once sync.Once
if config.InstanceName == "" {
config.InstanceName = swag.Name
}
if config.Title == "" {
config.Title = "Swagger UI"
}
// create a template with name
index, _ := template.New("swagger_index.html").Parse(swaggerIndexTpl)
var matcher = regexp.MustCompile(`(.*)(index\.html|doc\.json|favicon-16x16\.png|favicon-32x32\.png|/oauth2-redirect\.html|swagger-ui\.css|swagger-ui\.css\.map|swagger-ui\.js|swagger-ui\.js\.map|swagger-ui-bundle\.js|swagger-ui-bundle\.js\.map|swagger-ui-standalone-preset\.js|swagger-ui-standalone-preset\.js\.map)[?|.]*`)
return func(ctx *gin.Context) {
if ctx.Request.Method != http.MethodGet {
ctx.AbortWithStatus(http.StatusMethodNotAllowed)
return
}
// 路由匹配
matches := matcher.FindStringSubmatch(ctx.Request.RequestURI)
if len(matches) != 3 {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return
}
path := matches[2]
once.Do(func() {
handler.Prefix = matches[1]
})
switch filepath.Ext(path) {
case ".html":
ctx.Header("Content-Type", "text/html; charset=utf-8")
case ".css":
ctx.Header("Content-Type", "text/css; charset=utf-8")
case ".js":
ctx.Header("Content-Type", "application/javascript")
case ".png":
ctx.Header("Content-Type", "image/png")
case ".json":
ctx.Header("Content-Type", "application/json; charset=utf-8")
}
switch path {
// 主頁
case "index.html":
_ = index.Execute(ctx.Writer, config.toSwaggerConfig())
// API描述文件
case "doc.json":
doc, err := swag.ReadDoc(config.InstanceName)
if err != nil {
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
ctx.String(http.StatusOK, doc)
default:
handler.ServeHTTP(ctx.Writer, ctx.Request)
}
}
}通過生成的 go 代碼來自動完成實例注冊,下方是自動生成的docs.go的部分代碼
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "",
Description: "",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
// 注冊實例
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}可以看到在init函數中有一個 Register 函數用來注冊當前實例,如果想要修改實例名稱不建議在該文件進行編輯,因為docs.go文件是自動生成的,只需要在生成代碼時使用--instanceName appapi參數。為了方便,可以使用 go generate 命令嵌入的到 go 文件中,方便自動生成代碼,如下。
// swagger declarative api comment
// @title App Internal API Documentation
// @version v1.0.0
// @description Wilson api documentation
// @BasePath /api/v1
//go:generate swag init --generatedTime --instanceName appapi -g api.go -d ./ --output ./swagger個人並不喜歡將 swagger 的通用信息注釋寫在main.go或main函數上,將這些注釋寫在go generate上方最合適不過。
TIP
如果需要多個實例,務必保持實例名稱唯一,否則會panic
為了定制化一些配置,需要用ginSwagger.CustomWrapHandler,它相比前者多了一個 Config 參數,釋義如下
// Config stores ginSwagger configuration variables.
type Config struct {
// The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`.
URL string
// 接口列表展開狀態
DocExpansion string
// 實例名稱
InstanceName string
// 標題
Title string
// 展開深度
DefaultModelsExpandDepth int
// 顧名思義
DeepLinking bool
PersistAuthorization bool
Oauth2DefaultClientID string
}使用swaggerFiles.NewHandler()來替代默認的 Handler,在多個實例時尤其要如此。
engine.GET(openapi.ApiDoc, ginSwagger.CustomWrapHandler(openapi.Config, swaggerFiles.NewHandler()))除此之外還可以進行類型重寫等一系列操作,都是比較簡單的,更多內容可以閱讀官方文檔。
注意事項
swag 是根據注釋來生成 openapi 的接口描述文件的,在生成時,指定的目錄必須要包含接口文檔的基本信息,默認是在
main.go裡面查找swag init默認指定的是當前目錄,值為./,可以使用swag init -d指定多個目錄,使用逗號分隔,第一個指定的目錄必須包含接口文檔的基本信息。例如swag init -d ./,./api-g,接口文檔的基本信息的存放文件可以自定義文件名,默認是main.go,在生成文檔時,使用-g參數指定文件名swag init -g api.go -d ./,./api該命令的意思是在
./api.go解析接口文檔的基本信息,同時在./和./api兩個目錄下查找和解析其他接口的注釋信息並生成對應的文檔。-o參數可以指定文檔描述文件的輸出路徑,默認是./docs,例:swag init -o ./api/docs--ot可以指定輸出文件類型,默認是(docs.go,swagger.json,swagger.yaml),如果想要使用 go 程序加載 swagger ui,go 文件是必不可少的。swag init --ot go,yaml其余生成的 json 和 yaml 文件可以方便在其他接口管理軟件上導入數據。
注釋寫在哪裡都一樣,就算不寫在函數上也一樣能解析,只是寫在函數上可讀性好一些,本質上還是一個以注釋形式和 go 源代碼嵌在一起的 DSL。
swag 還支持很多其他的參數,可以使用
swag init -h查看。
