Gin错误处理中间件
错误处理是Web应用稳定性的关键,Gin提供了多种错误处理机制。
Gin内置错误处理
Recovery中间件
Go
func Recovery() HandlerFunc {
return func(c *Context) {
defer func() {
if err := recover(); err != nil {
// 检查连接断开
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") ||
strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
c.Abort()
return
}
}
}
// 记录panic日志
log.Printf("[Recovery] panic recovered: %v\n%s", err, debug.Stack())
// 返回500错误
c.AbortWithStatus(500)
}
}()
c.Next()
}
}
Context错误收集
Error方法
Go
// 添加错误到Context
func (c *Context) Error(err error) *Error {
typedError, ok := err.(*Error)
if !ok {
typedError = &Error{
Err: err,
Type: ErrorTypePrivate,
}
}
c.Errors = append(c.Errors, typedError)
return typedError
}
// Error结构
type Error struct {
Err error
Type ErrorType
Meta any
}
type ErrorType uint64
const (
ErrorTypePublic ErrorType = 1 << 0 // 公开错误
ErrorTypePrivate ErrorType = 1 << 1 // 私有错误
ErrorTypeBinding ErrorType = 1 << 2 // 绑定错误
)
使用示例
Go
func handler(c *gin.Context) {
if err := validateInput(c); err != nil {
// 添加绑定错误
c.Error(err).SetType(gin.ErrorTypeBinding)
c.AbortWithStatusJSON(400, gin.H{"error": err.Error()})
return
}
if err := processBusiness(c); err != nil {
// 添加私有错误(不对外暴露详情)
c.Error(err).SetType(gin.ErrorTypePrivate)
c.AbortWithStatusJSON(500, gin.H{"error": "internal error"})
return
}
}
自定义错误处理中间件
统一错误响应
Go
func ErrorHandlerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
// 处理收集的错误
if len(c.Errors) > 0 {
err := c.Errors.Last()
// 根据错误类型返回不同响应
switch err.Type {
case gin.ErrorTypeBinding:
c.JSON(400, gin.H{
"code": 400,
"message": "参数验证失败",
"detail": err.Error(),
})
case gin.ErrorTypePublic:
c.JSON(500, gin.H{
"code": 500,
"message": err.Error(),
})
case gin.ErrorTypePrivate:
// 私有错误不暴露详情
log.Printf("Private error: %v", err.Err)
c.JSON(500, gin.H{
"code": 500,
"message": "服务内部错误",
})
}
}
}
}
全局异常捕获增强
Go
func EnhancedRecoveryMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// 获取请求信息
traceID := c.GetString("traceID")
path := c.Request.URL.Path
method := c.Request.Method
// 记录详细错误信息
log.Printf("[Panic] traceID=%s path=%s method=%s error=%v stack=%s",
traceID, path, method, err, debug.Stack())
// 发送错误通知
sendErrorNotification(err)
// 返回统一错误响应
if !c.Writer.Written() {
c.AbortWithStatusJSON(500, gin.H{
"code": 500,
"message": "服务内部错误",
"traceID": traceID,
})
}
}
}()
c.Next()
}
}
业务错误处理
自定义错误类型
Go
// 业务错误定义
type BusinessError struct {
Code int
Message string
Detail string
}
func (e *BusinessError) Error() string {
return e.Message
}
// 错误构造
func NewBusinessError(code int, message string) *BusinessError {
return &BusinessError{
Code: code,
Message: message,
}
}
// 使用
func handler(c *gin.Context) {
if err := processOrder(); err != nil {
if be, ok := err.(*BusinessError); ok {
c.Error(err).SetMeta(be)
c.AbortWithStatusJSON(be.Code, gin.H{
"code": be.Code,
"message": be.Message,
})
return
}
// 其他错误
c.AbortWithStatusJSON(500, gin.H{"error": "internal error"})
}
}
错误码标准化
Go
// 错误码定义
const (
ErrCodeBadRequest = 400001
ErrCodeUnauthorized = 401001
ErrCodeForbidden = 403001
ErrCodeNotFound = 404001
ErrCodeInternal = 500001
)
// 统一响应结构
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
TraceID string `json:"traceID,omitempty"`
}
func SendError(c *gin.Context, code int, message string) {
traceID := c.GetString("traceID")
c.JSON(code, ErrorResponse{
Code: code,
Message: message,
TraceID: traceID,
})
c.Abort()
}
错误处理最佳实践
| 错误类型 | 处理方式 | 响应状态码 |
|---|---|---|
| 参数验证错误 | gin.ErrorTypeBinding | 400 |
| 认证失败 | AbortWithStatusJSON | 401 |
| 权限不足 | AbortWithStatusJSON | 403 |
| 业务逻辑错误 | 自定义BusinessError | 400/409 |
| 内部错误 | gin.ErrorTypePrivate | 500 |
| Panic | Recovery捕获 | 500 |
中间件顺序
Go
r := gin.New()
// 1. Recovery必须在最外层
r.Use(EnhancedRecoveryMiddleware())
// 2. 错误响应中间件在最后
r.Use(ErrorHandlerMiddleware())
// 3. 其他中间件
r.Use(LoggerMiddleware())
// 业务路由
r.GET("/api", handler)
注意:Recovery必须作为第一个中间件,ErrorHandler在最后统一处理。
要点总结
- Recovery捕获panic,防止服务崩溃
- c.Error()收集错误,支持错误类型标记
- 错误类型区分公开/私有/绑定,控制对外暴露
- 自定义BusinessError实现业务错误标准化
- Recovery最外层,ErrorHandler最后执行
📝 发现内容有误?点击此处直接编辑