Skip to content

swag

swaggo/swag est une implémentation de Swagger API 2.0 pour le langage Go. En écrivant des commentaires dans un format spécifique, on peut générer des documents d'interface de type swagger.json et swagger.yaml, faciles à exporter et importer.

Dépôt : swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)

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

Les frameworks web supportés par défaut par swag sont listés ci-dessous. Cet article utilise gin comme exemple pour démontrer la génération rapide de documentation d'API avec gin combiné à swagger.

TIP

Si vous n'êtes pas familier avec la syntaxe swagger, vous pouvez consulter About Swagger Specification | Documentation | Swagger

Installation

D'abord, télécharger l'outil en ligne de commande swagger

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

Ensuite, télécharger la dépendance du code source swagger

go get github.com/swaggo/swag

TIP

Pour éviter les problèmes, les deux versions doivent rester identiques.

Puis télécharger la bibliothèque de fichiers statiques swagger. Les fichiers HTML, CSS, JS, etc. sont intégrés dans le code Go.

go get github.com/swaggo/files@latest

Enfin, télécharger la bibliothèque d'adaptation swagger pour gin

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

Comme cet article utilise uniquement gin comme exemple, veuillez vous renseigner vous-même pour les adaptateurs d'autres frameworks web, le principe reste globalement le même.

Utilisation

Utiliser go mod pour créer un projet Go basique, créer un nouveau fichier main.go, et y écrire le contenu suivant.

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

C'est un exemple très simple de gin web. Les commentaires au-dessus de la fonction main sont les informations de base du document, et la fonction Ping est une interface ordinaire. Ensuite, exécutez la commande pour générer le document, par défaut dans le répertoire docs au même niveau que main.go.

swag init

Modifier le code de main.go

go
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
    // Import anonyme du package de documentation d'API généré
    _ "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()
    // Enregistrer la route de fichiers statiques 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")),
    })
}

Lancer le programme, accéder à 127.0.0.1/swagger/index.html, l'interface s'affiche comme suit

Ainsi, une documentation d'API basique est opérationnelle. Ensuite, à part quelques points particuliers à noter, l'utilisation est globalement similaire à celle dans d'autres langages.

Paramètres

Le format pour définir les paramètres est

@param name paramtype datatype isRequired comment

Un exemple est le suivant

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

Les types de paramètres supportés sont

  • query
  • path
  • header
  • body
  • formData

Les types de données sont

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

Le type de paramètre peut aussi être votre propre type, à condition qu'il puisse être scanné par swagger.

Réponses

Le format de base pour définir les réponses d'interface est le suivant

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

Composé du code de statut, du type de base et du type de données. {array} indique un tableau, qui affichera la forme tableau du type de données, {object} affichera la forme originale du type de données. Par exemple, nous définissons généralement un corps de réponse unifié

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

Le type du champ Data est incertain, lors de la description de la réponse, vous pouvez le combiner comme suit

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

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

Modèles

Ajouter des commentaires aux champs de structure sera scanné par swagger comme commentaires de champs de modèle

go
package model

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

La valeur du tag example sera affichée comme exemple dans la page. Bien sûr, il supporte aussi les contraintes de champs

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

Tous les modèles utilisés doivent s'assurer qu'ils peuvent être scannés par swagger, sinon ils ne fonctionneront pas.

Authentification

Pour l'authentification, cela supporte

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

Si l'authentification de l'interface utilise JWT, stocké dans le champ Authorization du header, nous pouvons définir comme suit

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

Essentiellement, ce n'est qu'une apikey, si vous passez un bearer token, vous devez ajouter manuellement le préfixe Bearer.

Puis ajoutez le commentaire suivant sur les interfaces nécessitant une authentification

go
// @security Bearer

Sa valeur est le nom que vous avez défini dans securityDefinitions.

Configuration

swag stocke en fait plusieurs instances swagger différentes dans une map, ginSwagger comme adaptateur lit doc.json depuis l'instance (le fichier de définition de l'API), swaggerFiles fournit les fichiers HTML statiques pour afficher la page web, analyse la définition de l'API et génère l'interface. Une fois le processus compris, vous pouvez effectuer des opérations personnalisées.

go
// Name is a unique name be used to register swag instance.
// Nom d'instance par défaut
const Name = "swagger"

var (
  swaggerMu sync.RWMutex
    // Table d'instances
  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
       }

       // Correspondance de 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 {
       // Page d'accueil
       case "index.html":
          _ = index.Execute(ctx.Writer, config.toSwaggerConfig())
       // Fichier de description 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)
       }
    }
}

L'enregistrement automatique des instances se fait via le code Go généré. Ci-dessous, une partie du code de docs.go généré automatiquement

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() {
    // Enregistrer l'instance
  swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

On peut voir que dans la fonction init, il y a une fonction Register pour enregistrer l'instance courante. Si vous voulez modifier le nom de l'instance, il n'est pas recommandé de modifier ce fichier car docs.go est généré automatiquement. Il suffit d'utiliser le paramètre --instanceName appapi lors de la génération du code. Pour plus de commodité, vous pouvez utiliser la commande go generate intégrée dans les fichiers go pour faciliter la génération automatique de code, comme ci-dessous.

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

Personnellement, je n'aime pas écrire les commentaires d'informations générales swagger dans main.go ou sur la fonction main. Écrire ces commentaires au-dessus de go generate est le plus approprié.

TIP

Si vous avez besoin de plusieurs instances, assurez-vous que les noms d'instances sont uniques, sinon il y aura un panic

Pour personnaliser certaines configurations, vous devez utiliser ginSwagger.CustomWrapHandler, qui a un paramètre Config supplémentaire par rapport au précédent, dont la signification est la suivante

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
    // État d'expansion de la liste d'interfaces
  DocExpansion             string
    // Nom de l'instance
  InstanceName             string
    // Titre
  Title                    string
    // Profondeur d'expansion
  DefaultModelsExpandDepth int
    // Comme son nom l'indique
  DeepLinking              bool
  PersistAuthorization     bool
  Oauth2DefaultClientID    string
}

Utilisez swaggerFiles.NewHandler() pour remplacer le Handler par défaut, surtout nécessaire dans le cas d'instances multiples.

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

De plus, vous pouvez effectuer une réécriture de type et d'autres opérations, qui sont relativement simples. Pour plus de contenu, vous pouvez consulter la documentation officielle.

Points d'attention

  1. swag génère le fichier de description openapi basé sur les commentaires. Lors de la génération, le répertoire spécifié doit contenir les informations de base de la documentation d'interface, par défaut recherchées dans main.go

  2. swag init spécifie par défaut le répertoire courant, dont la valeur est ./, vous pouvez utiliser swag init -d pour spécifier plusieurs répertoires, séparés par des virgules. Le premier répertoire spécifié doit contenir les informations de base de la documentation d'interface. Par exemple

    swag init -d ./,./api
  3. -g, le fichier de stockage des informations de base de la documentation d'interface peut avoir un nom de fichier personnalisé, par défaut main.go. Lors de la génération de la documentation, utilisez le paramètre -g pour spécifier le nom du fichier

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

    Cette commande signifie analyser les informations de base de la documentation d'interface dans ./api.go, et rechercher et analyser les commentaires des autres interfaces dans les deux répertoires ./ et ./api pour générer la documentation correspondante.

  4. Le paramètre -o peut spécifier le chemin de sortie des fichiers de description de documentation, par défaut ./docs, par exemple :

    swag init -o ./api/docs
  5. --ot peut spécifier le type de fichiers de sortie, par défaut (docs.go, swagger.json, swagger.yaml), si vous voulez utiliser un programme go pour charger swagger ui, le fichier go est indispensable.

    swag init --ot go,yaml

    Les fichiers json et yaml générés peuvent être facilement importés dans d'autres logiciels de gestion d'API.

  6. Les commentaires peuvent être écrits n'importe où, même s'ils ne sont pas écrits sur une fonction, ils peuvent être analysés. Les écrire sur une fonction améliore juste la lisibilité. Essentiellement, c'est un DSL intégré au code source go sous forme de commentaires.

  7. swag supporte encore beaucoup d'autres paramètres, vous pouvez utiliser swag init -h pour les voir.

Golang by www.golangdev.cn edit