go http中间件需在handler闭包内用defer+recover捕获panic,因http handler运行在独立goroutine中;recover后须检查并写入header与body避免连接卡死,禁用log.panicln等引发二次panic的方法,并注意servemux默认404不经过中间件。

Go HTTP 中间件里怎么捕获 panic
直接用 recover() 捕不到,因为 HTTP handler 是在独立 goroutine 里跑的,recover() 只对当前 goroutine 有效。必须在 handler 执行前就用 defer + recover() 包一层。
- 中间件函数本身要返回
http.Handler或闭包,不能只写个普通函数 - 真正执行业务逻辑的 handler(比如
func(w http.ResponseWriter, r *http.Request))必须被包裹在defer内部 - 别在中间件外层 defer,那只会 recover 中间件自己的 panic,不是下游 handler 的
recover 后怎么避免连接卡死或响应不完整
panic 发生时,HTTP 连接可能已写了一半 header,也可能完全没写。直接 return 会导致客户端一直等待超时。
- 先检查
w.Header().Get("Content-Type")是否为空,再调用w.WriteHeader(http.StatusInternalServerError) - 务必用
w.Write()或fmt.Fprint(w, ...)显式输出错误内容,否则某些反向代理(如 Nginx)会认为响应失败 - 如果用了
http.Hijacker或长连接流式写入,recover 后不能再写 body —— 此时连接状态已不可控,应直接关闭
log.Print 和 log.Panicln 在 recovery 中的区别
很多人用 log.Panicln() 想“把 panic 打出来”,结果触发二次 panic,中间件失效。
-
log.Print()、log.Printf()安全,只打日志 -
log.Panicln()、log.Fatal()会调用os.Exit()或 panic,绝对不要在 recover 块里用 - 推荐加 trace ID:用
req.Context().Value("trace_id")(如果已注入)或生成随机字符串,方便关联日志
为什么 recovery 中间件对 net/http.ServeMux 默认 404 不生效
因为 ServeMux 的默认 404 是在找不到路由时直接写响应,不进任何 handler,自然也不会进你的中间件。
- 如果你用的是
http.HandleFunc()注册的路由,中间件能覆盖到;但ServeMux自己兜底的 404 不走你 wrap 的 handler - 解决办法:自己实现一个
http.Handler,在ServeHTTP里先查路由,没命中再写 404,并确保整个流程在 defer-recover 范围内 - 更省事:换用
gorilla/mux或chi,它们的 NotFoundHandler 是可被中间件链拦截的
最常漏掉的是:recover 后没重置 response writer 的状态,导致后续中间件(比如 gzip、cors)误判 header 已写,跳过关键逻辑。实际处理时,要么保证 recover 后不再往下传 request,要么用 wrapper 类型把 writer 封装成可重置的。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











