gin-swagger生成多个api文档

gin-Swagger 是个很好用的API文档生成工具,如果使用gin框架,只需要给API编写注释即可使用swag init命令生成API文档。但是gin-swagger使用的是Swagger2.0版本,不支持项目切换。而我又恰好遇到了一个项目中有多个子项目的情况,如果把所有API都放在一个文档里,查找起来就非常冗余,而且不支持多级目录,所以只能生成多个Swagger文档,使用不同的链接访问。

我们的需求

假设我们的项目有多个子项目或者有多个版本时,目录大致如下

- v1
- v2
- admin

我们要实现的效果是:

localhost:8080/swagger/v1/index.html
localhost:8080/swagger/admin/index.html
localhost:8080/swagger/v2/index.html

官方demo解析

首先官方其实提供了demo给我们,
我们只需要分别执行两次命令,即可生成两个API文档

swag i -g main.go -dir api/v1 --instanceName v1
swag i -g main.go -dir api/v2 --instanceName v2

解释下各个参数的意义:

-g 入口函数的位置,如果设置了dir,这个参数是dir的相对路径

-dir 需要生成文档的目录 默认是当前目录

-instanceName 需要生成文档的实例名称

-o 生成的文档的输出目录,默认是./docs

--parseDependency  解析依赖,默认是false,如果你指定了dir,不开启该参数的话,如果你的注释中有其他包的数据结构会导致生成失败

所以我们来看看官方demo的思路:

  1. 不同子项目的路由分别在不同的目录下,所以使用-dir参数,v1的API文档只会从api/v1目录下搜索
- v1
    - api
        main.go

- v2
    - api
        main.go

  1. -g指定的main.go并非是项目入口,而是注册router的入口,这个地方进行了项目的一些定义
package v1

import (
	"github.com/gin-gonic/gin"
)

// @title Swagger Example API
// @version 1.0
// @description This is a sample server.
// @termsOfService http://swagger.io/terms/

// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @BasePath /v1

func Register(router *gin.Engine) {
	v1 := router.Group("v1")

	v1.GET("/books", GetBooks)
}

  1. 使用instanceName参数,指定生成文档的实例名称,比如v1生成的文档就是v1_swagger.json
- docs
    - v1_docs.json
    - v2_docs.json  
    - v1_swagger.json
    - v2_swagger.json
    - v1_swagger.yaml
    - v2_swagger.yaml
  1. 在代码中定义swagger 路由,注意_ “github.com/swaggo/gin-swagger/example/multiple/docs” 这一行不能缺少,相当于找到了文档所在目录
package main

import (
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	v1 "github.com/swaggo/gin-swagger/example/multiple/api/v1"
	v2 "github.com/swaggo/gin-swagger/example/multiple/api/v2"
	_ "github.com/swaggo/gin-swagger/example/multiple/docs"
)

func main() {
	// New gin router
	router := gin.New()

	// Register api/v1 endpoints
	v1.Register(router)
	router.GET("/swagger/v1/*any", ginSwagger.WrapHandler(swaggerFiles.NewHandler(), ginSwagger.InstanceName("v1")))

	// Register api/v2 endpoints
	v2.Register(router)
	router.GET("/swagger/v2/*any", ginSwagger.WrapHandler(swaggerFiles.NewHandler(), ginSwagger.InstanceName("v2")))

	// Listen and Server in
	_ = router.Run()
}

最后运行项目,我们就可以通过http://localhost:8080/swagger/v1/index.html 和 http://localhost:8080/swagger/v2/index.html 来查看生成的两个版本的文档

更复杂的工程

然而我们实际上的工程要比这个demo更加复杂,这样的demo可能无非让我们实现我们的需求

例如我的项目中有多个子项目,启动主项目时同时启动多个子项目,也可以单独启动子项目

同时这些不同的包可能共用一些数据结构,比如返回数据Response{code: 200,msg: “ok”,data: “data”}

这时候很需要多个API文档,客户端只需要访问一个文档就可以了

- cmd
    -v1
        -main.go
    -control
        -main.go
    -v2
        -main.go
- internal
    -v1
        -api
            -api.go
    -control
        -api
            -api.go
    -v2
        -api
            -api.go
- pkg
    -serializer
- docs
    -swag

那么我用来生成文档的命令是:

swag init -g  cmd/v1/main.go  -o ./docs/swag  --instanceName v1

swag init -g  ../../cmd/v2/main.go  -d internal/v2  -o ./docs/swag  --parseDependency --instanceName v2

swag init -g  ../../cmd/control/main.go  -d internal/control  -o ./docs/swag  --parseDependency --instanceName control

生成的目录结构如下

- docs
    - swag
        - v1_docs.json
        - control_docs.json
        - v2_docs.json
        - v1_swagger.json
        - control_swagger.json
        - v2_swagger.json
        - v1_swagger.yaml
        - control_swagger.yaml
        - v2_swagger.yaml

然后在代码中注册路由

package main

import (
	_ "gin-swagger-demo/docs/swag"
	control "gin-swagger-demo/internal/control/api"
	v1 "gin-swagger-demo/internal/v1/api"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	v2 "github.com/swaggo/gin-swagger/example/multiple/api/v2"
)

// @title Test API
// @version 1.0
// @description Test Full API v1.0
// @contact.name -Control API
// @contact.url http://localhost:8081/swagger/control/index.html
func main() {
	// New gin router
	router := gin.New()

	// Register v1 endpoints
	v1.Register(router)
	router.GET("/swagger/v1/*any", ginSwagger.WrapHandler(swaggerFiles.NewHandler(), ginSwagger.InstanceName("v1")))

	// Register v2 endpoints
	v2.Register(router)
	router.GET("/swagger/v2/*any", ginSwagger.WrapHandler(swaggerFiles.NewHandler(), ginSwagger.InstanceName("v2")))

	// Register control endpoints
	control.Register(router)
	router.GET("/swagger/control/*any", ginSwagger.WrapHandler(swaggerFiles.NewHandler(), ginSwagger.InstanceName("control")))

	// Listen and Server in
	_ = router.Run(":8081")
}

最后的效果如下:

请添加图片描述
请添加图片描述

打包时忽略swagger文档

我们一般在开发中需要swagger文档提供给其他同事查看,打包到生产环境时就不需要了。所以我们可以使用+build doc标签来忽略swagger文档的生成。

package main

import (
	_ "gin-swagger-demo/docs/swag"
	"github.com/gin-gonic/gin"
	v2 "github.com/swaggo/gin-swagger/example/multiple/api/v2"
)

var swagHandler gin.HandlerFunc

// @title Test API
// @version 2.0
// @description Test API v2.0
// @contact.name -V1 API
// @contact.url http://localhost:8081/swagger/v1/index.html
func main() {
	// New gin router
	router := gin.New()

	// Register v2 endpoints
	v2.Register(router)
	if swagHandler != nil {
		router.GET("/swagger/v2/*any", swagHandler)
	}

	// Listen and Server in
	_ = router.Run(":8081")
}


//go:build doc
// +build doc

package main

import (
	"fmt"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
)

func init() {
	fmt.Println("init swag")
	swagHandler = ginSwagger.WrapHandler(swaggerFiles.NewHandler(), ginSwagger.InstanceName("v2"))
}


// 打包后体积约11M 无swag文档
go build -o v2.exe ./cmd/v2

// 打包后体积约26M 有swag文档
go build -o v2_doc.exe -tags "doc" ./cmd/v2

总结

虽然我们在实际开发中可能遇不到多个子项目的情况,但多个版本的情况还是挺常见的,也许后续gin-swagger可能升级到Swagger3.0后就可以支持多项目/多版本的切换,但在此之前你也许可以参照我的方案来实现这一需求。

我也把测试代码开源了,欢迎下载使用 gin-swagger-demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值