根本原因是文档生成与版本控制未绑定同一元数据源;swag需显式用@Tags标记v1/v2,go-zero需拆分.api文件并统一version与路径,结构体须加@Model和json tag,且package名须匹配。

Go 项目里接口文档和版本变更脱节,根本原因不是工具没选对,而是文档生成和版本控制没绑定在同一套元数据源上。真正能跑通的方案,必须让 swag init 或 goctl api 能感知到「哪个 API 属于哪个版本」,否则生成的文档天然就是混杂的。
swag 生成文档时如何区分 v1/v2 接口
swag 不会自动识别路径里的 /v1/ 就是 v1 版本——它只认 @Tags 和 @Summary 这类注释。如果你的路由分组用了 r.Group("/v1"),但 handler 函数上没加 // @Tags v1,那生成的文档里所有接口就全挤在同一个标签页下。
- 每个 handler 文件顶部或函数上方必须显式写
// @Tags v1或// @Tags v2,不能靠目录名或路径推断 - 如果用
swag init -d internal/handler/v1 -d internal/handler/v2,得确保两个目录下的 handler 都各自带对应@Tags,否则合并后仍混乱 -
@Tags值要和 Swagger UI 的分组显示名一致,比如前端看到的是 “v1 Users”,那注释就得是// @Tags v1 Users,中间空格会被转成下划线 - 别依赖
@Version注释做区分——它只影响全局文档 version 字段,不参与接口分组
go-zero 的 .api 文件怎么表达多版本
go-zero 的 .api 文件本身不支持在一个文件里定义多个版本的同名 service;它靠的是「物理隔离 + 工具参数驱动」。你不能写 service greet-api-v1 和 service greet-api-v2 在同一个文件里,而应该拆成 greet-v1.api 和 greet-v2.api 两个文件。
- 每个
.api文件对应一个独立版本,info.version必须与文件名或目录名语义一致(如info.version: "v1.2.0") - 生成代码时用
goctl api go -api greet-v1.api -dir greet/v1,路径greet/v1会成为包路径和 URL 前缀的双重依据 - 如果想共用部分结构体(比如
GreetRequest),得把它们抽到单独的types.api中,再用import引入,否则goctl会为每个.api文件重复生成 struct -
goctl生成的文档默认不带版本筛选 UI,需配合 nginx 或 doc site 做静态路由分流,比如/docs/v1指向greet/v1/docs/swagger.json
版本变更后文档不更新?先查这三个地方
改了 handler 签名、加了新字段、升级了结构体,但 swag init 后 swagger.json 里还是旧的——大概率卡在这三个硬性条件上。
立即学习“go语言免费学习笔记(深入)”;
- 新加的 struct 没加
// @Model注释,swag 就不认识它,@Success 200 {object} User会变成空 schema - struct 字段没加
json:tag(比如Name string `json:"name"`),即使有@Model,字段也不会出现在文档中 - handler 所在的
.go文件 package 名不是main或实际注册路由的 package(比如写在package router里但swag init没加-g cmd/app/main.go),整个文件就被跳过
最常被忽略的一点:文档生成工具只管「有没有声明」,不管「有没有用到」。哪怕你删掉了某个 v1 接口的 handler 函数,只要它的 @Tags v1 注释还残留在其他函数上,或者旧的 .api 文件没清理干净,生成结果里就会继续出现它。自动化不是全自动,它依赖你主动维护元数据的准确性。


















