Radix树通过前缀分段跳转匹配路由:如/user/123切为["user","123"],先找user子节点,再匹配:id参数节点;:id要求段非空且不含/,不校验格式,需手动或v3约束校验;StrictRouting影响路径结尾斜杠匹配而非参数提取。

Radix 树如何导航到匹配节点
Fiber 不是逐条比对所有注册路由,而是靠自研基数树(radix tree)做前缀跳转。请求路径 /user/123 进入匹配流程后,引擎会按段切割为 ["user", "123"],然后从根节点开始:先找是否有 user 子节点;命中后,再看该节点下是否存在 :id 类型子节点(不是字符串 "id",而是带 : 前缀的参数标记)。只要当前段非空且不以 / 开头,就认为可绑定到 :id。
常见错误现象:GET /user/ 404 —— 因为 :id 要求后续路径段非空,而 /user/ 后无段可取;GET /user//123 也失败,因双斜杠被 fasthttp 自动归一化为单斜杠,但中间空段仍导致参数节点不触发。
- 静态路径优先于参数节点:若同时注册了
/user/new和/user/:id,访问/user/new一定走前者 - 同级参数节点互斥:
/user/:id和/user/:name不能共存,后者会覆盖前者 - 通配节点
*filepath只能出现在路径末尾,且一旦存在,就屏蔽同级所有其他节点
c.Params() 提取值的生命周期限制
c.Params("id") 返回的是底层字节切片的引用,不是新分配的字符串。这意味着它只在当前 handler 执行期间有效;一旦 handler 函数返回,这块内存可能被下一个请求复用 —— 所以你不能把它直接赋值给全局变量、传进 goroutine、或塞进 map 里长期持有。
实操建议:
- 需要持久化时,用
utils.CopyString(c.Params("id"))或手动copy()字节切片再转string - 不要在中间件里调用
c.Params()后存到c.Locals再跨中间件读取——虽然c.Locals是线程安全的,但存进去的仍是易失效的引用 - 如果只是做日志或简单判断,直接用没问题;但涉及异步、缓存、数据库写入等场景,必须拷贝
为什么 :id 不校验格式,以及怎么加校验
Fiber 的 :id 本质是占位符,匹配逻辑只检查“该段是否非空且不含 /”,不做任何正则或类型判断。所以 /user/abc、/user/123abc 都能成功绑定到 c.Params("id")。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
如果你需要数字 ID 校验,有两条路:
- 手动解析:在 handler 中用
strconv.Atoi(c.Params("id")),失败则c.Status(400).SendString("invalid id") - 升级到 Fiber v3 并使用自定义约束:
app.Get("/user/:id<int>", handler)</int>,需提前调用fiber.RegisterCustomConstraint("int", ...)
v2 用户没有内置约束机制,硬加校验只能靠 handler 内部逻辑,别指望路由层拦截。
StrictRouting 对参数提取没影响,但影响路径结构
StrictRouting: true 只控制是否允许 /api/users 和 /api/users/ 视为同一路径。它不改变参数提取行为 —— /user/:id 在 strict 模式下依然能匹配 /user/123,只是不会自动把 /user/123/ 重定向到 /user/123。
真正要注意的是:开启 StrictRouting 后,/user/:id 和 /user/:id/ 是两个独立路由节点。如果你注册了前者,又用后者发请求,就会 404 —— 因为基数树里压根没建那个带尾部斜杠的分支。
容易被忽略的一点:Fiber 默认关闭 StrictRouting,所以 /user/123 和 /user/123/ 都能命中同一个 :id 节点;但如果你显式启用了它,就得确保前端 URL 构造和后端路由定义完全一致,否则参数看似“消失了”,其实是根本没匹配上节点。


















