go-swagger 集成 Knife4j 并支持 OpenApi3

go-swagger 集成 Knife4j 并支持 OpenApi3

相关源码
Knife4j
gin-swagger
swag
Fizz

参考文章
Swagger 2与OpenAPI 3
手把手详细教你如何使用go-swagger文档

集成 Knife4j 前端

go-swagger默认的经典页面较为简陋,Knife4j作为swagger的升级版则看着很舒服,操作方便,故想要将 Knife4j 的前端页面集成到 go 项目中,查找 Knife4j 仓库发现早已有人提出该问题(仅使用knife4j-openapi3-ui,如何搭配项目中的已经生成好的openapi.json使用),并且作者提供了仅集成前端的方法。

按照作者所说,拉取 knife4j-4.5.0 仓库源码,并在本地 npm run dev 运行 knife4j-vue3 后发现 doc.html 会首先请求 /services.json 文件,对比 Knife4j示例demoservices.json内容即作者所说的接口分组数据(以下数据url和location自定):

[
    {
        "name": "Knife4j示例",
        "url": "/doc/swagger.json",
        "swaggerVersion": "2.0",
        "location": "/api/doc/swagger.json"
    }
]

拉取 gin-swagger 代码,以此为测试的后台服务。

安装 gin-swagger\example\basic 示例相关依赖,并按照文档用 swag 生成 docs 目录下文件,核心为 OpenApi 描述文件 swagger.json,然后修改 main.go,增加一个 /services.json 路由处理(/api为vite配置的 VITE_APP_BASE_API 前缀)。

r.StaticFile("/services.json", "./docs/services.json")

然后将以上json存为 services.json 文件放到 docs 目录下,运行basic示例。

再次刷新前端发现还会请求 /api/doc/swagger.json,再在后端 basic 中增加一条路由处理,让其指向 swag 生成的 swagger.json

r.StaticFile("/doc/swagger.json", "./docs/swagger.json")

再次运行,刷新前端,已经可以正确渲染出接口文档

至此第一步前端集成大功告成。

注意:swag + knife4j-vue3 的方式代码中无需依赖 go-swagger

支持 OpenApi3

阅读文档发现 go-swagger 只支持 Swagger 2.0 ,且不打算支持 OpenApi3。而 go-swagger 是使用 swag 工具生成 OpenApi 描述文件 services.json的。查看 swag 相关 issue,早已有人提出该问题,并且有几种解决方案:

  1. 使用 kin-openapi 这个库进行转换
  2. 使用 https://converter.swagger.io 上提供了 OpenApi2 到 OpenApi3 的转换接口
  3. 使用最新的 swag 代码,已经支持 OpenApi3,但存在bug,详见 kin-openapi 介绍

研究之下发现 kin-openapi 介绍了目前 go OpenApi3 生成的相关问题,并且提供了代码层面的转换方法,可以参考 https://github.com/getkin/kin-openapi/blob/master/openapi2conv/openapi2_conv_test.go 测试代码,暂时先不做测试,直接使用第三种方法,虽然可能存在bug,但最容易实现。

拉取 swag 源码 https://github.com/swaggo/swag ,切换分支进行编译尝试后发现 v2 分支支持生成 3.1 版本接口,需要如下参数

用编译的最新 swag.exe 重新生成 basic 的文档(swagv2.exe init --v3.1),可以看到 swagger.json 已经变成了 "openapi": "3.1.0",注意:需要重新 go get github.com/swaggo/swag/v2

阅读 knife4j-vue3 代码发现其本身就支持编译为不依赖 springboot 的纯前端代码,只需要将 VITE_RELEASE_APP_TYPE 改为 Knife4jFront 即可:

# just a flag
ENV = 'development'

# base api
VITE_APP_BASE_API = '/api'
# 发行版本类型 可选值: SpringDocOpenApi | Knife4jSpringUi | Knife4jJFinal | Knife4jFront
VITE_RELEASE_APP_TYPE = 'Knife4jFront'

然后 npm run build 构建生产环境代码,将生成的 dist 下文件放到 example\basic\knife4j-vue3 下,修改静态资源路由,使得可以访问 doc.html 和 依赖的 webjars 目录:

func main() {
	r := gin.New()

	r.StaticFile("/doc.html", "./knife4j-vue3/doc.html")
	r.StaticFile("/api/services.json", "./docs/services.json")
	r.StaticFile("/api/doc/swagger.json", "./docs/swagger.json")
	r.StaticFS("/webjars", http.Dir("./knife4j-vue3/webjars"))

	r.GET("/v2/testapi/get-string-by-int/:some_id", api.GetStringByInt)
	r.GET("/v2/testapi/get-struct-array-by-string/:some_id", api.GetStructArrayByString)

	url := ginSwagger.URL("http://localhost:8080/swagger/swagger.json") // The url pointing to API definition
	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))

	r.Run()
}

然后直接访问 http://localhost:8080/doc.html ,OpenApi3 的接口文档展示成功

后端目录结构:

其它方法

研究过程中也发现了另一种可以集成 Knife4j 的方式,可以查看 https://github.com/ccfish86/fizz 作者的仓库,通过另一种类似Java的思路,直接由代码生成 OpenApi3 文件,而不用通过 swag 工具手动执行,代码如下。

// ListFruitsParams represents the parameters that can
// be used to filter the fruit's market listing.
type ListFruitsParams struct {
	Origin   *string  `query:"origin" description:"filter by fruit origin"`
	PriceMin *float64 `query:"price_min" description:"filter by minimum inclusive price" validate:"omitempty,min=1"`
	PriceMax *float64 `query:"price_max" description:"filter by maximum inclusive price" validate:"omitempty,max=15"`
}

grp.GET("", []fizz.OperationOption{
	fizz.Summary("List the fruits of the market"),
	fizz.Response("400", "Bad request", nil, nil, nil),
	fizz.Header("X-Market-Listing-Size", "Listing size", fizz.Long),
}, tonic.Handler(ListFruits, 200))

作者将 gin 框架封装为 fizz,并借助 tonic 实现了自动生成 OpenApi3,可以运行示例代码进行尝试。

相比之下 go-swagger 的方式步骤略微复杂,需要写很多类似Java注解的注释(注释比代码还多),但对代码原本逻辑无侵入性,。

fizz 的方式对现有代码逻辑侵入性较高,需要在结构体上增加额外tag,增加了代码复杂度。

posted @ 2024-01-20 00:19  jixhua  阅读(426)  评论(0编辑  收藏  举报