swaggo/swag

swaggo/swag 提供了Go版本的Swagger自动生产RESTful API文档,其做法是在代码中按Swaggo的格式编写API注释,然后Swaggo会去解析这些注释,生成Swagger的文档以及托管到Web的框架代码,最终将代码编译到Web应用中,达到API文档托管的目的。

go-swagger是一套完整且功能齐全的高性能API组件,可与Swagger-API一起使用,分为三块分别是服务端、客户端、数据模型。

swagger

Swagger优势

  • 支持API自动生成同步的在线文档,使用Swagger后可直接通过代码生成文档,无需手动编写接口文档。
  • 提供Web页面的在线测试API,仅需定义好参数和格式,直接在界面上输入参数对应的值即可在线测试接口。

swaggo

  • swag命令可以将Go的注释转换为Swagger文档
  • swaggo支持的Web框架包括gin、echo、buffalo、net/http。

swaggo使用前需安装swag脚手架命令行工具、HTTP服务器、模板

$ go get -u -v github.com/swaggo/swag/cmd/swag
$ go get -u -v github.com/swaggo/http-swagger
$ go get -u -v github.com/alecthomas/template
  • swag库将Go批注转换为Swagger文档,随后由http-swagger使用,为SwaggerUI提供服务。
  • http-swagger库使用swag生成的文档帮助提供SwaggerUI
  • template库用于安装模板,这是Go的text/template包的分支,swag生成的docs.go文件中需此依赖关系,而在没有该依赖关系的情况下运行应用程序时会得到一个错误。

swag

安装Swag命令行

使用Swaggo首先要下载一个Swag命令行(脚手架)

$ go get -u -v github.com/swaggo/swag/cmd/swag

安装成功后会在GOPATH下的bin文件夹内生成swag.exe命令行工具

$ swag --version
swag version v1.7.8

安装最新版本

$ go install -v github.com/swaggo/swag/cmd/swag@latest
$ swag -v
swag version v1.7.8

在包含main.go主入口的工程根目录下执行swag init初始化,swag会检索当前工程中的swag注释。

$ cd gwf
$ swag init

执行swag init命令后会在项目根目录下生成docs文件夹,同时文件夹下会生成三个文件docs.goswagger.jsonswagger.yaml

查看swag命令支持的选项

$ swag --help
NAME:
   swag - Automatically generate RESTful API documentation with Swagger 2.0 for Go.

USAGE:
   swag [global options] command [command options] [arguments...]

VERSION:
   v1.7.8

COMMANDS:
   init, i  Create docs.go
   fmt, f   format swag comments
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help (default: false)
   --version, -v  print the version (default: false)

swag init

$ swag init --help
NAME:
   swag init - Create docs.go

USAGE:
   swag init [command options] [arguments...]

OPTIONS:
   --generalInfo value, -g value          Go file path in which 'swagger general API Info' is written (default: "main.go")
   --dir value, -d value                  Directories you want to parse,comma separated and general-info file must be in the first one (default: "./")
   --exclude value                        Exclude directories and files when searching, comma separated
   --propertyStrategy value, -p value     Property Naming Strategy like snakecase,camelcase,pascalcase (default: "camelcase")
   --output value, -o value               Output directory for all the generated files(swagger.json, swagger.yaml and doc.go) (default: "./docs")
   --parseVendor                          Parse go files in 'vendor' folder, disabled by default (default: false)
   --parseDependency, --pd                Parse go files inside dependency folder, disabled by default (default: false)
   --markdownFiles value, --md value      Parse folder containing markdown files to use as description, disabled by default
   --codeExampleFiles value, --cef value  Parse folder containing code example files to use for the x-codeSamples extension, disabled by default
   --parseInternal                        Parse go files in internal packages, disabled by default (default: false)
   --generatedTime                        Generate timestamp at the top of docs.go, disabled by default (default: false)
   --parseDepth value                     Dependency parse depth (default: 100)
   --instanceName value                   This parameter can be used to name different swagger document instances. It is optional.
   --overridesFile value                  File to read global type overrides from. (default: ".swaggo")
   --help, -h                             show help (default: false)
选项 默认值 描述
-g main.go API通用信息所在Go源文件路径,若是相对路径则基于API解析目录。
-d ./ API解析目录
--exclude 解析扫描时排除的目录,多个目录可使用逗号分割。
-p camelcase 结构体字段命名规则,三种可选snakecase、camelcase、pascalcase。
-o ./docs 文件输出目录
--parseVendor 禁用 是否解析vendor目录中的Go源文件
--parseDependency 禁用 是否解析依赖目录中的Go源文件
--md - 指定API的描述信息所使用的MarkDown文件所在的目录
--generateTime 启用 是否输出时间到输出文件docs.go顶部
--cef 禁用 解析包含用于x-codeSamples扩展的代码示例文件的文件夹
--parseInternal 禁用 解析internal包中的Go文件
--parseDepth 100 依赖解析深度
--instanceName swagger 设置文档实例名称

若项目根目录下存在main.go主入口文件,可直接使用swag init初始化创建docs.go。若接口注释没有编写在默认的main.go主入口文件中,可使用-g标识来告知swag。

例如:

$ swag init -g swag.go

生成文档

生成Swagger文档的步骤

  1. 在代码中添加注释,先通过注释的main方法添加整个项目的一般描述。

例如:添加项目级别的文档

$ vim main.go
package main

import (
    "net/http"
    "github.com/gorilla/mux"
    httpSwagger "github.com/swaggo/http-swagger"
)

// @title RESTful API
// @version 1.0
// @description RESTful API Document
// @host 127.0.0.1:8801
// @BasePath /
func main() {
    router := mux.NewRouter()
    router.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler)
    http.ListenAndServe(":8801", router)
}
  1. 生成swagger配置文件

完成对项目入口函数main()注释后, 使用swag init命令生成Swagger文档。

$ swag init -g main.go

执行成功后会在项目根目录下生成docs目录,并会根据注解生成最新的配置文件。

  1. 加载配置文件

入口文件通过导入httpSwagger "github.com/swaggo/http-swagger"包,即可使用SwaggerUI。使用前必须将swag init生成的文档配置加载到入口。

$ vim main.go
package main

import (
    "net/http"
    _ "gwf/docs"
    "github.com/gorilla/mux"
    httpSwagger "github.com/swaggo/http-swagger"
)

// @title RESTful API
// @version 1.0
// @description RESTful API Document
// @host 127.0.0.1:8801
// @BasePath /
func main() {
    router := mux.NewRouter()
    router.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler)
    http.ListenAndServe(":8801", router)
}

这里关键核心在于导入包的_ "gwf/docs"位置,_是一种导入副作用包的方法,意味着代码没有显式地调用包中的任何方法,但可能执行注入注册处理程序之类的操作。

  1. 运行测试
$ go run main.go

浏览器输入地址 http://127.0.0.1:8801/swagger/index.html ,加载Swagger UI。

SwaggerUI

Swagger提供了在线编辑器,通过复制配置文件内容来显示UI界面。

通用API信息

主函数采用声明式注释格式包含通用API信息

// @title RESTful API
// @version 1.0
// @description RESTful API Document
// @host 127.0.0.1:8801
// @BasePath /
func main() 
  • 若想直接在Swagger中调试API,必须填写@host@BasePath两项。
注释 说明
@title 应用程序名称,必填。
@version 提供应用程序API的版本
@description 应用程序简短描述
@tag.name 标签名称
@tag.description 标签描述
@tag.docs.url 标签的外部文档URL
@termsOfService API服务条款
@contact.url 联系信息的URL,必须采用网址格式。
@license.name 必填,用于API的许可证名称。
@license.url 用于API许可证的URL,必须采用网址格式。
@host 运行API的主机,主机名或IP地址。
@BasePath 运行API的基本路径
@accept API可使用的MIME类型列表
@produce API可以生成的MIME类型列表
@query.collection.format 请求URI Query里数组参数的默认格式,默认csv。
@schemes 空格分割的请求的传输协议
@x-name 扩展的键,必须以x-开头,且只能使用JSON值。

处理程序

  • 封装HTTP服务器,使用gorilla/mux出来路由。
  • 为处理程序添加注释并通过Swagger测试

创建入口函数

$ vim main.go
package main

import (
    "gwf/router"
    "net/http"
)

// @title RESTful API
// @version 1.0
// @description RESTful API Document
// @host 127.0.0.1:8801
// @BasePath /
func main() {
    http.ListenAndServe(":8801", router.Register())
}

分离路由实现路由注册

$ vim router/router.go
package router

import (
    "gwf/router/admin"

    "github.com/gorilla/mux"
)

func Register() *mux.Router {
    //创建路由器
    r := mux.NewRouter().StrictSlash(true)
    //注册路由
    admin.Swag(r)
    admin.Home(r)
    return r
}

创建Swagger服务路由

$ vim router/admin/swag.go
package admin

import (
    _ "gwf/docs"

    "github.com/gorilla/mux"
    httpSwagger "github.com/swaggo/http-swagger"
)

func Swag(r *mux.Router) {
    g := r.PathPrefix("/swag")
    g.Handler(httpSwagger.WrapHandler)
}

创建后台首页接口并添加Swagger注释

$ vim router/admin/home.go
package admin

import (
    "gwf/handler/admin"

    "github.com/gorilla/mux"
)

func Home(r *mux.Router) {
    g := r.PathPrefix("/admin").Subrouter()
    h := admin.NewHome()
    {
        g.HandleFunc("/", h.Index).Methods("GET").Name("admin_home")
    }
}

创建后台后路由对应的处理程序

$ vim handler/admin/home.go
package admin

import (
    "gwf/response"
    "log"
    "net/http"
)

type home struct{}

func NewHome() *home {
    return new(home)
}

// @Summary 首页
// @Description 默认首页
// @Tags 后台
// @Accept json
// @Produce json
// @Failure 500 {string} string "{code:500, msg:"failure"}"
// @Success 200 {string} string "{code:200, msg:"ok", data:null}"
// @Router /admin [get]
func (*home) Index(w http.ResponseWriter, r *http.Request) {
    log.Printf("%s %s %s\n", r.RemoteAddr, r.RequestURI, r.Method)

    w.Header().Set("Content-Type", "application/json;charset=utf-8")
    w.Write(response.Ok.Json())
}

统一输出的JSON参数

$ mkdir response && cd response
$ vim response.go
package response

import (
    "encoding/json"
    "log"
)

var (
    Ok  = New(200, "操作成功")
    Err = New(500, "操作失败")
)

type reply struct {
    Code int         `json:"code"`
    Msg  string      `json:"msg"`
    Data interface{} `json:"data"`
}

// reply 构造函数
func New(code int, msg string) *reply {
    return &reply{
        Code: code,
        Msg:  msg,
        Data: nil,
    }
}

// WithMsg 追加响应消息
func (t *reply) WithMsg(msg string) reply {
    return reply{
        Code: t.Code,
        Msg:  msg,
        Data: t.Data,
    }
}

// WithData 追加响应数据
func (t *reply) WithData(data interface{}) reply {
    return reply{
        Code: t.Code,
        Msg:  t.Msg,
        Data: data,
    }
}

// Json 返回JSON格式的数据
func (t *reply) Json() []byte {
    s := &struct {
        Code int         `json:"code"`
        Msg  string      `json:"msg"`
        Data interface{} `json:"data"`
    }{
        Code: t.Code,
        Msg:  t.Msg,
        Data: t.Data,
    }
    log.Printf("%+v\n", s)
    raw, err := json.Marshal(s)
    if err != nil {
        log.Println(err)
    }
    return raw
}

生成最新Swagger文档

$ swag init

浏览器输入地址 http://127.0.0.1:8801/swag/index.html 测试接口

测试接口

API操作注释

// @Summary 首页
// @Description 默认首页
// @Tags 后台
// @Accept json
// @Produce json
// @Failure 500 {string} string "{code:500, msg:"failure"}"
// @Success 200 {string} string "{code:200, msg:"ok", data:null}"
// @Router /admin [get]
func (*home) Index(w http.ResponseWriter, r *http.Request) 
  • @Tags用来给API分组
注释 描述
@Description 操作行为的详细说明
@Description.markdown 应用程序的简短描述,该描述从名为endpointname.md的文件中读取。
@Id 用于标识操作的唯一字符串,在所有API操作中必须唯一。
@Tags 每个API操作的标签列表,以逗号分割。
@Summary 操作的简短摘要
@Accept API可使用的MIME类型列表,Accept仅影响具有请求正文的操作,值必须是"Mime类型"。
@Produce API可以生成的MIME类型的列表,值必须是“Mime类型”。
@Param 空格分隔的参数
@Secuirty API操作的安全性
@Success 空格分割的成功响应
@Failure 空格分割的故障响应
@Response 与@success、@failure作用相同
@Header 空格分割的头字段
@Router 空格分割的路径定义
@X-name 扩展字段必须以x-开头,只能使用JSON值。

@Param

Param参数格式

@Param 参数名 参数类型 参数数据类型 是否必须 参数描述 其它属性

例如:

@Param id path integer true "自增主键"
@Param who query string true "人名"
@Param name body string true "名称" default(admin)
@Param username formData string true "账户" default(admin)
@Param param body main.JSONParam true "上传的JSON"
参数 示例 描述
参数名 name 解释参数的名字
参数类型 body 参数类型分为5种
参数数据类型 string 参数数据类型支持类型4类
是否必须 true 该参数是否是必须需要的
参数描述 "名称" 参数的说明文本
其它属性 default(admin) 额外属性,比如枚举、默认值、值范围等。

参数类型支持5种

参数类型 描述
path 直接拼接在URL中
query 组合在URL中
formData 一般是POST、PUT方法所用
body Accept为JSON时,使用该字段接受JSON类型。
header 请求头中传递的参数

参数数据类型支持4种

参数数据类型 可选值
string string
integer int、uint、uint32、uint64
number float32
boolean bool

额外属性可组合使用

额外属性 描述
Enums(A,B,C) 枚举值
minlength(1) 最小长度
maxlength(20) 最大长度
minium(1) 最小值
maxinum(20) 最大值
default(0) 默认值

@Success

@Success注释用于指定成功响应的数据,格式为

@Success HTTP响应码 {响应参数类型} 响应数据类型 其它描述

例如:

@Success 200 {object} main.File
@Success 200 {string} string ""
元素 描述
HTTP响应码 诸如200、400、500
响应参数类型 -
响应数据类型 返回的数据类型,可以是自定义类型,也可以是JSON。
其它描述 描述说明

@Router

@Router用于指定路由与HTTP方法,不用添加基础路径。

@Router格式如下:

@Router /path/to/handler [HTTP方法]

MIME类型

swag接受所有格式正确的MIME类型,即使匹配*/*,除此之外swag还接受某些MIME类型的别名。

别名 MIME
json application/json
xml text/xml
plain text/plain
html text/html
mpfd multipart/form-data
x-www-form-urlencoded application/x-www-form-urlencoded
json-api application/vnd.api + json
json-stream application/x-json-stream
octet-stream application/octet-stream
png image/png
jpeg image/jpeg
gif image/gif
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容