chi路由树需显式调用mount挂载子路由,子路由不自动注册;use仅作用于当前及后续挂载的子路由;with返回新路由实例实现轻量中间件定制;重复路径注册会静默覆盖,/users与/users/视为不同路由。

Chi 路由树必须显式调用 Mount 才能嵌套子路由
很多人以为 chi.Router 支持像 echo.Group 那样直接链式声明子路径,结果写完 r.Get("/api/users", handler) 发现 404——根本没注册进去。原因在于 Chi 的子路由是独立实例,不自动挂载到父路由上。
正确做法是:先创建子路由,再用 Mount 显式挂载到路径前缀下。否则子路由的路径永远无法被父路由器识别。
-
Mount第一个参数是路径前缀(如"/api"),必须以/开头且不以/结尾 - 子路由里写的路径是相对的,比如
subR.Get("/users", ...)最终匹配的是GET /api/users - 别把
Mount和Group混用——Chi 没有Group方法,那是 Gin 或 Echo 的概念
subR := chi.NewRouter()
subR.Get("/users", usersHandler)
subR.Post("/users", createUserHandler)
r.Mount("/api", subR) // ✅ 必须这一步
中间件顺序决定执行时机,Use 不等于全局生效
在 Chi 中,Use 只对「当前路由实例及其后续挂载的子路由」生效,不是“全站中间件”。如果在根路由调用 r.Use(authMw),但之后又 Mount 了一个没经过它的子路由,那部分请求就完全绕过了认证。
常见错误是把日志、恢复 panic 的中间件写在最外层,却忘了子路由里自己又调了一次 Use,导致重复执行或漏掉某一层。
立即学习“go语言免费学习笔记(深入)”;
- 中间件按
Use调用顺序入栈,请求时正向执行,响应时逆向执行(类似洋葱模型) - 子路由调用
Use后,它自己的中间件会插在父路由中间件之后、自身 handler 之前 - 想让某个中间件只作用于特定路径?直接在对应子路由上调用
Use,而不是堆在根上
With 创建临时中间件链,适合一次性定制逻辑
当某个接口需要额外校验、临时加 header、或者只对一组路由启用调试中间件时,With 比新建子路由更轻量。它返回一个新路由实例,所有后续注册都走这条带中间件的分支,但不污染原路由。
容易踩的坑是误以为 With 会修改原路由,或者把它和 Use 混用造成中间件叠加混乱。
-
With返回的是新路由,必须用新变量接收,比如authR := r.With(authMw) - 它不会影响
r本身,所以r.Get("/public", ...)和authR.Get("/private", ...)是两条平行路径 - 性能无额外开销——
With只是复制路由结构指针,不复制中间件函数体
debugR := r.With(debugLogMw)
debugR.Get("/debug/pprof", pprof.Handler())
路由冲突时 Chi 不报错,但后注册的会覆盖前注册的
Chi 允许重复注册相同方法 + 相同路径的 handler,比如两次 r.Get("/health", h1) 和 r.Get("/health", h2)。运行时不报错,但只有最后一个生效——而且没有任何提示。线上出问题时极难排查。
尤其在多人协作或模块化注册路由时,不同包可能无意中注册了同一路径,导致某个服务突然“失联”。
- Chi 默认不校验重复路由,开发阶段建议加个简单检查:遍历
r.Routes()(需导入github.com/go-chi/chi/v5/middleware) - 避免跨包直接操作全局路由,推荐用函数返回子路由,由主程序统一
Mount - 路径末尾斜杠敏感:
/users和/users/是两个不同路由,别指望自动重定向











