swag
swaggo/swag — это реализация Swagger API 2.0 для языка Go, позволяющая генерировать документы интерфейсов типа swagger.json и swagger.yaml путём написания комментариев в определённом формате, что удобно для экспорта и импорта.
Репозиторий: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
Документация: swaggo/swag: Automatically generate RESTful API documentation with Swagger 2.0 for Go. (github.com)
swag по умолчанию поддерживает следующие веб-фреймворки. В этой статье в качестве примера используется gin для демонстрации быстрой генерации документов интерфейсов с помощью gin и swagger.
TIP
Если вы не знакомы с синтаксисом swagger, можете ознакомиться: About Swagger Specification | Documentation | Swagger
Установка
Сначала загрузите инструмент командной строки swagger
go install github.com/swaggo/swag/cmd/swag@latestЗатем загрузите зависимости исходного кода swagger
go get github.com/swaggo/swagTIP
Во избежание проблем версии обоих должны совпадать.
Затем загрузите библиотеку статических файлов swagger: html, css, js и т.д., встроенные в код Go.
go get github.com/swaggo/files@latestНаконец, загрузите библиотеку адаптации swagger для gin
go get github.com/swaggo/gin-swagger@latestПоскольку в этой статье используется только gin в качестве примера, адаптеры для других веб-фреймворков можно изучить самостоятельно, в основном они похожи.
Использование
Создайте минимальный проект Go с помощью go mod, создайте main.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")),
})
}Это очень простой пример gin web. Комментарии в функции main — это базовая информация документа, Ping — обычный интерфейс. Далее выполните команду для генерации документа, по умолчанию в каталоге docs на одном уровне с main.go
swag initИзмените код main.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
// Анонимный импорт сгенерированного пакета документов интерфейсов
_ "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
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")),
})
}Запустите программу, посетите 127.0.0.1/swagger/index.html, интерфейс будет следующим

Таким образом, запущен базовый документ интерфейсов. Далее, кроме некоторых особых моментов, в основном нет большой разницы с использованием на других языках.
Параметры
Формат определения параметров
@param name paramtype datatype isRequired commentПример
@param userId query int true "user unique id"Поддерживаемые типы параметров
- query
- path
- header
- body
- formData
Типы данных
- string (string)
- integer (int, uint, uint32, uint64)
- number (float32)
- boolean (bool)
- пользовательская структура
Тип параметра также может быть вашим типом, при условии, что он может быть просканирован swagger.
Ответы
Базовый формат определения ответа интерфейса
// @Success 200 {array} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPErrorСостоит из кода состояния, базового типа, типа данных. {array} означает, что это массив, будет показана форма массива типа данных, {object} покажет оригинальную форму типа данных. Например, обычно мы определяем единый ответ
type JSONResult struct {
Code int `json:"code" `
Message string `json:"message"`
Data interface{} `json:"data"`
}Тип поля Data не определён, при описании примеров ответа можно комбинировать следующим образом
// Комбинация
@success 200 {object} jsonresult.JSONResult{data=Account} "desc"
// Массив
@success 200 {object} jsonresult.JSONResult{data=[]Account} "desc"Модели
Комментарии к полям структур будут просканированы swagger как комментарии полей модели
package model
type Account struct {
// account id
ID int `json:"id" example:"1"`
// username
Name string `json:"name" example:"account name"`
}Значение тега example будет отображаться как пример значения на странице, конечно, он также поддерживает ограничения полей
type Foo struct {
Bar string `minLength:"4" maxLength:"16"`
Baz int `minimum:"10" maximum:"20" default:"15"`
Qux []string `enums:"foo,bar,baz"`
}Все модели должны быть просканированы swagger при использовании, иначе не будут работать.
Аутентификация
В части аутентификации поддерживается
- Basic Auth
- API Key
- OAuth2 app auth
- OAuth2 implicit auth
- OAuth2 password auth
- OAuth2 access code auth
Если аутентификация интерфейса использует JWT, хранится в поле заголовка Authorization, можно определить следующим образом
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
По сути это просто apikey, если вы передаёте bearer token, нужно вручную добавить префикс Bearer.

Затем в интерфейсах, требующих аутентификации, добавьте следующие комментарии
// @security BearerЕго значение — имя, определённое в securityDefinitions.
Конфигурация
swag фактически хранит несколько различных экземпляров swagger в map, ginSwagger как адаптер читает doc.json из экземпляра, то есть файл определения API интерфейсов, swaggerFiles предоставляет статические HTML-файлы для отображения веб-страниц, разбирает определение API и генерирует интерфейс. Поняв весь процесс, можно выполнять пользовательские операции.
// Name — уникальное имя для регистрации экземпляра swag.
// Имя экземпляра по умолчанию
const Name = "swagger"
var (
swaggerMu sync.RWMutex
// Таблица экземпляров
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"
}
// создать шаблон с именем
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
}
// Сопоставление маршрута
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 {
// Главная страница
case "index.html":
_ = index.Execute(ctx.Writer, config.toSwaggerConfig())
// Файл описания 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)
}
}
}Автоматическая регистрация экземпляров через сгенерированный код Go. Ниже приведена часть автоматически сгенерированного кода docs.go
// SwaggerInfo хранит экспортированную информацию Swagger для модификации клиентами
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "",
Description: "",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
// Зарегистрировать экземпляр
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}Можно видеть, что в функции init есть функция Register для регистрации текущего экземпляра. Если хотите изменить имя экземпляра, не рекомендуется редактировать этот файл, поскольку файл docs.go генерируется автоматически. Просто используйте параметр --instanceName appapi при генерации кода. Для удобства можно использовать команду go generate, встроенную в файл 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Лично мне не нравится писать общие комментарии swagger в main.go или функции main, лучше всего написать эти комментарии над go generate.
TIP
Если требуется несколько экземпляров, обязательно сохраняйте уникальность имён экземпляров, иначе будет panic
Для настройки конфигурации нужно использовать ginSwagger.CustomWrapHandler, он по сравнению с первым имеет дополнительный параметр Config, описание如下
// Config хранит переменные конфигурации ginSwagger.
type Config struct {
// URL, указывающий на определение API (обычно swagger.json или swagger.yaml). По умолчанию `doc.json`.
URL string
// Состояние развёртывания списка интерфейсов
DocExpansion string
// Имя экземпляра
InstanceName string
// Заголовок
Title string
// Глубина развёртывания
DefaultModelsExpandDepth int
// Как следует из названия
DeepLinking bool
PersistAuthorization bool
Oauth2DefaultClientID string
}Используйте swaggerFiles.NewHandler() вместо обработчика по умолчанию, особенно при нескольких экземплярах.
engine.GET(openapi.ApiDoc, ginSwagger.CustomWrapHandler(openapi.Config, swaggerFiles.NewHandler()))Кроме того, можно выполнять переопределение типов и другие операции, всё довольно просто. Больше содержимого можно прочитать в официальной документации.
Примечания
swag генерирует файл описания интерфейсов openapi на основе комментариев. При генерации указанный каталог должен содержать базовую информацию документа интерфейсов, по умолчанию ищется в
main.goswag initпо умолчанию указывает текущий каталог, значение./, можно использоватьswag init -dдля указания нескольких каталогов, разделённых запятой. Первый указанный каталог должен содержать базовую информацию документа интерфейсов. Напримерswag init -d ./,./api-g, файл хранения базовой информации документа интерфейсов можно настроить, по умолчаниюmain.go, при генерации документа используйте параметр-gдля указания имени файлаswag init -g api.go -d ./,./apiЭта команда означает разбор базовой информации документа интерфейсов в
./api.go, одновременно поиск и разбор комментариев других интерфейсов в каталогах./и./apiи генерацию соответствующих документов.Параметр
-oможет указать путь вывода файла описания документа, по умолчанию./docs, например:swag init -o ./api/docs--otможет указать тип выходного файла, по умолчанию (docs.go, swagger.json, swagger.yaml). Если хотите загрузить swagger ui программой Go, файл go необходим.swag init --ot go,yamlОстальные сгенерированные json и yaml файлы удобны для импорта данных в другие системы управления интерфейсами.
Где писать комментарии — неважно, даже если не писать в функции, всё равно будет разобрано, просто запись в функции улучшает читаемость. По сути это DSL, встроенный в комментарии и исходный код Go.
swag поддерживает много других параметров, можно использовать
swag init -hдля просмотра.
