如何在Golang中捕获Web请求异常_统一返回错误响应

统一捕获请求异常需中间件拦截+错误包装+标准化响应结构,定义ErrorResponse含code/message/data,用recover捕获panic并转JSON响应,Gin中替换Recovery并规范c.Error处理业务错误,HTTP状态码与业务码分层设计。

在 Go Web 开发中,统一捕获请求异常并返回标准化错误响应,关键在于中间件拦截 + 错误包装 + 统一响应结构。不依赖框架时,用 http.Handler 装饰器即可实现;使用 Gin 或 Echo 等框架时,则利用其内置的 Recovery 和自定义错误处理机制。

定义统一错误响应结构

先约定前后端可解析的错误格式,包含状态码、错误码、消息和可选详情:

type ErrorResponse struct {
    Code    int    `json:"code"`    // 业务错误码,如 1001
    Message string `json:"message"` // 用户友好的提示
    Data    any    `json:"data,omitempty"`
}

func (e *ErrorResponse) WithData(data any) *ErrorResponse {
    e.Data = data
    return e
}

// 快捷构造函数
func NewError(code int, msg string) *ErrorResponse {
    return &ErrorResponse{Code: code, Message: msg}
}

使用中间件全局捕获 panic 和错误

对标准 net/http,编写一个 recover 中间件,拦截 panic 并转为 500 响应;同时支持手动注入错误(如通过 context 传递):

  • defer + recover() 捕获 panic,记录日志并返回 500 Internal Server Error
  • 在 handler 内部主动调用 http.Error 或写入自定义错误响应前,先检查是否已写 header,避免重复写入
  • 推荐将错误通过 context.WithValue 注入,再由中间件读取并渲染,避免每个 handler 都手动写错误逻辑

Gin 框架下的标准做法

Gin 自带 gin.Recovery() 中间件,但默认只打印 panic 日志。要统一返回 JSON 错误,需自定义 Recovery 函数:

  • 替换默认 Recovery:r.Use(customRecovery)
  • customRecovery 中,recover() 后构造 ErrorResponse,调用 c.AbortWithStatusJSON(500, errResp)
  • 同时注册全局错误处理:用 c.Error(err) 抛出业务错误,再通过 c.Errors.ByType(gin.ErrorTypePrivate) 在最后统一渲染

业务层主动返回错误的规范方式

避免在 handler 里直接 returnpanic,而是封装错误工具函数:

  • 定义错误变量或错误工厂,如 ErrUserNotFound = errors.New("user not found")
  • handler 中检测失败后,调用 c.Error(errors.Join(ErrUserNotFound, err)),或直接 c.AbortWithStatusJSON(404, NewError(2004, "用户不存在"))
  • 配合 validator 使用:Gin 的 ShouldBind 失败时自动调用 c.Error(),你只需在 Recovery 后统一处理

不复杂但容易忽略的是:HTTP 状态码与业务错误码要分层设计——状态码表示协议层结果(4xx/5xx),业务码(如 1001)用于前端路由或提示逻辑。两者都应在响应中明确体现。