接口版本化本质是让不同语义/结构/行为的同一资源共存、可灰度、不破坏调用方;Go微服务中推荐URL路径前缀(如/v1)方式,因其在基础设施中天然支持路由、缓存、监控;Accept头更RESTful但运维成本高;Header方案折中但需统一拦截校验;关键在于DTO隔离、模型稳定、文档独立与SDK封装。

接口版本化不是给接口起个带 v1/v2 的名字,而是让不同语义、结构或行为的同一资源能共存、可灰度、不破坏调用方——在 Go 微服务里,它直接决定你能不能安全上线新字段、回滚旧逻辑、做 AB 测试。
URL 路径前缀(如 /v1/users)是最稳妥的落地方式
绝大多数生产项目选这个,不是因为“简单”,而是它在 Nginx/Envoy/Kong 这类基础设施里天然可路由、可缓存、可监控、可日志打标。浏览器、curl、Postman、前端 fetch 全都不需要额外配置就能跑通。
-
gin.Group("/v1")和gin.Group("/v2")必须分开注册,中间件、handler、DTO 都各自独立,不能共用一个函数靠if version == "v2"分支 - 路径里写死
v1不是“污染语义”,而是明确声明契约边界:/v1/users 和 /v2/users 是两个独立端点,哪怕底层查同一张表 - 别提前注册未发布的
/v2/xxx路由——Swagger 文档会暴露、健康检查可能误报、Nginx rewrite 规则会意外命中 - CDN 或反向代理按完整路径缓存,
/v1/users和/v2/users互不影响,而 query 参数(如?version=v2)默认不缓存
Accept 头(如 Accept: application/vnd.myapi.v2+json)更 RESTful,但调试和运维成本高
它把版本决策交给内容协商机制,理论上更干净。但实际中,浏览器发请求默认带 Accept: text/html,*/*,不加 -H 就测不出问题;API 网关、CDN、日志系统往往不记录或不透传 Accept 值,导致线上排查时“版本去哪了”成了玄学。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 必须用
mime.ParseMediaType解析,不能靠strings.Contains(r.Header.Get("Accept"), "v2")——application/json; version=2这类写法会漏判 - 中间件里提取后要存进
context.Context(比如context.WithValue(ctx, keyVersion, "v2")),而不是每个 handler 都调r.Header.Get("Accept") - 返回错误必须是
406 Not Acceptable,不是400 Bad Request——语义不对会让客户端 SDK 误判为网络错误 - Swagger 生成工具(如 swaggo)对 Accept 头支持弱,
@version注释容易失效,文档里 v1/v2 接口字段常混在一起
Header 版本(如 X-API-Version: v2)是折中方案,但必须统一拦截校验
它比 Accept 头更容易被客户端控制(fetch/fetch API 默认支持设置 header),也比 URL 路径更灵活(比如同一 URL 支持 v2.1 和 v2.2)。但风险在于:HTTP/2 代理可能丢弃自定义 header,Kubernetes Ingress 默认不透传,前端跨域请求还要显式配 Access-Control-Expose-Headers。
立即学习“go语言免费学习笔记(深入)”;
- 必须在入口中间件统一处理:
c.GetHeader("X-API-Version")→ 白名单校验 →c.Set("version", ver)→c.Next(),禁止业务 handler 自己解析 - 如果同时支持 Accept 和 X-API-Version,优先级必须写死(建议
Accept > X-API-Version > default),并在 OpenAPI 文档里明确定义 - gRPC-Gateway 场景下,
X-API-Version会自动透传到 gRPC metadata,但需在 gateway 配置里显式声明grpc-gateway的allowed_headers - 别用
strconv.Atoi直接转版本号——v2.1会 panic;要用正则先匹配再判断,或引入semver库处理带点的格式
真正难的从来不是怎么写路由,而是 DTO 结构体是否真的隔离、数据库模型是否稳定、OpenAPI 文档是否每版独立生成、客户端 SDK 是否强制封装版本头——这些地方漏一个,v2 上线当天就会有前端报警说“字段突然没了”。

















