go中间件链必须用func(http.handler) http.handler签名,通过函数嵌套组合;需显式调用next.servehttp(w,r),用context.withcontext更新request传递数据,客户端拦截唯一入口是roundtripper。

中间件链必须用 func(http.Handler) http.Handler 签名
Go 的中间件链不是“插件列表”,而是函数嵌套:每个中间件接收一个 http.Handler,返回另一个 http.Handler。漏掉这个签名,中间件就无法接入标准路由(如 http.ServeMux)或 http.Server。
常见错误是直接返回闭包而不包装:func(w http.ResponseWriter, r *http.Request) 类型不匹配 http.Handler 接口,编译报错 cannot use func literal as type http.Handler。
- 必须用
http.HandlerFunc显式转换,它实现了ServeHTTP方法 - 中间件内必须调用
next.ServeHTTP(w, r),否则请求链断裂,后续逻辑完全不执行 - 顺序决定执行时机:外层中间件先入、后出;
auth(logging(handler))实际执行顺序是 auth → logging → handler → logging → auth
RoundTripper 是客户端请求的唯一拦截点
服务端中间件(http.Handler 链)对出站 HTTP 请求完全无效。想记录、修改或 mock 所有 http.Client 发出的请求,唯一可靠入口是 http.RoundTripper 接口。
别试图包装 http.DefaultTransport 本身——它是 *http.Transport 类型,不能直接赋给 Client.Transport 字段(类型不匹配)。必须实现自定义结构体,嵌入并委托给真实 transport。
-
RoundTrip方法里可安全读取/修改req.URL、req.Header,但不可消费req.Body(会破坏下游读取) - 若需记录响应体,必须用
io.TeeReader或完整复制resp.Body,否则client.Do()后业务代码拿不到数据 - 响应未到达时
resp为nil,直接访问resp.StatusCode会 panic,务必先判空
不要用 for 循环拼接中间件链
把中间件写成切片再 for 循环调用,看似简洁,实则破坏闭包语义和执行控制流。Go 标准库不支持这种“扁平化”链式调用,会导致变量复用、状态污染、错误无法中断等问题。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
正确做法是函数式组合:从右往左嵌套,即 middleware1(middleware2(handler))。推荐封装为 Chain 工具函数:
func Chain(middlewares ...func(http.Handler) http.Handler) func(http.Handler) http.Handler {
return func(final http.Handler) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
final = middlewares[i](final)
}
return final
}
}
调用时保持清晰语义:Chain(logging, auth, metrics)(myHandler)。
Context 传递必须显式注入到 *http.Request
中间件之间共享数据(如 traceID、用户身份)不能靠全局变量或闭包捕获,必须通过 context.Context 并更新 *http.Request 实例。
常见错误是只改了 r.Context() 却没把新 context 再塞回 request:
- ❌ 错误:
ctx := context.WithValue(r.Context(), key, val)—— ctx 没被使用 - ✅ 正确:
next.ServeHTTP(w, r.WithContext(ctx))—— 新 request 传给下一层 - 注意:
context.WithValue的 key 建议用私有类型(非 string),避免键冲突
真正容易被忽略的是:http.Request 是不可变结构体指针,任何 context 修改都必须重新生成 request 实例,否则下层永远看不到变更。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










