proto文件多版本路径通过在google.api.http选项中显式定义/v1/、/v2/等前缀实现路径隔离,v1和v2须分属独立service块,避免路由冲突;共享biz逻辑需通过纯Go结构体解耦,错误码共用同一errors.proto并追加枚举项;客户端升级需配套Swagger分版本生成、网关显式路由及接口长期维护。

proto 文件怎么定义多版本路径?
Kratos 的 HTTP 路由绑定完全依赖 google.api.http 选项,多版本本质是路径隔离,不是代码分支。比如 v1 和 v2 接口不能共用 /users/{id},否则无法并存。
正确做法是在 .proto 中显式区分路径前缀:
- v1 版本:用
option (google.api.http) = { get: "/v1/users/{id}" }; - v2 版本:用
option (google.api.http) = { get: "/v2/users/{id}" }; - 避免用
/api/v1/...这类冗余前缀——Kratos 默认不处理/api,容易和反向代理规则冲突
注意:v1 和 v2 是独立 service 定义,不能写在一个 service 块里混用。否则生成的 xxx_http.pb.go 会把两个版本路由注册到同一个 HTTP handler,导致 panic 或覆盖。
如何让 v1 和 v2 共享同一套 biz 逻辑?
直接复用 internal/biz 层代码是安全的,前提是 biz 层不依赖任何 proto 生成的结构体(如 *v1.User)。否则 v1 和 v2 的 message 类型不同,会强制拆分逻辑。
推荐解耦方式:
- 在
internal/biz/model下定义纯 Go 结构体(如User),不带包名前缀 - 在
internal/data层做转换:把*v1.User→biz.User,再传给用例函数 - v2 的转换逻辑单独写在
internal/transport/http/v2/xxx.go,不污染 v1 的 transport 层
这样即使 v2 新增了字段、重命名了字段,biz 层完全无感;出参同理,用 biz.User 统一返回,再按需映射到 *v2.User。
错误码和枚举怎么跨版本保持兼容?
Kratos 的错误码基于 errors.proto 中的 enum ErrorReason,但 v1 和 v2 必须共用同一份 errors.proto,否则客户端无法统一解析。
关键约束:
- 所有版本共用一个
api/errors/errors.proto,放在项目根目录或api/common/下 - 新增错误码只能追加,禁止修改已有枚举值的数字编号(比如
USER_NOT_FOUND = 0永远是 0) - 如果 v2 需要新语义错误,加新枚举项(如
USER_DEPRECATED = 100),不要复用旧编号 - 废弃的错误码不能删,但可在注释中标明
// DEPRECATED: use USER_DEPRECATED instead
生成的 errors.pb.go 会被所有版本 transport 层引用,所以必须一次定义、全局生效。
客户端升级时怎么避免 breaking change?
最常被忽略的一点:HTTP 客户端默认不会感知 URL 变更。如果你只改了 proto 里的 get: "/v2/...",但前端仍调用 /v1/...,服务端返回 404,这不是框架问题,是契约断裂。
必须配套做的三件事:
- 在 Swagger 文档中为每个版本生成独立
swagger.json(Kratos 自动生成greeter_v1.swagger.json和greeter_v2.swagger.json) - 用 Nginx 或 API 网关做
/v1/ → /v1/、/v2/ → /v2/的显式路由,禁止 fallback 到默认版本 - 在
internal/transport/http启动日志里打印已注册的路由表,确认GET /v1/users/{id}和GET /v2/users/{id}都存在且 handler 不同
别指望客户端自动升级——v1 接口至少保留 6 个月,期间只修复 bug,不加功能;新需求全走 v2。这才是平滑升级的真实节奏。


















