必须自定义panicrecovery中间件并置于最前,因gin.recovery()会提前吞掉panic;需recover→记录完整堆栈→abortwithstatusjson→中断流程,且error与panic须统一响应体系。

Gin 默认的 gin.Recovery() 只打日志、返回空白 500,根本没法对接前端或排查问题——你得自己写中间件,且必须放在中间件链最前面。
为什么不能直接用 gin.Default().Use() 加自定义 recovery
因为 gin.Default() 内部已经注册了 gin.Recovery(),它会在你的中间件之前执行并吞掉 panic,导致你写的 recover() 永远收不到值。调试时连堆栈都看不到,线上出问题只能靠猜。
- 开发阶段务必用
gin.SetMode(gin.DebugMode),否则 panic 被静默吃掉 - 生产环境必须用
gin.New()启动,手动注册中间件顺序:PanicRecovery()→gin.Logger()→ 其他 -
gin.Recovery()不调用c.Error(),也不触发你写的错误映射逻辑,纯属“摆设”
PanicRecovery() 中间件怎么写才真正可用
核心就四件事:recover → 记录堆栈 → 构造统一响应 → 立即中断。漏掉任何一环,不是日志没信息,就是响应错乱或重复写 body。
- 必须用
debug.Stack()获取完整堆栈,别只打印err(比如panic("xxx")时err是 string,不是error) - 响应必须用
c.AbortWithStatusJSON(500, ...),不能c.JSON()+c.Abort(),后者可能被后续中间件再处理一次 - 堆栈信息绝不能返回给前端,只存进日志字段(如
log.WithField("stack", string(debug.Stack()))) - HTTP 状态码固定用
500,业务错误码走响应体里的code字段,二者严格分离
如何把普通 error 和 panic 统一到同一套响应体系
光兜住 panic 不够。业务层抛出的 error(比如参数校验失败、DB 查询为空)也得走同一套 Fail() 函数,否则前端要写两套解析逻辑。
- 所有 handler 里不要出现
c.JSON(400, gin.H{...})这种硬编码,统一调用response.Fail(c, http.StatusBadRequest, errcode.ParamInvalid, "参数错误") - 自定义错误类型(如
AppError)要实现Error()方法,并带HttpStatus和Code字段,方便中间件识别 - 写一个
errorHandler()中间件,在c.Next()后检查c.Errors,遇到AppError就转成结构化响应;但它不能替代PanicRecovery,因为 panic 不会进c.Errors - 注意:
c.Error()只是往c.Errors里塞对象,不自动终止流程,必须配合c.Abort()才能阻止后续 handler 执行
最容易被忽略的是中间件顺序和 panic 堆栈的完整性——前者决定你能不能捕获到 panic,后者决定你上线后能不能在 3 分钟内定位到第 7 行的空指针。别省那几行代码。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











