一个通过分析代码生成 OpenAPI 文档的工具
eAPI 通过分析 AST 生成 接口文档 及 前端代码。与 swaggo/swag 等工具不同之处在于,eAPI 无需编写注解即可使用。另外,eAPI 还支持生成 Typescript 类型代码 和 前端接口请求代码。
eAPI 首先解析出代码中的路由(方法/路径)声明,得到接口的 Path、Method 及对应的 Handler 函数。然后再对 Handler 函数进行解析,得到 请求参数(Query/FormData/JSON-Payload等)、响应数据等信息。最终生成一份符合 OpenAPI 3 标准的 JSON 文档。
eAPI 目前支持了 gin, echo 框架的文档生成,其他主流框架在计划中。如果你需要将 eAPI 应用在其他未被支持的框架,可以通过编写自定义插件的方式进行实现,或者给我们提交 PR。
go install github.com/gotomicro/eapi/cmd/eapi@latest
- 创建配置文件
在代码根目录创建配置文件 eapi.yaml
:
plugin: gin # 目前支持 gin 和 echo
output: docs
dir: .
- 生成文档
在代码根目录执行命令:
$ eapi
执行完成后会在 docs
目录下生成 openapi.json
文件。
如下是完整的配置文件示例:
output: docs # 输出文档的目录
plugin: gin # gin | echo . 取决于你使用的框架,目前支持了 gin 和 echo
dir: '.' # 需要解析的代码目录
# 可选. 请求/响应数据中依赖的类型对应的包
depends:
- github.com/gotomicro/gotoant
- gorm.io/datatypes
# 可选. 插件配置. 用于自定义请求响应的函数调用
properties:
# 自定义请求参数绑定
request:
- type: '*server/pkg/handler.CustomContext'
method: 'Bind'
return:
data:
type: 'args[0]' # 指定第一个函数参数为请求参数
# 自定义响应函数
response:
- type: '*server/pkg/handler.CustomContext'
method: 'JSONOK'
return:
contentType: 'application/json' # 指定响应的 content-type
data: # 这是一个嵌套的数据格式示例 '{"code":0,"msg":"hello",data:{...}}'
type: 'object'
properties:
code:
type: 'number'
msg:
type: 'string'
data:
optional: true # 是否可选. 默认 false
type: 'args[0]' # 指定为第一个函数参数
status: 200 # 指定为 200 状态码
# 可选. 配置代码生成器
generators:
- name: ts # 生成器名称. 暂时只支持 "ts" (用于生成 typescript 类型)
output: ./src/types # 输出文件的目录. 执行完成之后会在该目录下生成TS类型文件
properties
用于配置自定义请求参数绑定函数和响应输出函数。
配置示例:
properties:
# 自定义请求参数绑定
request:
- type: '*server/pkg/handler.CustomContext'
method: 'Bind'
return:
data:
type: 'args[0]' # 指定第一个函数参数为请求参数
配置示例:
response:
- type: '*server/pkg/handler.CustomContext' # 方法所在的 package/receiver
method: 'JSONOK'
return:
contentType: 'application/json' # 指定响应的 content-type
data: # 这是一个嵌套的数据格式示例 '{"code":0,"msg":"hello",data:{...}}'
type: 'object'
properties:
code:
type: 'number'
msg:
type: 'string'
data:
optional: true # 是否可选. 默认 false
type: 'args[0]' # 指定为第一个函数参数
status: 200 # 指定为 200 状态码
其中,data type 可选值为:
- string
- number
- integer
- boolean
- array
- file
- object
此外,还可以将函数入参作为参数类型,eAPI 会自动解析对应的参数类型。比如 args[0]
代表函数第一个参数。
完整的配置参考 https://github.com/link-duan/eapi/blob/main/plugins/common/config.go 下面的 DataSchema
类型声明。
如果需要使用代码生成功能,需要在配置文件内添加如下配置:
# 可选
generators:
- name: ts # 生成器名称. 暂时支持 "ts" | "umi"
output: ./src/types # 输出文件的目录. 执行完成之后会在该目录下生成TS类型文件
umi 代码生成器用于生成适用于使用 umi.js
框架的前端接口请求代码及 TypeScript 类型。
示例配置:
generators:
- name: umi
output: ./src/requests # 输出文件的目录
ts 代码生成器用于生成 TypeScript 类型代码。 示例配置:
generators:
- name: ts
output: ./src/types # 输出文件的目录
如果你需要对文档的内容进行更精细化的调整(比如接口标题、字段是否必选等),那么你需要使用到注解。
如果没有写注解,eAPI 也会帮你生成关于接口的必要信息。对应关系如下:
接口信息 | 默认值 |
---|---|
接口的 summary (标题) | pkg.HandlerName handler 函数所在的包名和函数名共同组成接口标题。如果有注释,会默认使用注释作为标题 |
接口描述 | handler 函数的注释(非注解部分) |
Path/Query/Form参数 | 根据代码生成。比如 gin 里面的 ctx.Query("q") 会被解析为 query 参数 q 。如果在这行代码上面加上注释,则会被作为这个参数的描述 |
请求 Body | 根据代码生成。比如 gin 里面的 ctx.Bind(&request) 参数绑定 |
Model 字段描述 | 字段注释 |
接口地址 | 根据代码里面的路由声明自动解析 |
允许写在 handler 函数的上方。用于设置接口的 summary
(或者叫标题)。
示例
// @summary 创建 XXX 资源
func Create(c *gin.Context) {
// your code
}
用于设置字段是否必填。允许写在 struct 字段注释里 或者 获取请求参数注释里。
注意:最新的 OpenAPI 标准中,没有对 可选字段 提供支持,只能设置必选字段。
struct字段注释
type XxRequest struct {
// 我是字段描述
// @required
Title string `json:"title"`
}
在这个示例里面,"我是字段描述” 会被显示为文档中的字段描述,并且会被显示为必填字段。
请求参数获取
// Create 创建 XXX 资源接口
func Create(c *gin.Context) {
// 分类 ID
// @required
categoryId := c.Param("categoryId")
// @required
arg0 := c.Query("arg0")
arg1 := c.Query("arg1")
}
在这个示例里面有三个参数:
- categoryId Path参数 字段描述:"分类 ID" 字段必选
- arg0 Query参数 无字段描述 必选
- arg0 Query参数 无字段描述 非必选
用于设置接口请求 body 的 content-type 。默认为 application/json
。允许写在 handler 函数注释里面。
示例
// @consume application/xml
func Create(c *gin.Context) {
var request view.CreateXxRequest
err = c.Bind(&request)
// ...
}
在上面这个示例里面,请求参数 request 会被认为是 XML 格式。
用于设置接口响应 body 的 content-type 。默认为 application/json
。允许写在 handler 函数注释里面。
示例
// @produce application/xml
func Create(c *gin.Context) {
var res view.CreateXxResponse
// ...
c.JSON(http.StatusOK, res)
}
在上面这个示例里面,响应 Body 数据 res 会被认为是 XML 格式。
用于忽略不需要展示在文档中的接口。允许写在以下位置:
- 代码文件头部:会忽略整个文件的接口
- 代码块上方:忽略代码块内的接口
func registerRoutes(g *gin.RouteGroup) {
// @ignore
{
// 代码块内的路由都会被忽略
g.GET("/v1/resources", handler.ResourceList)
g.POST("/v1/resources", handler.ResourceCreate)
}
// 代码块外面的不会被忽略
g.GET("/v1/pods", handler.PodList)
}
上面这个示例中,代码块内的两个接口都会被忽略,不会展示在文档中。而代码块外面的 GET /api/pods
接口则不会被忽略。
- 函数注释:忽略函数内的接口
// @ignore
func registerRoutes() {
g.GET("/v1/resources", handler.ResourceList)
g.POST("/v1/resources", handler.ResourceCreate)
g.GET("/v1/pods", handler.PodList)
}
上面这个示例中,registerRoutes
函数内的接口都会被忽略,不会展示在文档中。
用于设置接口的 Tag 。允许写在 handler 函数的注释、路由定义处的代码块注释、路由定义所在函数注释。设置了相同的 Tag 会在文档内展示在同一个分类下面。
示例
package router
// @tags User
func Create(c *gin.Context) {
// ...
}
func registerRoute2(r *gin.RouterGroup) {
// @tags Shop
{
r.GET("/goods", GoodsList)
}
}
// @tags Hello
func registerRoute(r *gin.RouterGroup) {
r.GET("/hello", Hello)
}
如上代码所示,三种注释均有效。
如果同时使用了上面三种中的多种注解,优先级为 第一种 > 第二种 > 第三种。
用于设置接口的 operationId
。 允许写在 handler 函数注释内。默认值为 handler 所在包名 + 函数名
operationId 除了会被应用在文档内,还会被作为生成的前端代码的函数名。
package user
// @id CreateUser
func Create(c *gin.Context) {
// ...
}
在上面这个示例中,Create
接口的 operationId 默认为 user.Create
,但由于设置了 @id
注解,所以 operationId 为 "CreateUser" 。
用于标记字段或者接口为弃用。允许用于字段注释和 handler 函数注释内。
示例
type User struct {
// Use NewField instead
// @deprecated
OldField string `json:"xx"`
}
// @deprecated
func Create(c *gin.Context) {
// ...
}
用于设置接口鉴权 (Security Requirement) ,参考 https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-requirement-object
// @security oauth2 pets:write pets:read
func XxxHandler() {
// ...
}
对应的 securitySchemes 配置示例:
openapi:
info:
title: This is an Example
description: Example description for Example
securitySchemes:
oauth2:
type: oauth2
flows:
implicit:
authorizationUrl: "https://example.org/api/oauth/dialog"
scopes:
"pets:write": "modify pets in your account"
"pets:read": "read your pets"
通常需要配合 securitySchemes 使用,参考 https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-scheme-object
在上面示例中,User.OldField
字段会被标记为弃用,Create
函数对应的接口会被标记为弃用。
- Clickvisual 项目
- 文档站: https://clickvisual.gocn.vip/api
- 文档描述文件: https://github.com/clickvisual/clickvisual/blob/master/api/docs/swagger.json
- gin 示例
- echo 示例