全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-18 7 分钟 ✍️ juanwangdev

中间件错误处理

中间件是统一错误处理的最佳位置,可以实现集中式的错误响应。

c.Error 记录错误

Go
func handler(c *gin.Context) {
    if err := doSomething(); err != nil {
        c.Error(err) // 记录错误,不终止请求
    }
    c.JSON(200, gin.H{"status": "ok"})
}

收集错误统一处理

Go
func errorHandlerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next() // 执行后续处理

        // 收集所有错误
        if len(c.Errors) > 0 {
            errors := make([]string, 0)
            for _, e := range c.Errors {
                errors = append(errors, e.Error())
            }

            c.JSON(500, gin.H{
                "code":    500,
                "message": "处理过程中发生错误",
                "errors":  errors,
            })
            return
        }
    }
}

r.Use(errorHandlerMiddleware())

r.GET("/test", func(c *gin.Context) {
    c.Error(errors.New("错误1"))
    c.Error(errors.New("错误2"))
    // 最终返回包含所有错误的统一响应
})

错误类型标记

Go
type CustomError struct {
    Code    int
    Message string
}

func (e CustomError) Error() string {
    return e.Message
}

func handler(c *gin.Context) {
    err := CustomError{Code: 400, Message: "参数错误"}
    c.Error(err).SetType(gin.ErrorTypePublic) // 设置错误类型
}

// 中间件中按类型处理
func typeAwareMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()

        publicErrors := c.Errors.ByType(gin.ErrorTypePublic)
        privateErrors := c.Errors.ByType(gin.ErrorTypePrivate)

        if len(publicErrors) > 0 {
            c.JSON(400, gin.H{"errors": publicErrors.JSON()})
        }
        // private 错误只记录日志
        for _, e := range privateErrors {
            log.Printf("内部错误: %v", e)
        }
    }
}

错误类型常量

类型说明
gin.ErrorTypePublic公开错误,返回给客户端
gin.ErrorTypePrivate私有错误,仅记录日志
gin.ErrorTypeAny所有错误

AbortWithStatusJSON 快捷处理

Go
func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.AbortWithStatusJSON(401, gin.H{
                "code":    401,
                "message": "请先登录",
            })
            return
        }
        c.Next()
    }
}

Panic 捕获处理

Go
func recoveryMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("Panic: %v", err)
                c.AbortWithStatusJSON(500, gin.H{
                    "code":    500,
                    "message": "服务器内部错误",
                })
            }
        }()
        c.Next()
    }
}

业务错误统一处理

Go
// 定义业务错误结构
type BizError struct {
    Code    int
    Message string
}

func (e *BizError) Error() string {
    return e.Message
}

// 业务错误中间件
func bizErrorMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()

        // 检查是否有业务错误
        for _, e := range c.Errors {
            if bizErr, ok := e.Err.(*BizError); ok {
                c.JSON(bizErr.Code, gin.H{
                    "code":    bizErr.Code,
                    "message": bizErr.Message,
                })
                return
            }
        }
    }
}

// 使用示例
r.Use(bizErrorMiddleware())

r.GET("/order/:id", func(c *gin.Context) {
    order, err := getOrder(c.Param("id"))
    if err != nil {
        c.Error(&BizError{Code: 404, Message: "订单不存在"})
        return
    }
    c.JSON(200, order)
})

错误处理最佳实践

Go
// 统一错误响应结构
type ErrorResponse struct {
    Code    int      `json:"code"`
    Message string   `json:"message"`
    Details []string `json:"details,omitempty"`
}

// 全局错误处理
func globalErrorHandler() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()

        if c.IsAborted() {
            return // 已处理,跳过
        }

        if len(c.Errors) > 0 {
            details := c.Errors.JSON()
            c.JSON(500, ErrorResponse{
                Code:    500,
                Message: "服务处理错误",
                Details: details,
            })
        }
    }
}

c.Error() 不终止请求,适合收集多个错误统一处理;c.Abort() 立即终止,适合认证失败等场景。

要点总结

  • c.Error() 记录错误,请求继续执行
  • c.Errors 在中间件中统一处理所有错误
  • 使用 ErrorTypePublic/Private 区分公开和私有错误
  • c.AbortWithStatusJSON() 快捷终止并返回错误响应
  • Recovery 中间件捕获 panic 防止服务崩溃

📝 发现内容有误?点击此处直接编辑

← 上一篇 中间件性能优化与注意事项
下一篇 → 全局中间件与路由组中间件
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库