Chi 路由器不支持直接注册自定义匹配器,需通过中间件拦截请求并手动判断路径、方法、Header 等;版本路由应使用 r.Group + r.Mount 实现;chi.URLParam 仅对花括号声明的占位符有效;中间件中终止请求须写响应后立即 return。

Chi 路由器怎么注册自定义匹配器?
Chi 本身不提供类似 http.ServeMux 那种自由注册任意函数的接口,它的路由匹配是基于预编译的正则路径模板(如 /users/{id:\d+})和内置的 chi.Router 结构驱动的。真要“自定义匹配逻辑”,得绕过路径模板,用中间件 + chi.Route 或 chi.Group 手动拦截。
常见错误是试图在 chi.Get("/path", handler) 里传入一个返回 bool 的函数——这根本不会生效,chi.Get 第二个参数必须是 http.HandlerFunc,不是匹配器。
- 真正能插手匹配时机的位置只有:中间件中读取
r.URL.Path和r.Method,然后调用next.ServeHTTP(w, r)或直接写响应 - 如果需要按 Host、Header、Query 参数做分流,应在
chi.MiddlewareFunc里判断,而不是塞进路由定义 -
chi.With只能加中间件,不能改匹配行为;想动态开关某段路由,用if包裹r.Get(...)更直接
如何让 Chi 支持 /api/v1/users/123 这类带版本前缀的路由?
Chi 没有原生“API 版本路由”概念,但靠嵌套路由(chi.Group)可以干净实现,且天然支持中间件隔离。
容易踩的坑是把版本号写死在每个 handler 里做字符串切片判断,既难维护又丢失路由语义。正确做法是用 r.Group 提前剥离前缀,再注册子路由:
立即学习“go语言免费学习笔记(深入)”;
api := r.Group(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 可选:统一加 version header 或日志
w.Header().Set("X-API-Version", "v1")
next.ServeHTTP(w, r)
})
})
api.Use(versionMiddleware) // 比如校验 Accept: application/vnd.myapp.v1+json
api.Get("/users/{id}", userHandler)
注意:api.Get("/users/{id}") 实际匹配的是完整路径 /api/v1/users/123,前提是外层 r.Mount("/api/v1", api) ——没调用 Mount 就不会生效。
为什么 chi.URLParam(r, "id") 返回空?
不是所有路径参数都能用 chi.URLParam 拿到,它只对在路由定义中显式声明的占位符有效,比如 /users/{id} 中的 id。如果路径是 /users/123 但路由写成 /users/* 或 /users/,chi.URLParam(r, "id") 一定为空。
- 检查路由是否用了花括号语法:
r.Get("/posts/{slug}", handler)✅,r.Get("/posts/:slug", handler)❌(这是 Gin 写法,Chi 不认) - 确保 handler 是通过 Chi 的
http.HandlerFunc注册的,不是直接丢给http.ListenAndServe - 如果路径含多个占位符(如
/orgs/{org}/repos/{repo}),每个名字必须唯一,重复会导致后一个覆盖前一个
Chi 中间件里怎么提前终止请求并返回 JSON 错误?
Chi 中间件本质是包装 http.Handler,所以可以在任意位置调用 w.WriteHeader() + json.NewEncoder(w).Encode(...),但必须确保没调用 next.ServeHTTP,否则会二次写 body 导致 panic。
典型错误是写了 w.WriteHeader(401) 却忘了 return,导致后续 handler 继续执行并尝试写 header。
- 标准写法是:判断失败 → 写状态码和 body →
return(中断链) - 别用
http.Error,它内部会调用http.Error并可能触发默认 HTML 响应,破坏 API 一致性 - 如果中间件需要复用(比如 auth),建议封装成函数返回
chi.MiddlewareFunc,避免重复写if err != nil { ... return }
复杂点在于:Chi 的中间件链是线性的,没有“跳过后续中间件但继续 handler”的机制。一旦你决定拦截,就必须全权负责响应,没法只改 header 后放行——这点和 Express 的 next('route') 不同。











