Recovery 中间件原理与自定义错误恢复

结论先行

Recovery 中间件在内部用 defer + recover 包住 c.Next():handler 链中任何 panic 都会冒泡到它,被捕获后打印堆栈并中止为 500(默认),避免整个进程崩溃。gin.Default() 自带 Logger+Recovery;要自定义响应/日志,用 gin.CustomRecoverygin.RecoveryWithWriter。注意:子 goroutine 里的 panic 它救不了,必须 goroutine 内部自行 recover。

关键点

主题说明
原理defer func(){ if err := recover(); err != nil {…} }() 包裹 c.Next()
默认行为打堆栈日志 → AbortWithStatus(500)
组合gin.New() 是空引擎;gin.Default() = New + Logger + Recovery
自定义gin.CustomRecovery(func(c, recovered){…}) 决定响应;RecoveryWithWriter 决定日志去向
位置放最外层才能包住后面注册的所有中间件与 handler
边界响应已部分写入时无法再改状态码,只能记日志

代码示例

func main() {
    r := gin.New()
    r.Use(gin.RecoveryWithWriter(os.Stderr, func(c *gin.Context, recovered any) {
        // 自定义 500 响应:不让内部 panic 原文泄漏给客户端
        c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "internal error"})
    }))
    r.Use(Auth(), Logger())

    r.GET("/boom", func(c *gin.Context) {
        panic("boom") // 被上方 Recovery 捕获 → 500
    })
    _ = r.Run(":8080")
}

常见追问 / 记忆点

  • 追问:Recovery 为什么能接到 handler 的 panic?答:Recovery 中间件先入链,它调 c.Next() 时 handler 还在同一调用栈上,panic 向上冒泡到它的 defer/recover。
  • 追问:goroutine 里 panic 会怎样?答:未被 recover 的 panic 会终止整个进程——Recovery 中间件帮不上忙,业务 goroutine 要自带 defer recover。
  • 追问:要不要把 panic 全转 500?答:运行时错误转 500 合理;但“配置缺失、资源未初始化”这类启动期错误应直接崩,别被吞掉掩盖问题。
  • 记忆点:defer+recover 包 Next、默认 500、放最外层、救不了子 goroutine。
笔记加载中…