竹简文档
辅助中间件

Panic 恢复

PanicRecovery 捕获异常并返回统一错误响应

Panic 恢复

PanicRecovery 全局 Panic 捕获与恢复中间件,防止服务因未处理的异常而崩溃,并返回统一的 JSON 错误响应。

PanicRecovery

panic_recovery.go
func PanicRecovery() gin.HandlerFunc

实现:

panic_recovery.go
func PanicRecovery() gin.HandlerFunc {
    return gin.RecoveryWithWriter(io.Discard, func(c *gin.Context, recovered interface{}) {
        // 1. 从上下文提取错误码
        value, exists := c.Get(xCtx.ErrorCodeKey.String())
        getErrMessage, msgExist := c.Get(xCtx.ErrorMessageKey.String())

        // 2. 默认错误码: ServerInternalError (50000)
        errorCode := xError.ServerInternalError
        if exists && value != nil {
            if ec, ok := value.(*xError.ErrorCode); ok && ec != nil {
                errorCode = ec
            }
        }

        // 3. 默认错误消息
        if !msgExist {
            getErrMessage = "未知错误,请稍后再试"
        }

        // 4. 记录日志
        xLog.WithName(xLog.NamedMIDE).Error(c.Request.Context(), "Panic 恢复", ...)

        // 5. 返回统一 JSON 响应
        c.JSON(int(errorCode.Code/100), xBase.BaseResponse{...})
        c.Abort()
    })
}

错误码提取

中间件会尝试从上下文获取错误信息:

字段

类型

响应格式

{
  "context": "abc123-uuid",
  "output": "SERVER_INTERNAL_ERROR",
  "code": 50000,
  "message": "服务器内部错误",
  "error_message": "具体错误描述"
}

响应字段说明

字段

类型

使用示例

基础使用

main.go
func main() {
    router := gin.New()

    router.Use(xHelper.RequestContext())
    // 注册 Panic 恢复中间件
    router.Use(xHelper.PanicRecovery())
    router.Use(xHelper.HttpLogger())

    router.Run(":8080")
}

测试 Panic 恢复

handler/test.go
func TestPanic(ctx *gin.Context) {
    // 触发 Panic
    panic("测试 Panic 恢复")
}

响应:

{
  "context": "550e8400-e29b-41d4-a716-446655440000",
  "output": "SERVER_INTERNAL_ERROR",
  "code": 50000,
  "message": "服务器内部错误",
  "error_message": "未知错误,请稍后再试"
}

带错误码的 Panic

handler/user.go
func GetUser(ctx *gin.Context) {
    // 设置错误码后再 Panic
    ctx.Set(xCtx.ErrorCodeKey.String(), xError.NotFound)
    ctx.Set(xCtx.ErrorMessageKey.String(), "用户不存在")
    panic("user not found")
}

响应:

{
  "context": "550e8400-e29b-41d4-a716-446655440000",
  "output": "NOT_EXIST",
  "code": 40004,
  "message": "数据不存在",
  "error_message": "用户不存在"
}

日志输出

Panic 发生时会记录 ERROR 级别日志:

2024-01-15 10:30:00.123 [ERRO] [abc123] [MIDE] Panic 恢复
    code=50000
    output=SERVER_INTERNAL_ERROR
    message=服务器内部错误
    errorMessage=未知错误,请稍后再试
goroutine 1 [running]:
...

工作流程

注意事项

中间件顺序

// PanicRecovery 应该在 HttpLogger 之前
router.Use(xHelper.RequestContext())
router.Use(xHelper.PanicRecovery())  // 先注册
router.Use(xHelper.HttpLogger())      // 后注册

与 xError 配合

推荐使用 xError.NewError 而不是直接 Panic:

// 推荐:使用 xError
ctx.Error(xError.NewError(ctx.Request.Context(), xError.NotFound, "用户不存在", true))
return

// 不推荐:直接 Panic
panic("user not found")

下一步

On this page