HTTP错误响应必须封装成结构体,统一包含status_code、message、error_code字段;禁用http.Error,需显式设置Content-Type和状态码;用中间件recover panic并AbortWithStatusJSON;业务错误用自定义AppError类型区分状态码;鉴权错误须由中间件前置拦截并返回标准401/403。

HTTP错误响应必须封装成结构体,不能直接返回字符串或裸error
Go的http.Error虽然能快速返回错误,但会导致前端无法解析结构化数据。统一错误响应的核心是定义一个标准错误结构体,确保所有接口返回一致的status_code、message、error_code字段。
- 避免用
http.Error(w, "not found", http.StatusNotFound)——它只写入纯文本,Content-Type默认是text/plain,前端拿不到JSON字段 - 必须手动设置
w.Header().Set("Content-Type", "application/json; charset=utf-8") -
状态码要通过
w.WriteHeader(statusCode)显式设置,不能依赖json.Marshal自动设
用中间件拦截panic和未捕获error,避免服务崩溃
Web服务中未处理的panic会导致goroutine终止,但HTTP handler仍会返回500且无有效错误信息。中间件是兜底关键。
func Recovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, map[string]interface{}{
"error_code": "INTERNAL_ERROR",
"message": "server error",
"status_code": http.StatusInternalServerError,
})
}
}()
c.Next()
}
}
- 使用
defer + recover()捕获panic,但注意:仅对当前goroutine生效,不跨goroutine传播 - 不要在recover里调用
c.JSON()后继续c.Next()——必须用c.AbortWithStatusJSON()中断流程 - 若用原生
net/http,需包装http.Handler,类似http.HandlerFunc闭包模式
业务错误要用自定义error类型区分,而非全用fmt.Errorf
统一响应需要识别错误类型来映射HTTP状态码和错误码,比如ErrNotFound应返回404,ErrInvalidParam应返回400。
type AppError struct {
Code string
Message string
Status int
}
func (e *AppError) Error() string { return e.Message }
var (
ErrNotFound = &AppError{"NOT_FOUND", "resource not found", http.StatusNotFound}
ErrInvalidParam = &AppError{"INVALID_PARAM", "invalid request parameter", http.StatusBadRequest}
)
- 不要用
errors.Is(err, ErrNotFound)做深层判断——如果错误被fmt.Errorf("failed: %w", err)包装过,errors.Is仍能穿透识别 - 在handler中用
switch或errors.As提取*AppError,再决定返回什么HTTP状态和JSON结构 - 数据库层(如
gorm)的record not found错误需主动转为ErrNotFound,不能透传原始error
HTTP 401/403等认证鉴权错误必须由中间件提前拦截
权限校验失败不能等到业务逻辑里才返回错误,否则可能已执行副作用(如扣款、发消息)。Auth中间件应在c.Next()前就决策是否放行。
立即学习“go语言免费学习笔记(深入)”;
- JWT解析失败、token过期、scope不足等,应统一返回
401 Unauthorized或403 Forbidden,并带明确error_code(如"TOKEN_EXPIRED") - 不要在每个handler里重复写
if !authorized { ... }——抽取为AuthRequired()中间件 - 注意:401响应必须带
WWW-Authenticate头(如w.Header().Set("WWW-Authenticate", "Bearer")),否则部分客户端(如curl -v)无法识别认证失败语义
Error方法只是快捷封装,真正可控的错误流必须自己管理error类型、状态码映射和响应格式。最容易被忽略的是:**HTTP状态码和JSON body里的status_code字段必须保持一致,且不能靠客户端自行推断**——后者会导致前端异常处理分支错乱。










