Skip to content

swag

swaggo/swag ist eine Implementierung von Swagger API 2.0 in Go. Durch das Schreiben von Kommentaren in einem bestimmten Format können API-Dokumentationen im swagger.json- und swagger.yaml-Format generiert werden, die einfach exportiert und importiert werden können.

Repository: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)

Dokumentation: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)

Swag unterstützt standardmäßig folgende Web-Frameworks. In diesem Artikel wird gin als Beispiel verwendet, um zu demonstrieren, wie gin in Kombination mit swagger schnell API-Dokumentationen generiert.

TIP

Wenn Sie mit der Swagger-Syntax nicht vertraut sind, besuchen Sie About Swagger Specification | Documentation | Swagger

Installation

Laden Sie zuerst das Swagger-Kommandozeilen-Tool herunter

go install github.com/swaggo/swag/cmd/swag@latest

Laden Sie dann die Swagger-Quellcode-Abhängigkeit herunter

go get github.com/swaggo/swag

TIP

Um Probleme zu vermeiden, müssen beide Versionen übereinstimmen.

Laden Sie dann die statische Dateibibliothek von Swagger herunter. HTML, CSS, JS usw. wurden in Go-Code eingebettet.

go get github.com/swaggo/files@latest

Laden Sie schließlich die Gin-Adapterbibliothek von Swagger herunter

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

Da dieser Artikel nur gin als Beispiel verwendet, informieren Sie sich bitte selbst über Adapter für andere Web-Frameworks. Sie sind im Grunde alle sehr ähnlich.

Verwendung

Erstellen Sie mit go mod ein grundlegendes Go-Projekt, erstellen Sie main.go und schreiben Sie folgenden Inhalt.

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

Dies ist ein sehr einfaches gin-Web-Beispiel. Die Kommentare über der main-Funktion sind die grundlegenden Informationen des Dokuments, und die Ping-Funktion ist eine normale Schnittstelle. Führen Sie als Nächstes den Befehl aus, um die Dokumentation zu generieren. Standardmäßig wird sie im docs-Verzeichnis auf derselben Ebene wie main.go erstellt.

swag init

Ändern Sie den Code in main.go

go
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
    // Anonymer Import des generierten API-Dokumentations-Pakets
    _ "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 statische Datei-Route registrieren
    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")),
    })
}

Führen Sie das Programm aus und rufen Sie 127.0.0.1/swagger/index.html auf. Die Oberfläche sieht wie folgt aus:

Damit wurde eine grundlegende API-Dokumentation gestartet. Abgesehen von einigen besonders zu beachtenden Punkten gibt es keinen großen Unterschied zur Verwendung in anderen Sprachen.

Parameter

Das Format zur Definition von Parametern ist:

@param name paramtype datatype isRequired comment

Ein Beispiel:

go
@param userId query int true "user unique id"

Die unterstützten Parametertypen sind:

  • query
  • path
  • header
  • body
  • formData

Die Datentypen sind:

  • string (string)
  • integer (int, uint, uint32, uint64)
  • number (float32)
  • boolean (bool)
  • user defined struct

Der Parametertyp kann auch Ihr eigener Typ sein, vorausgesetzt, er kann von Swagger gescannt werden.

Antwort

Das grundlegende Format zur Definition von Schnittstellenantworten:

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

Bestehend aus Statuscode, Basistyp und Datentyp. {array} bedeutet, dass es sich um ein Array handelt, und zeigt die Array-Form des Datentyps. {object} zeigt die ursprüngliche Form des Datentyps. Zum Beispiel definieren wir normalerweise einen einheitlichen Antwortkörper:

go
type JSONResult struct {
    Code    int          `json:"code" `
    Message string       `json:"message"`
    Data    interface{}  `json:"data"`
}

Der Typ des Data-Feldes ist unbestimmt. Bei der Beschreibung von Antwortbeispielen kann er wie folgt kombiniert werden:

go
// Kombination
@success 200 {object} jsonresult.JSONResult{data=Account} "desc"

// Array
@success 200 {object} jsonresult.JSONResult{data=[]Account} "desc"

Modelle

Kommentare zu Strukturfeldern werden von Swagger als Modellfeldkommentare gescannt:

go
package model

type Account struct {
  // account id
    ID   int    `json:"id" example:"1"`
    // username
    Name string `json:"name" example:"account name"`
}

Der Wert des example-Tags wird als Beispielwert auf der Seite angezeigt. Natürlich werden auch Feldbeschränkungen unterstützt:

go
type Foo struct {
    Bar string `minLength:"4" maxLength:"16"`
    Baz int `minimum:"10" maximum:"20" default:"15"`
    Qux []string `enums:"foo,bar,baz"`
}

Alle Modelle müssen bei der Verwendung sicherstellen, dass sie von Swagger gescannt werden können, sonst funktionieren sie nicht.

Authentifizierung

Im Bereich der Authentifizierung werden unterstützt:

  • Basic Auth
  • API Key
  • OAuth2 app auth
  • OAuth2 implicit auth
  • OAuth2 password auth
  • OAuth2 access code auth

Wenn die Schnittstellenauthentifizierung JWT verwendet und im Authorization-Feld des Headers gespeichert wird, können wir wie folgt definieren:

go
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization

Im Wesentlichen ist dies nur ein API-Key. Wenn Sie ein Bearer-Token übergeben, müssen Sie das Bearer-Präfix manuell hinzufügen.

Fügen Sie dann den folgenden Kommentar zu den Schnittstellen hinzu, die Authentifizierung erfordern:

go
// @security Bearer

Der Wert ist der Name, den Sie in securityDefinitions definiert haben.

Konfiguration

Swag speichert tatsächlich mehrere verschiedene Swagger-Instanzen in einer Map. ginSwagger dient als Adapter, um doc.json aus den Instanzen zu lesen, also die API-Schnittstellendefinitionsdatei. swaggerFiles stellt statische HTML-Dateien für die Anzeige der Webseite bereit, parst die API-Definition und generiert die Oberfläche. Wenn Sie den gesamten Prozess verstehen, können Sie benutzerdefinierte Operationen durchführen.

go
// Name is a unique name be used to register swag instance.
// Standardinstanzname
const Name = "swagger"

var (
  swaggerMu sync.RWMutex
    // Instanztabelle
  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"
    }

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

       // Routenabgleich
       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 {
       // Hauptseite
       case "index.html":
          _ = index.Execute(ctx.Writer, config.toSwaggerConfig())
       // API-Beschreibungsdatei
       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)
       }
    }
}

Durch den generierten Go-Code wird die Instanzregistrierung automatisch abgeschlossen. Unten steht ein Teil des Codes der automatisch generierten docs.go:

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() {
    // Instanz registrieren
  swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

Wie Sie sehen, gibt es in der init-Funktion eine Register-Funktion zum Registrieren der aktuellen Instanz. Wenn Sie den Instanznamen ändern möchten, wird nicht empfohlen, dies in dieser Datei zu tun, da docs.go automatisch generiert wird. Verwenden Sie einfach den Parameter --instanceName appapi beim Generieren des Codes. Zur Vereinfachung können Sie den go generate-Befehl einbetten, um den Code automatisch zu generieren:

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

Ich persönlich schreibe die allgemeinen Swagger-Informationskommentare nicht gerne in main.go oder auf die main-Funktion. Diese Kommentare über go generate zu schreiben, ist am besten geeignet.

TIP

Wenn Sie mehrere Instanzen benötigen, stellen Sie sicher, dass der Instanzname eindeutig ist, sonst tritt ein panic auf.

Für einige benutzerdefinierte Konfigurationen müssen Sie ginSwagger.CustomWrapHandler verwenden. Im Vergleich zum ersteren hat es einen zusätzlichen Config-Parameter mit folgender Bedeutung:

go
// 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
    // Schnittstellenlisten-Ausklappstatus
  DocExpansion             string
    // Instanzname
  InstanceName             string
    // Titel
  Title                    string
    // Ausklapptiefe
  DefaultModelsExpandDepth int
    // Der Name sagt alles
  DeepLinking              bool
  PersistAuthorization     bool
  Oauth2DefaultClientID    string
}

Verwenden Sie swaggerFiles.NewHandler() anstelle des Standard-Handlers, besonders bei mehreren Instanzen.

go
engine.GET(openapi.ApiDoc, ginSwagger.CustomWrapHandler(openapi.Config, swaggerFiles.NewHandler()))

Darüber hinaus sind auch Typ-Neuschreibungen und eine Reihe anderer Operationen möglich, die alle relativ einfach sind. Weitere Inhalte finden Sie in der offiziellen Dokumentation.

Hinweise

  1. Swag generiert OpenAPI-Schnittstellenbeschreibungsdateien basierend auf Kommentaren. Beim Generieren muss das angegebene Verzeichnis die grundlegenden Informationen der API-Dokumentation enthalten. Standardmäßig wird in main.go gesucht.

  2. swag init gibt standardmäßig das aktuelle Verzeichnis an, der Wert ist ./. Sie können mehrere Verzeichnisse mit swag init -d angeben, durch Kommas getrennt. Das erste angegebene Verzeichnis muss die grundlegenden Informationen der API-Dokumentation enthalten. Zum Beispiel:

    swag init -d ./,./api
  3. -g: Die Datei für die grundlegenden Informationen der API-Dokumentation kann angepasst werden. Standardmäßig ist es main.go. Verwenden Sie beim Generieren der Dokumentation den -g-Parameter, um den Dateinamen anzugeben:

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

    Dieser Befehl bedeutet: Parse die grundlegenden Informationen der API-Dokumentation in ./api.go und durchsuche gleichzeitig die Verzeichnisse ./ und ./api nach anderen Schnittstellenkommentaren, um die entsprechende Dokumentation zu generieren.

  4. Der -o-Parameter kann den Ausgabepfad der Dokumentbeschreibungsdateien angeben. Standardmäßig ist es ./docs. Zum Beispiel:

    swag init -o ./api/docs
  5. --ot kann den Ausgabedateityp angeben. Standardmäßig sind es (docs.go, swagger.json, swagger.yaml). Wenn Sie ein Go-Programm zum Laden von Swagger UI verwenden möchten, ist die Go-Datei unverzichtbar.

    swag init --ot go,yaml

    Die übrigen generierten JSON- und YAML-Dateien können einfach in andere API-Verwaltungssoftware importiert werden.

  6. Es ist egal, wo die Kommentare stehen. Selbst wenn sie nicht auf einer Funktion stehen, können sie geparst werden. Das Schreiben auf einer Funktion dient nur der besseren Lesbarkeit. Im Wesentlichen ist es eine DSL, die in Form von Kommentaren in den Go-Quellcode eingebettet ist.

  7. Swag unterstützt viele weitere Parameter. Verwenden Sie swag init -h, um sie anzuzeigen.

Golang by www.golangdev.cn edit