统一捕获请求异常需中间件拦截+错误包装+标准化响应结构,定义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 函数:
立即学习“go语言免费学习笔记(深入)”;
- 替换默认 Recovery:
r.Use(customRecovery) - 在
customRecovery中,recover()后构造ErrorResponse,调用c.AbortWithStatusJSON(500, errResp) - 同时注册全局错误处理:用
c.Error(err)抛出业务错误,再通过c.Errors.ByType(gin.ErrorTypePrivate)在最后统一渲染
业务层主动返回错误的规范方式
避免在 handler 里直接 return 或 panic,而是封装错误工具函数:
- 定义错误变量或错误工厂,如
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)用于前端路由或提示逻辑。两者都应在响应中明确体现。










