Go的HTTP中间件是函数套函数模式,用http.Handler包装另一Handler,在请求前后添加逻辑,本质为闭包+函数值;常见写法是返回http.HandlerFunc,如日志、鉴权、Header设置等,标准库net/http原生支持。

什么是Go的HTTP中间件,它长什么样
Go里没有“中间件”这个内置概念,它只是函数套函数的模式:用一个http.Handler包装另一个http.Handler,在真正处理请求前/后加逻辑。本质就是闭包+函数值。
常见写法是返回http.HandlerFunc,比如记录日志、校验Token、设置Header这些事,都靠它串起来。
别被“中间件”这个词唬住——它不是框架特供,标准库net/http完全支持,不需要任何第三方包。
怎么写一个带错误恢复的中间件
panic会导致整个HTTP服务崩溃,必须拦截。但recover()只能在defer里生效,所以中间件得在调用next.ServeHTTP()前后做手脚。
立即学习“go语言免费学习笔记(深入)”;
- 必须用
defer包裹recover(),且放在next.ServeHTTP()调用之前 - 不能直接
return,要手动写http.Error()或调用w.WriteHeader()+w.Write() - 注意:如果下游Handler已经写了header(比如状态码200),再调用
http.Error()会panic,得先检查w.Header().Get("Content-Type")或用ResponseWriter包装器
简版示例:
func Recovery(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
为什么链式调用时顺序很重要
中间件执行顺序 = 注册顺序,越靠前的越先执行(也越早退出)。比如Recovery应该放在最外层,而Auth应该在Logging之后——否则日志里看不到认证失败的请求细节。
典型错误是把Auth放最外,结果401请求连日志都不记;或者把Timeout放太内,导致超时逻辑被前面中间件阻塞。
- 推荐顺序:Recovery → Logging → Timeout → Auth → Router
- 每个中间件只做一件事,别在
Auth里顺手改Header,那是Headers中间件的活 - 调试时可加
log.Printf("[MIDDLEWARE] %s -> %s", name, r.URL.Path)快速定位哪一层卡住了
用http.StripPrefix和http.FileServer组合静态文件时踩过哪些坑
很多人想用中间件给http.FileServer加权限控制,结果发现StripPrefix一嵌套就404——根本原因是http.StripPrefix修改的是r.URL.Path,但FileServer内部又会基于原始路径拼接文件系统路径,中间件顺序错就会路径错乱。
- 必须把
StripPrefix作为最外层中间件,再套其他逻辑(比如鉴权) - 鉴权中间件里判断路径,要用
r.URL.Path(已被Strip过),而不是r.RequestURI - 如果用
os.Stat检查文件是否存在再放行,注意Windows下路径分隔符是\,建议统一用path.Clean()和filepath.Join()
正确链式写法:
handler := http.StripPrefix("/static/", http.FileServer(http.Dir("./assets/")))
handler = Auth(handler) // 这里r.URL.Path已无/static/
handler = Logging(handler)
http.Handle("/static/", handler)
中间件本身不复杂,难的是每层之间状态怎么透传、错误怎么归因、响应头怎么协作。别急着封装“通用中间件”,先搞清当前Handler到底拿到了什么、改了什么、又交给了谁。


















