路径版本管理必须用 /v1、/v2 前缀显式分组,通过 gin.Group("/v1") 统一管控路由、中间件、指标与文档;严禁硬编码路径或动态 if 分支,须按版本拆分 struct、校验非法版本返回 406、下线版本返回 410。

路径版本管理必须用 /v1、/v2 这类前缀显式分组,别试图用 query 或 header 替代——缓存、日志、监控、文档全会乱套。
gin.Group("/v1") 是唯一靠谱的起点
所有版本路由必须从 Group() 开始,而不是在 GET("/v1/users") 这种字符串路径里硬编码。前者能绑定中间件、隔离指标、生成独立 OpenAPI 分组;后者只是把版本当普通路径段,失去语义和管控能力。
-
v1 := r.Group("/v1")后注册的子路由,c.Request.URL.Path自动剥离前缀(比如实际是/users,不是/v1/users),避免 handler 里反复strings.TrimPrefix - 每个
Group可挂专属中间件:v2.Use(jwtStrictMiddleware),而v1.Use(sessionFallback),互不干扰 - 别把
Group("/v1")和Group("/v2")写反顺序——Gin 匹配是顺序优先,写错会导致/v2被/v1的通配规则提前截获
chi.Mount("/v2", v2Router) 才算真正隔离
chi 不支持 Group() 语义,必须用 Mount()。它会新建一个子路由器并切断父级中间件继承,这是防止 v1 日志中间件意外跑到 v2 响应头里的关键机制。
-
r.Mount("/v2", v2Router)后,v2Router内部的Get("/users")实际匹配/v2/users,且不会继承根路由的.Use() - 切忌在同一个
chi.Router里混写r.Get("/v1/users", ...)和r.Get("/v2/users", ...)——这会让路由树扁平化,丢失版本维度,Prometheus 指标无法按path="/v2/users"聚合 - 如果要用 gorilla/mux,等价写法是
subr := r.PathPrefix("/v1").Subrouter(),再往subr注册 handler;别在 handler 里手动判断strings.HasPrefix(c.Request.URL.Path, "/v1")
struct 必须按版本拆开,哪怕字段一模一样
共用 User struct 是最隐蔽的崩塌点。v2 加个 Nickname string,v1 客户端 JSON 解析就可能失败——因为 Go 的 encoding/json 默认序列化所有导出字段,不管版本。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 定义
UserV1和UserV2两个独立 struct,即使当前字段完全一致也分开声明 - 禁止用指针字段“模拟兼容”:
type User struct { Name *string }看似让 v1 客户端忽略空值,实则破坏了 v1 的契约:它本该收到非空Name string - handler 返回前必须显式转换:
json.Marshal(UserV1{...}),不能直接传 domain model 给c.JSON() - Swagger 注释如
@Success 200 {object} UserV1必须和对应Group绑定,否则 v1 文档里会混进 v2 字段
404 和 406 的边界必须卡死
非法版本号(比如 /v999/users)不能静默 fallback 到 v1,也不能返回泛泛的 404——客户端会误以为路径错了,而不是版本不支持。
- 在入口中间件校验
c.Request.URL.Path是否匹配已注册的/v1或/v2前缀,不匹配就立即返回406 Not Acceptable - 对已下线版本(如 v1 已停服),应返回
410 Gone并带Deprecation: true响应头,而非 301 重定向——重定向会把 v1 客户端拖进 v2 的字段陷阱 - 别在 handler 里写
if version == "v2" { ... }动态分支——这会让灰度、回滚、监控指标全部失效,版本逻辑彻底失控
真正的难点不在怎么写路由,而在每次加字段、改类型、删字段时,是否还记得去同步更新所有版本的 DTO 和转换逻辑。这个动作没法自动化,只能靠目录结构(handlers/v1/、handlers/v2/)和 Code Review 卡住。

















