一个通过分析代码生成 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 示例