中间件函数签名必须是func(http.Handler) http.Handler,因Go标准库仅识别http.Handler接口;错误写法如func(http.HandlerFunc) http.HandlerFunc会导致类型不匹配编译失败,正确做法是内部用http.HandlerFunc包装逻辑并确保返回值实现ServeHTTP方法。

中间件函数签名必须是 func(http.Handler) http.Handler
Go 标准库不认“中间件”这个概念,只认 http.Handler 接口。任何想链起来的函数,入参和返回值都得是它——否则 http.Handle 或 http.ListenAndServe 直接编译报错:cannot use myMW(handler) (type http.HandlerFunc) as type http.Handler。
常见错误是写成 func(http.HandlerFunc) http.HandlerFunc:看似能链,但返回值只是类型别名,没实现 ServeHTTP 方法,无法被标准库接收。
- 正确做法:内部用
http.HandlerFunc包逻辑,但最终return的必须是满足http.Handler接口的值(比如闭包转成的http.Handler) - 更安全的写法是显式构造结构体,如
&myHandler{next: next},并在其ServeHTTP方法里调用next.ServeHTTP(w, r) - 别漏掉
return;漏了会导致 handler 为 nil,请求卡住、无响应、无错误日志,只能靠超时发现
next.ServeHTTP(w, r) 的位置决定中间件语义
这不是装饰器里的魔法,是你手动控制流程的唯一开关。放哪儿,就决定了它是前置拦截、改写后放行,还是后置处理。
- 放在最前面:做拦截判断,比如
if !valid(r) { http.Error(w, "401", 401); return },之后代码不执行 - 放在中间:先改请求再交出去,比如
r = r.WithContext(context.WithValue(r.Context(), key, value)),再传给next.ServeHTTP(w, r) - 放在最后:做耗时统计、写响应头等后置动作,但注意:
w.Header().Set()在响应已写出后会 panic
日志中间件若想记录完整耗时,必须在 next.ServeHTTP 前后都打点,且开始时间要提前获取。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
链式嵌套顺序就是洋葱模型的执行顺序
写成 auth(logging(handler)),就表示请求先进 auth,再进 logging,最后到 handler;响应则按反向出来:handler → logging → auth。
- 鉴权失败不调
next.ServeHTTP,后续中间件和业务逻辑彻底跳过 -
recovery类中间件必须在最外层(如recovery(auth(logging(handler)))),否则内层 panic 会穿透到http.Server导致进程退出 - body 解析类中间件(如读取并重放
r.Body)必须放在所有依赖原始Body的中间件之前,否则下游读到空io.ReadCloser
手写嵌套容易括号错位,建议用切片倒序组装:for i := len(mws)-1; i >= 0; i-- { h = mws[i](h) },比 m1(m2(m3(h))) 更可靠。
ResponseWriter 被多个中间件共享,改写需谨慎
所有中间件看到的是同一个 http.ResponseWriter,一旦某个中间件调用了 w.WriteHeader(401),后续中间件再调 w.Header().Set() 就会 panic:http: multiple response.WriteHeader calls。
- 需要拦截响应体或统一加 header 的场景,必须包装
ResponseWriter(如自定义responseWriter结构体) - Go 的
http.ResponseWriter接口没有StatusCode()方法,无法直接判断是否已写 header,得靠包装时记录状态 - 后置中间件若要改 header,必须确保
next.ServeHTTP还没真正写出响应——这要求你清楚下游行为,或用包装 writer 拦截WriteHeader调用
最容易被忽略的是:panic 恢复必须配合 defer 注册在 next.ServeHTTP 之前,且恢复逻辑本身不能 panic,否则整个 recover 失效。

















