Gin 路由树(radix tree)与路由冲突规则

结论先行

Gin 的路由表是按 HTTP 方法分开的多棵 radix tree(压缩前缀树):注册时构建、匹配时逐段比较,匹配开销接近 O(路径长度)。路由冲突在注册阶段直接 panic(fail fast),而不是运行期悄悄覆盖——这是刻意设计,避免线上路由语义被误改。

关键要点

主题说明
树的组织engine.trees 里每种方法一棵树(GET/POST/PUT…),方法与路径都命中才算匹配
节点结构每个节点保存静态段/参数段/通配段子节点,匹配时按优先级取段
静态与通配优先级静态段 > 参数段(:name) > 通配段(*path),保证 /user/me 优先于 /user/:id
冲突何时发生路由注册阶段,同方法重复注册或通配符布局冲突会 panic
404/405未匹配走 NoRoute 处理;默认未开启方法不允许检测,可设 HandleMethodNotAllowed=true 返回 405

常见冲突(典型报错以当前版本源码为准)

  • 同一方法下同一路径注册两次:报 “handlers are already registered for path ...”。
  • 参数段与通配段相互冲突(如不同名字的同级参数、通配段后还有其他内容)。
  • catch-all(*path)未放在最后一段。
  • 已存在 *path 路由后,再注册其下的静态/参数子路由会产生“conflicts with existing wildcard”类 panic。

代码示例

r := gin.New()

r.GET("/user/:id", h1)         // 正常
// r.GET("/user/:id", h2)      // panic:同方法同路径重复注册
r.GET("/files/*filepath", h3)  // catch-all 必须在末尾
// r.GET("/files/info", h4)    // 与上方 *filepath 冲突,panic

r.HandleMethodNotAllowed = true // 可选:方法不匹配但路径存在时返回 405

常见追问 / 记忆点

  • 追问:为什么路由冲突要 panic 而不是后注册覆盖先注册?答:覆盖会让“哪条生效”取决于注册顺序,改动顺序即改语义,极难排查;panic 把问题提前到启动期。
  • 追问:参数段与静态段在同一层能不能共存、谁优先?答:Gin 匹配时静态段优先,与 httprouter 的行为存在差异,具体共存规则与各版本实现相关,以官方文档与源码为准。
  • 记忆点:方法分树 + 压缩前缀树;冲突 = 启动即 panic = fail fast;静态优先于通配。
笔记加载中…