swag
swaggo/swag é uma implementação da Swagger API 2.0 na linguagem Go, gerando documentos de interface nos formatos swagger.json e swagger.yaml através de comentários em formato específico, facilitando exportação e importação.
Repositório: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
Documentação: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
O swag suporta por padrão os seguintes frameworks web listados abaixo, este artigo usa gin como exemplo para demonstrar como gerar rapidamente documentação de API com gin e swagger.
TIP
Se não estiver familiarizado com a sintaxe swagger, consulte About Swagger Specification | Documentation | Swagger
Instalação
Primeiro, baixe a ferramenta de linha de comando swagger
go install github.com/swaggo/swag/cmd/swag@latestEm seguida, baixe as dependências do código-fonte swagger
go get github.com/swaggo/swagTIP
Para evitar problemas, as versões de ambos devem ser consistentes.
Em seguida, baixe a biblioteca de arquivos estáticos swagger, html, css, js, etc., todos incorporados no código Go.
go get github.com/swaggo/files@latestFinalmente, baixe a biblioteca de adaptação swagger para gin
go get github.com/swaggo/gin-swagger@latestComo este artigo usa apenas gin como exemplo, adaptadores para outros frameworks web devem ser consultados individualmente, basicamente são muito similares.
Uso
Crie um projeto Go mais básico usando go mod, crie main.go e escreva o seguinte conteúdo.
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")),
})
}Este é um exemplo simples de gin web, os comentários na função main são informações básicas do documento, a função Ping é uma interface comum. Em seguida, execute o comando para gerar o documento, por padrão está no diretório docs no mesmo nível de main.go
swag initModifique o código main.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
// importação anônima do pacote de documentação de interface gerado
_ "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()
// registra rota de arquivos estáticos 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")),
})
}Execute o programa e acesse 127.0.0.1/swagger/index.html, a interface é a seguinte

Assim, um documento de interface básico foi executado. A seguir, exceto por alguns pontos especialmente importantes, basicamente não há muita diferença em relação ao uso em outras linguagens.
Parâmetros
O formato para definir parâmetros é
@param name paramtype datatype isRequired commentUm exemplo é o seguinte
@param userId query int true "user unique id"Os tipos de parâmetro suportados são
- query
- path
- header
- body
- formData
Os tipos de dados são
- string (string)
- integer (int, uint, uint32, uint64)
- number (float32)
- boolean (bool)
- struct definido pelo usuário
O tipo de parâmetro também pode ser seu próprio tipo, desde que possa ser escaneado pelo swagger.
Resposta
O formato básico para definir resposta de interface é o seguinte
// @Success 200 {array} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPErrorComposto por código de status, tipo básico e tipo de dados. {array} indica que é um array e mostrará a forma de array do tipo de dados, {object} mostrará a forma original do tipo de dados. Por exemplo, geralmente definimos um corpo de resposta unificado
type JSONResult struct {
Code int `json:"code" `
Message string `json:"message"`
Data interface{} `json:"data"`
}O tipo do campo Data é incerto, ao descrever casos de uso de resposta, pode combiná-lo, como segue
// combinação
@success 200 {object} jsonresult.JSONResult{data=Account} "desc"
// array
@success 200 {object} jsonresult.JSONResult{data=[]Account} "desc"Modelos
Adicionar comentários a campos de struct será escaneado pelo swagger como comentários de campo de modelo
package model
type Account struct {
// account id
ID int `json:"id" example:"1"`
// username
Name string `json:"name" example:"account name"`
}O valor da tag example será exibido como valor de exemplo na página, claro, também suporta limitações de campo
type Foo struct {
Bar string `minLength:"4" maxLength:"16"`
Baz int `minimum:"10" maximum:"20" default:"15"`
Qux []string `enums:"foo,bar,baz"`
}Todos os modelos devem ser escaneados pelo swagger ao usar, caso contrário não funcionarão.
Autenticação
Na parte de autenticação, suporta
- Basic Auth
- API Key
- OAuth2 app auth
- OAuth2 implicit auth
- OAuth2 password auth
- OAuth2 access code auth
Se a autenticação de interface usar JWT, armazenada no campo Authorization do header, podemos definir da seguinte forma
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
Essencialmente, isso é apenas uma apikey, se você transmitir um bearer token, precisará adicionar manualmente o prefixo Bearer.

Em seguida, adicione os seguintes comentários nas interfaces que requerem autenticação
// @security BearerSeu valor é o nome definido em seu securityDefinitions.
Configuração
O swag na verdade armazena múltiplas instâncias swagger diferentes em um map, ginSwagger como adaptador lê doc.json do arquivo de definição de API, swaggerFiles fornece arquivos HTML estáticos para exibição de páginas, analisa a definição de API e gera a interface. Após entender todo o fluxo, é possível realizar operações customizadas.
// Name é um nome único usado para registrar instância swag.
// nome de instância padrão
const Name = "swagger"
var (
swaggerMu sync.RWMutex
// tabela de instâncias
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"
}
// cria um template com nome
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
}
// correspondência de rota
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 {
// página principal
case "index.html":
_ = index.Execute(ctx.Writer, config.toSwaggerConfig())
// arquivo de descrição de 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)
}
}
}Através do código Go gerado automaticamente para completar o registro de instância, abaixo está parte do código de docs.go gerado automaticamente
// SwaggerInfo mantém informações Swagger exportadas para que clientes possam modificá-las
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "",
Description: "",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
// registra instância
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}Pode-se ver que há uma função Register na função init usada para registrar a instância atual, se desejar modificar o nome da instância, não é recomendado editar neste arquivo, pois o arquivo docs.go é gerado automaticamente, basta usar o parâmetro --instanceName appapi ao gerar o código. Para conveniência, pode-se usar o comando go generate embutido em arquivos Go para facilitar a geração automática de código, como segue.
// 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 ./swaggerPessoalmente, não gosto de escrever comentários de informações genéricas do swagger em main.go ou na função main, escrever esses comentários acima de go generate é mais apropriado.
TIP
Se precisar de múltiplas instâncias, certifique-se de que os nomes das instâncias sejam únicos, caso contrário ocorrerá panic
Para customizar algumas configurações, é necessário usar ginSwagger.CustomWrapHandler, que possui um parâmetro Config a mais em relação ao anterior, com a seguinte definição
// Config armazena variáveis de configuração ginSwagger.
type Config struct {
// A URL apontando para definição de API (normalmente swagger.json ou swagger.yaml). Padrão é `doc.json`.
URL string
// estado de expansão da lista de interfaces
DocExpansion string
// nome da instância
InstanceName string
// título
Title string
// profundidade de expansão
DefaultModelsExpandDepth int
// autoexplicativo
DeepLinking bool
PersistAuthorization bool
Oauth2DefaultClientID string
}Use swaggerFiles.NewHandler() para substituir o Handler padrão, especialmente ao usar múltiplas instâncias.
engine.GET(openapi.ApiDoc, ginSwagger.CustomWrapHandler(openapi.Config, swaggerFiles.NewHandler()))Além disso, é possível realizar uma série de operações como reescrita de tipos, todas bastante simples, mais conteúdo pode ser consultado na documentação oficial.
Notas
O swag gera o arquivo de descrição de interface openapi baseado em comentários, ao gerar, o diretório especificado deve conter as informações básicas do documento de interface, por padrão é procurado em
main.goswag initpor padrão especifica o diretório atual, valor é./, pode usarswag init -dpara especificar múltiplos diretórios, separados por vírgula, o primeiro diretório especificado deve conter as informações básicas do documento de interface. Por exemploswag init -d ./,./api-g, o arquivo de armazenamento das informações básicas do documento de interface pode ser customizado, por padrão émain.go, ao gerar o documento, use o parâmetro-gpara especificar o nome do arquivoswag init -g api.go -d ./,./apiEste comando significa analisar as informações básicas do documento de interface em
./api.go, e ao mesmo tempo procurar e analisar outras informações de comentários de interface nos diretórios./e./apipara gerar os documentos correspondentes.O parâmetro
-opode especificar o caminho de saída do arquivo de descrição do documento, por padrão é./docs, exemplo:swag init -o ./api/docs--otpode especificar o tipo de arquivo de saída, por padrão é (docs.go,swagger.json,swagger.yaml), se desejar carregar swagger ui com programa Go, o arquivo Go é essencial.swag init --ot go,yamlOs arquivos json e yaml gerados podem facilitar a importação de dados em outros softwares de gerenciamento de interface.
Não importa onde os comentários sejam escritos, mesmo que não sejam escritos em funções, ainda podem ser analisados, apenas escrever em funções tem melhor legibilidade, essencialmente é uma DSL embutida junto com comentários e código-fonte Go.
O swag suporta muitos outros parâmetros, pode usar
swag init -hpara consultar.
