Go标准库net/http无内置中间件机制,必须手动嵌套func(http.Handler)http.Handler签名的函数,漏调next.ServeHTTP会导致请求静默卡死,链式顺序决定执行流,闭包实现配置化,路由粒度控制且避免全局硬套。

Go 标准库 net/http 本身不提供中间件注册机制,所谓“中间件链”必须手动嵌套函数调用,且每个环节都得严格符合 func(http.Handler) http.Handler 签名——漏掉任何一环,请求就静默卡死。
中间件函数签名必须是 func(http.Handler) http.Handler
这是硬性约束,不是风格偏好。标准库的 http.ServeMux 和 http.ListenAndServe 只接受 http.Handler 类型;而中间件的本质,就是接收一个 http.Handler,返回另一个 http.Handler。
- 错误写法:
func logging()或func(w http.ResponseWriter, r *http.Request)—— 这俩都不是中间件,无法参与链式调用 - 正确骨架:
func logging(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ... }) } - 别试图绕过:哪怕你用
http.HandlerFunc包一层,也得确保外层函数返回的是http.Handler,否则http.Handle("/path", myMiddleware)会编译报错
链式调用顺序决定执行流,漏掉 next.ServeHTTP(w, r) 就断链
中间件链是洋葱模型:最外层最先拿到请求,也最后拿到响应。但每层都必须显式调用 next.ServeHTTP(w, r),否则下游完全收不到请求。
- 常见静默失败:写了日志、改了 header,但忘了这行,结果浏览器一直 loading,服务端也没 error 日志
- 提前终止要自己写响应:比如鉴权失败时,
http.Error(w, "Unauthorized", http.StatusUnauthorized)后不能跟next.ServeHTTP - 顺序敏感:想先校验 token 再打日志,就得写成
auth(log(handler)),而不是log(auth(handler))——后者在鉴权失败时根本不会记录请求路径
带参数的中间件必须靠闭包固化,不能塞进函数参数列表
Go 没有“中间件工厂”的语法糖,但可以用函数返回函数模拟配置化。如果把 timeout 时间或 header 名直接当参数传给中间件主函数,它就无法参与标准链式拼接。
立即学习“go语言免费学习笔记(深入)”;
- 错误写法:
func timeout(next http.Handler, d time.Duration) http.Handler—— 这个签名无法被auth(timeout(handler, 5*time.Second))调用 - 正确姿势:
func timeout(d time.Duration) func(http.Handler) http.Handler,再用timeout(5*time.Second)(handler) - 闭包变量安全:每次调用
timeout(...)都生成独立闭包,不会跨请求共享状态;但注意别在循环里直接引用迭代变量(如for _, m := range ms { chain = m(chain) }),要用局部变量拷贝
路由粒度控制中间件,别全局硬套
net/http 没有全局中间件概念。你只能对具体路由路径做包装,比如只给 /api/ 加 auth,而静态资源路径跳过。
- 手动嵌套示例:
http.Handle("/api/", auth(logging(apiHandler))),http.Handle("/static/", staticHandler) - 避免滥用
http.DefaultServeMux:它没法按路径分组加中间件,建议用独立http.ServeMux实例隔离逻辑 - 别碰
r.URL.Path做中间件路由判断:它已被解码,可能丢失原始编码信息;真正需要路径匹配(如权限白名单)才用它;日志记录优先用r.RequestURI或r.URL.EscapedPath()
最易被忽略的点:中间件里修改 r.Header 或 r.URL 后,若不 deep-copy 就传给下游,某些 handler(比如 http.StripPrefix 的内部逻辑)可能读到意外状态。真要改路径,优先用 http.StripPrefix 这类标准工具,而不是手撕 r.URL.Path。


















