17370845950

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

HTTP错误响应必须封装成结构体,不能直接返回字符串或裸error

Go的http.Error虽然能快速返回错误,但会导致前端无法解析结构化数据。统一错误响应的核心是定义一个标准错误结构体,确保所有接口返回一致的status_codemessageerror_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.StatusI

nternalServerError, }) } }() 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中用switcherrors.As提取*AppError,再决定返回什么HTTP状态和JSON结构
  • 数据库层(如gorm)的record not found错误需主动转为ErrNotFound,不能透传原始error

HTTP 401/403等认证鉴权错误必须由中间件提前拦截

权限校验失败不能等到业务逻辑里才返回错误,否则可能已执行副作用(如扣款、发消息)。Auth中间件应在c.Next()前就决策是否放行。

  • JWT解析失败、token过期、scope不足等,应统一返回401 Unauthorized403 Forbidden,并带明确error_code(如"TOKEN_EXPIRED"
  • 不要在每个handler里重复写if !authorized { ... }——抽取为AuthRequired()中间件
  • 注意:401响应必须带WWW-Authenticate头(如w.Header().Set("WWW-Authenticate", "Bearer")),否则部分客户端(如curl -v)无法识别认证失败语义
Gin或Echo等框架的Error方法只是快捷封装,真正可控的错误流必须自己管理error类型、状态码映射和响应格式。最容易被忽略的是:**HTTP状态码和JSON body里的status_code字段必须保持一致,且不能靠客户端自行推断**——后者会导致前端异常处理分支错乱。