统一捕获请求异常需中间件拦截+错误包装+标准化响应结构,定义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}
}
对标准 net/http,编写一个 recover 中间件,拦截 panic 并转为 500 响应;同时支持手动注入错误(如通过 context 传递):
defer + recover() 捕获 panic,记录日志并返回 500 Internal Server Error
http.Error 或写入自定义错误响应前,先检查是否已写 header,避免重复写入context.WithValue 注入,再由中间件读取并渲染,避免每个 handler
都手动写错误逻辑Gin 自带 gin.Recovery() 中间件,但默认只打印 panic 日志。要统一返回 JSON 错误,需自定义 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")
c.Error(errors.Join(ErrUserNotFound, err)),或直接 c.AbortWithStatusJSON(404, NewError(2004, "用户不存在"))
ShouldBind 失败时自动调用 c.Error(),你只需在 Recovery 后统一处理不复杂但容易忽略的是:HTTP 状态码与业务错误码要分层设计——状态码表示协议层结果(4xx/5xx),业务码(如 1001)用于前端路由或提示逻辑。两者都应在响应中明确体现。