Skip to content

swag

swaggo/swag es una implementación de Swagger API 2.0 en el lenguaje Go, generando documentos de interfaz tipo swagger.json y swagger.yaml mediante comentarios en formato específico, facilitando la exportación e importación.

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

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

Los frameworks web soportados por defecto en swag son los siguientes, este artículo usa gin como ejemplo para demostrar cómo generar rápidamente documentación de interfaz con gin y swagger.

TIP

Si no estás familiarizado con la sintaxis de swagger, puedes visitar About Swagger Specification | Documentation | Swagger

Instalación

Primero descarga la herramienta de línea de comandos swagger

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

Luego descarga las dependencias del código fuente de swagger

go get github.com/swaggo/swag

TIP

Para evitar problemas, las versiones de ambos deben mantenerse consistentes.

Luego descarga la biblioteca de archivos estáticos de swagger, html, css, js, etc., están incrustados en el código Go.

go get github.com/swaggo/files@latest

Finalmente descarga la biblioteca de adaptación de gin para swagger

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

Como este artículo solo usa gin como ejemplo, los adaptadores de otros frameworks web pueden ser consultados por tu cuenta, básicamente son muy similares.

Uso

Usa go mod para crear un proyecto Go básico, crea main.go, escribe el siguiente contenido.

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

Este es un ejemplo simple de gin web, los comentarios en la función main son la información básica del documento, Ping es una interfaz común. Luego ejecuta el comando para generar el documento, por defecto está en el directorio docs del mismo nivel que main.go

swag init

Modifica el código main.go

go
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
    // importar anónimamente el paquete de documentos de interfaz generado
    _ "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()
    // registrar ruta de archivos estáticos de 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")),
    })
}

Ejecuta el programa, accede a 127.0.0.1/swagger/index.html, la interfaz es la siguiente

Así se ejecuta un documento de interfaz básico. A continuación, excepto por algunos puntos特别需要注意的,básicamente no hay mucha diferencia con otros lenguajes.

Parámetros

El formato para definir parámetros es

@param name paramtype datatype isRequired comment

Un ejemplo es el siguiente

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

Los tipos de parámetros soportados son

  • query
  • path
  • header
  • body
  • formData

Los tipos de datos son

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

El tipo de parámetro también puede ser tu propio tipo, siempre que pueda ser escaneado por swagger.

Respuesta

El formato básico para definir la respuesta de la interfaz es el siguiente

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

Compuesto por código de estado, tipo básico, tipo de datos. {array} indica que es un array, mostrará la forma de array del tipo de datos, {object} mostrará la forma original del tipo de datos. Por ejemplo, generalmente definiremos un cuerpo de respuesta unificado

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

El tipo del campo Data es incierto, al describir el caso de uso de respuesta, se puede combinar como sigue

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

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

Modelos

Agregar comentarios a los campos de la estructura será escaneado por swagger como comentarios de campos de modelo

go
package model

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

El valor de la etiqueta example se mostrará como valor de ejemplo en la página, por supuesto, también soporta limitaciones de campos

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

Todos los modelos deben ser escaneados por swagger al usarlos, de lo contrario no funcionarán.

Autenticación

En cuanto a autenticación, soporta

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

Si la autenticación de la interfaz usa JWT, almacenada en el campo Authorization del header, podemos definir como sigue

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

Esencialmente esto es solo un apikey, si transmites un bearer token, necesitas agregar manualmente el prefijo Bearer.

Luego en tu interfaz que necesita autenticación, agrega el siguiente comentario

go
// @security Bearer

Su valor es el nombre de tu definición securityDefinitions.

Configuración

swag esencialmente almacena múltiples instancias diferentes de swagger en un map, ginSwagger como adaptador lee doc.json del archivo de definición de interfaz API, swaggerFiles proporciona archivos HTML estáticos para mostrar la página web, analiza la definición de API y genera la interfaz, después de entender todo el flujo, se pueden realizar operaciones personalizadas.

go
// Name es un nombre único usado para registrar la instancia de swag.
// nombre de instancia por defecto
const Name = "swagger"

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

    // crear una plantilla con nombre
    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
       }

       // coincidencia de ruta
       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())
       // archivo de descripción 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)
       }
    }
}

A través del código Go generado se completa automáticamente el registro de instancias, abajo está parte del código de docs.go generado automáticamente

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

Se puede ver que en la función init hay una función Register usada para registrar la instancia actual, si quieres modificar el nombre de la instancia no se recomienda editar en este archivo, porque el archivo docs.go se genera automáticamente, solo necesitas usar el parámetro --instanceName appapi al generar el código. Por conveniencia, puedes usar el comando go generate incrustado en el archivo Go para facilitar la generación automática de código, como sigue.

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 no me gusta escribir los comentarios generales de swagger en main.go o en la función main, escribir estos comentarios encima de go generate es lo más apropiado.

TIP

Si necesitas múltiples instancias, asegúrate de que los nombres de instancia sean únicos, de lo contrario ocurrirá panic

Para personalizar algunas configuraciones, necesitas usar ginSwagger.CustomWrapHandler, tiene un parámetro Config más que el anterior, la definición es la siguiente

go
// Config stores ginSwagger configuration variables.
type Config struct {
  // La url que apunta a la definición de API (normalmente swagger.json o swagger.yaml). Por defecto es `doc.json`.
  URL                      string
    // estado de expansión de la lista de interfaces
  DocExpansion             string
    // nombre de instancia
  InstanceName             string
    // título
  Title                    string
    // profundidad de expansión
  DefaultModelsExpandDepth int
    // como su nombre indica
  DeepLinking              bool
  PersistAuthorization     bool
  Oauth2DefaultClientID    string
}

Usa swaggerFiles.NewHandler() para reemplazar el Handler predeterminado, especialmente cuando hay múltiples instancias.

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

Además de esto, también se pueden realizar operaciones como reescritura de tipos, etc., son bastante simples, más contenido puede ser consultado en la documentación oficial.

Notas

  1. swag genera el archivo de descripción de interfaz openapi basado en comentarios, al generar, el directorio especificado debe contener la información básica del documento de interfaz, por defecto se busca en main.go

  2. swag init por defecto especifica el directorio actual, el valor es ./, puedes usar swag init -d para especificar múltiples directorios, separados por comas, el primer directorio especificado debe contener la información básica del documento de interfaz. Por ejemplo

    swag init -d ./,./api
  3. -g, el archivo de almacenamiento de la información básica del documento de interfaz puede ser un nombre de archivo personalizado, por defecto es main.go, al generar el documento, usa el parámetro -g para especificar el nombre del archivo

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

    El significado de este comando es analizar la información básica del documento de interfaz en ./api.go, y al mismo tiempo buscar y analizar la información de comentarios de otras interfaces en los directorios ./ y ./api y generar los documentos correspondientes.

  4. El parámetro -o puede especificar la ruta de salida del archivo de descripción del documento, por defecto es ./docs, ejemplo:

    swag init -o ./api/docs
  5. --ot puede especificar el tipo de archivo de salida, por defecto es (docs.go,swagger.json,swagger.yaml), si quieres usar el programa Go para cargar swagger ui, el archivo go es esencial.

    swag init --ot go,yaml

    Los archivos json y yaml generados pueden facilitar la importación de datos en otros software de gestión de interfaces.

  6. No importa dónde se escriban los comentarios, incluso si no se escriben en la función, también se pueden analizar, solo que escribirlos en la función tiene mejor legibilidad, esencialmente es un DSL incrustado junto con comentarios y código fuente Go.

  7. swag soporta muchos otros parámetros, puedes usar swag init -h para verlos.

Golang editado por www.golangdev.cn