Skip to content

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@latest

Poi scaricare le dipendenze del codice sorgente swagger

go get github.com/swaggo/swag

TIP

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@latest

Infine scaricare la libreria di adattamento gin di swagger

go get github.com/swaggo/gin-swagger@latest

Poiché 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.

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")),
  })
}

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 init

Modificare il codice main.go

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 comment

Un esempio è il seguente

go
@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

go
// @Success      200  {array}   model.Account
// @Failure      400  {object}  httputil.HTTPError
// @Failure      404  {object}  httputil.HTTPError
// @Failure      500  {object}  httputil.HTTPError

Composto 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

go
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

go
// 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

go
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

go
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

go
// @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

go
// @security Bearer

Il 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.

go
// 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
)
go
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

go
// 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.

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

Personalmente 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

go
// 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.

go
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

  1. 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.go

  2. swag init di default specifica la directory corrente, il valore è ./, è possibile usare swag init -d per specificare più directory, separate da virgole, la prima directory specificata deve contenere le informazioni di base del documento API. Ad esempio

    swag init -d ./,./api
  3. -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 -g per specificare il nome del file

    swag init -g api.go -d ./,./api

    Il 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 ./api e generare i documenti corrispondenti.

  4. Il parametro -o può specificare il percorso di output del file di descrizione del documento, di default è ./docs, ad esempio:

    swag init -o ./api/docs
  5. --ot può 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,yaml

    I restanti file json e yaml generati possono facilitare l'importazione dei dati su altri software di gestione interfacce.

  6. 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.

  7. swag supporta molti altri parametri, è possibile usare swag init -h per visualizzare.

Golang by www.golangdev.cn edit