go中间件必须是func(http.handler) http.handler类型,返回http.handler;错误写法会导致类型不匹配或panic;正确需用http.handlerfunc包裹并转为http.handler。

中间件函数签名必须是 func(http.Handler) http.Handler
Go 没有中间件语法糖,所谓“中间件”就是接收一个 http.Handler、返回另一个 http.Handler 的函数。写成 func(http.HandlerFunc) http.HandlerFunc 看似能编译,但一旦嵌套多个中间件(比如日志 + 鉴权),下游中间件期望接收 http.Handler,而你传的是 http.HandlerFunc —— 类型不匹配,Go 不自动转换,注册时直接报错。
常见错误包括:
-
func auth(next http.Handler) { }—— 没返回值,http.ListenAndServe收不到有效 handler,启动 panic -
func auth(next http.Handler) func(http.ResponseWriter, *http.Request) { }—— 返回类型不是http.Handler,无法接入标准 handler 链
正确写法统一用 http.HandlerFunc 包裹闭包,再转为 http.Handler:
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
提前终止请求必须 return,且不能调用 next.ServeHTTP()
鉴权失败、参数校验不通过、限流触发等场景下,你写了 http.Error(w, "Unauthorized", http.StatusUnauthorized) 就得立刻 return。否则 next.ServeHTTP(w, r) 仍会执行,可能造成重复响应、数据误写或敏感信息泄露。
更隐蔽的问题是:在 next.ServeHTTP() 之后再写 http.Error(),此时 response header 可能已 flush,触发 http: multiple response.WriteHeader calls panic。
建议封装一个工具函数:
func writeError(w http.ResponseWriter, status int, msg string) {
if w.Header().Get("Content-Type") == "" {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
}
http.Error(w, msg, status)
}
读取 r.Body 前必须缓存,否则下游收不到数据
r.Body 是 io.ReadCloser,只能读一次。日志中间件里用 io.ReadAll(r.Body) 打印 body 后,下游 handler 再读就是空字节——这不是 bug,是设计使然。
调试时想看完整 body,必须手动缓存:
- 先
bodyBytes, _ := io.ReadAll(r.Body) - 再
r.Body = io.NopCloser(bytes.NewReader(bodyBytes))塞回去
注意:生产环境慎用,大文件或高并发下易 OOM;建议只采样或只记录长度 r.ContentLength。别用 r.ParseForm() 后再去读 r.Body,它内部已经读过一遍了。
跨中间件传值唯一安全方式是 context.WithValue()
别用全局变量或闭包捕获外部变量传参,这会导致并发冲突或数据污染。所有中间件之间共享的数据(如用户 ID、请求 ID)必须通过 context.Context 传递。
正确做法是:
- 在中间件中用
ctx := r.Context()获取原始上下文 - 用
ctx = context.WithValue(ctx, key, value)注入新值 - 用
r = r.WithContext(ctx)创建新请求对象 - 下游 handler 从
r.Context().Value(key)取值
key 必须是导出的、不可比较的类型(比如自定义 struct 或 type userIDKey struct{}),避免字符串 key 冲突。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











