swag
swaggo/swag è un'implementazione di Swagger API 2.0 nel linguaggio Go, che genera documenti API swagger.json e swagger.yaml scrivendo commenti in un formato specifico, facilitando l'esportazione e l'importazione.
Repository: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
Documentazione: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
swag supporta di default i seguenti framework web, questo articolo usa gin come esempio per dimostrare come generare rapidamente documenti API con gin e swagger.
TIP
Se non si ha familiarità con la sintassi swagger, si può consultare About Swagger Specification | Documentation | Swagger
Installazione
Per prima cosa scaricare lo strumento da riga di comando swagger
go install github.com/swaggo/swag/cmd/swag@latestPoi scaricare le dipendenze del codice sorgente swagger
go get github.com/swaggo/swagTIP
Per evitare problemi, le versioni di entrambi devono essere coerenti.
Poi scaricare la libreria di file statici swagger, html, css, js, ecc., tutti incorporati nel codice Go.
go get github.com/swaggo/files@latestInfine scaricare la libreria di adattamento gin di swagger
go get github.com/swaggo/gin-swagger@latestPoiché questo articolo usa solo gin come esempio, gli adattatori per altri framework web possono essere consultati autonomamente, sono fondamentalmente simili.
Utilizzo
Usare go mod per creare un progetto Go più basilare, creare main.go, scrivere il seguente contenuto.
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")),
})
}Questo è un semplice esempio di gin web, i commenti sulla funzione main sono le informazioni di base del documento, Ping è un'interfaccia ordinaria. Successivamente eseguire il comando per generare il documento, di default è nella directory docs allo stesso livello di main.go
swag initModificare il codice main.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
// Import anonimo del pacchetto documenti API generato
_ "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 route file statici 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")),
})
}Eseguire il programma, accedere a 127.0.0.1/swagger/index.html, l'interfaccia è la seguente

In questo modo è stato avviato un documento API di base. Successivamente, a parte alcuni punti particolarmente importanti, fondamentalmente non ci sono grandi differenze rispetto all'uso in altri linguaggi.
Parametri
Il formato per definire i parametri è
@param name paramtype datatype isRequired commentUn esempio è il seguente
@param userId query int true "user unique id"I tipi di parametri supportati sono
- query
- path
- header
- body
- formData
I tipi di dati sono
- string (string)
- integer (int, uint, uint32, uint64)
- number (float32)
- boolean (bool)
- struct definita dall'utente
Il tipo di parametro può anche essere il proprio tipo, purché possa essere scansionato da swagger.
Risposta
Il formato di base per definire la risposta dell'interfaccia è il seguente
// @Success 200 {array} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPErrorComposto da codice di stato, tipo di base, tipo di dati. {array} indica che è un array, mostrerà la forma array del tipo di dati, {object} mostrerà la forma originale del tipo di dati. Ad esempio, di solito definiamo un corpo di risposta unificato
type JSONResult struct {
Code int `json:"code" `
Message string `json:"message"`
Data interface{} `json:"data"`
}Il tipo del campo Data è incerto, quando si descrivono casi di risposta, può essere combinato, come segue
// Combinazione
@success 200 {object} jsonresult.JSONResult{data=Account} "desc"
// Array
@success 200 {object} jsonresult.JSONResult{data=[]Account} "desc"Modelli
Aggiungere commenti ai campi della struct verrà scansionato da swagger come commenti dei campi del modello
package model
type Account struct {
// account id
ID int `json:"id" example:"1"`
// username
Name string `json:"name" example:"account name"`
}Il valore del tag example verrà visualizzato come valore di esempio nella pagina, naturalmente supporta anche limitazioni di campo
type Foo struct {
Bar string `minLength:"4" maxLength:"16"`
Baz int `minimum:"10" maximum:"20" default:"15"`
Qux []string `enums:"foo,bar,baz"`
}Tutti i modelli devono essere scansionabili da swagger quando vengono utilizzati, altrimenti non avranno effetto.
Autenticazione
Per l'autenticazione supporta
- Basic Auth
- API Key
- OAuth2 app auth
- OAuth2 implicit auth
- OAuth2 password auth
- OAuth2 access code auth
Se l'autenticazione dell'interfaccia usa JWT, memorizzata nel campo Authorization dell'header, possiamo definire come segue
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
Essenzialmente questa è solo un'apikey, se si trasmette un bearer token, è necessario aggiungere manualmente il prefisso Bearer.

Poi sulle interfacce che richiedono autenticazione aggiungere il seguente commento
// @security BearerIl suo valore è il nome definito nel tuo securityDefinitions.
Configurazione
swag memorizza effettivamente diverse istanze swagger in una map, ginSwagger come adattatore legge doc.json dall'istanza, ovvero il file di definizione delle API, swaggerFiles fornisce file HTML statici per visualizzare la pagina web, analizza la definizione API e genera l'interfaccia, dopo aver compreso l'intero processo, è possibile eseguire operazioni personalizzate.
// Name è un nome unico usato per registrare l'istanza swag.
// Nome istanza predefinito
const Name = "swagger"
var (
swaggerMu sync.RWMutex
// Tabella istanze
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"
}
// crea un template con 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
}
// Corrispondenza route
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 {
// Homepage
case "index.html":
_ = index.Execute(ctx.Writer, config.toSwaggerConfig())
// File descrizione 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)
}
}
}Completare automaticamente la registrazione dell'istanza tramite il codice Go generato, di seguito è parte del codice docs.go generato automaticamente
// SwaggerInfo contiene informazioni Swagger esportate in modo che i client possano modificarle
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "",
Description: "",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
// Registra istanza
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}Si può vedere che nella funzione init c'è una funzione Register usata per registrare l'istanza corrente, se si desidera modificare il nome dell'istanza non è consigliabile modificare questo file, perché il file docs.go è generato automaticamente, basta usare il parametro --instanceName appapi durante la generazione del codice. Per comodità, è possibile incorporare il comando go generate nei file Go per facilitare la generazione automatica del codice, come 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 ./swaggerPersonalmente non mi piace scrivere i commenti generici di swagger su main.go o sulla funzione main, scrivere questi commenti sopra go generate è più appropriato.
TIP
Se sono necessarie più istanze, assicurarsi che i nomi delle istanze siano unici, altrimenti si verificherà panic
Per personalizzare alcune configurazioni, è necessario usare ginSwagger.CustomWrapHandler, che rispetto al precedente ha un parametro Config in più, la definizione è la seguente
// Config memorizza le variabili di configurazione ginSwagger.
type Config struct {
// L'URL che punta alla definizione API (normalmente swagger.json o swagger.yaml). Il default è `doc.json`.
URL string
// Stato espansione elenco interfacce
DocExpansion string
// Nome istanza
InstanceName string
// Titolo
Title string
// Profondità espansione
DefaultModelsExpandDepth int
// Come suggerisce il nome
DeepLinking bool
PersistAuthorization bool
Oauth2DefaultClientID string
}Usare swaggerFiles.NewHandler() per sostituire l'Handler predefinito, specialmente quando ci sono più istanze.
engine.GET(openapi.ApiDoc, ginSwagger.CustomWrapHandler(openapi.Config, swaggerFiles.NewHandler()))Oltre a questo è possibile eseguire una serie di operazioni come la riscrittura dei tipi, ecc., sono tutte abbastanza semplici, per ulteriori contenuti si può leggere la documentazione ufficiale.
Note
swag genera il file di descrizione API openapi dai commenti, durante la generazione, la directory specificata deve contenere le informazioni di base del documento API, di default vengono cercate in
main.goswag initdi default specifica la directory corrente, il valore è./, è possibile usareswag init -dper specificare più directory, separate da virgole, la prima directory specificata deve contenere le informazioni di base del documento API. Ad esempioswag init -d ./,./api-g, il file di memorizzazione delle informazioni di base del documento API può personalizzare il nome del file, di default èmain.go, durante la generazione del documento, usare il parametro-gper specificare il nome del fileswag init -g api.go -d ./,./apiIl significato di questo comando è analizzare le informazioni di base del documento API in
./api.go, e allo stesso tempo cercare e analizzare i commenti di altre interfacce nelle directory./e./apie generare i documenti corrispondenti.Il parametro
-opuò specificare il percorso di output del file di descrizione del documento, di default è./docs, ad esempio:swag init -o ./api/docs--otpuò specificare il tipo di file di output, di default è (docs.go,swagger.json,swagger.yaml), se si desidera usare il programma Go per caricare swagger ui, il file go è essenziale.swag init --ot go,yamlI restanti file json e yaml generati possono facilitare l'importazione dei dati su altri software di gestione interfacce.
Non importa dove vengono scritti i commenti, anche se non scritti sulla funzione possono essere analizzati, è solo che scriverli sulla funzione ha una migliore leggibilità, essenzialmente è ancora un DSL incorporato nei commenti e nel codice sorgente Go.
swag supporta molti altri parametri, è possibile usare
swag init -hper visualizzare.
