Header校验必须由最外层中间件完成,注册在所有路由组之前,通过c.Set()注入版本值,错误交由统一错误处理链路,路由应统一注册如r.GET("/users"),handler内按c.GetString("api_version")分支处理。

Header校验必须放在最外层中间件,不能塞进 handler
直接在 GET("/users") 的 handler 里写 c.GetHeader("X-API-Version") + if-else 是典型反模式。这会让每个接口重复校验逻辑、无法统一错误响应、单元测试困难。Header 校验属于请求预处理,应由中间件完成,且必须注册在所有路由组之前(即 r.Use(versionMiddleware)),否则后续中间件或 handler 可能读不到已解析的版本信息。
用 c.Set() 注入版本值,别依赖全局变量或闭包
校验通过后,必须调用 c.Set("api_version", version) 将解析结果写入 *gin.Context。这是唯一安全、线程隔离的传递方式。常见错误包括:用闭包捕获局部变量、写到 map 全局缓存、或直接传参给 handler 函数——这些都会导致并发请求间数据污染或 panic。
- 正确:
c.Set("api_version", "v2"),后续用c.GetString("api_version")读取 - 错误:
version := "v2"然后在 handler 里直接用该变量(闭包捕获不可靠) - 错误:
var versionMap = make(map[string]string)并发写入未加锁
校验失败时不要 c.Abort(),让错误处理中间件接管
在版本中间件里调用 c.AbortWithStatusJSON(400, ...) 会跳过所有后续中间件(包括你写的全局日志、panic 恢复、监控上报等)。应该只做校验和注入,把错误响应交给统一的错误处理链路。比如:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- Header 缺失 →
c.Set("api_version", "v1")(明确 fallback)或c.Set("api_version", ""),让 handler 自行判断是否 abort - Header 值非法(如
X-API-Version: v3)→c.Set("api_version", "invalid"),handler 中 switch 到 default 分支再返回 400 - 绝不调用
c.Abort()或c.AbortWithStatus()
路由注册别搞路径前缀分组,同一路径按 Header 分支处理
有人写 v1 := r.Group("/v1") 和 v2 := r.Group("/v2"),然后对 /v1/users 和 /v2/users 分别注册 handler —— 这不是 Header 版本路由,是 URL 版本路由。真实需求是客户端发 GET /users + X-API-Version: v2,服务端返回 v2 行为。所以路由注册必须是:
r.GET("/users", usersHandler)
而 usersHandler 内部才根据 c.GetString("api_version") 分支:
-
"v1"→ 调用handleUsersV1(c) -
"v2"→ 调用handleUsersV2(c) -
""或"invalid"→ 统一返回错误
路径前缀分组只适用于语义完全独立的 API 集合(如 /admin 和 /public),不适用于同一资源的多版本演进。


















