Go-Zero项目中通过定义CommonResponse结构体、封装Success/Error响应函数、引入CodeError错误类型并注册全局错误处理器,实现所有HTTP接口统一返回code/msg/data格式,且HTTP状态码恒为200。

在Go-Zero项目中让所有HTTP接口返回一致的code/msg/data结构,避免前端反复适配不同格式,同时确保业务错误码能穿透到响应体而非仅靠HTTP状态码区分。
定义统一响应结构体
在internal/types/response.go中创建CommonResponse结构体,字段必须导出且带JSON标签:
type CommonResponse struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` }
注意Data字段加omitempty标签——空值(nil、零值map/slice)不会出现在JSON中,前端解析更安全;不加会导致"data":null或"data":{}冗余输出。
封装成功与失败响应函数
方法一:基础版辅助函数(适合小项目)
在internal/handler/response.go中写两个函数:
func Success(data interface{}) CommonResponse { return CommonResponse{Code: 0, Message: "success", Data: data} }
func Error(code int, msg string) CommonResponse { return CommonResponse{Code: code, Message: msg, Data: nil} }
方法二:错误对象驱动(推荐,支持链式扩展)
新建internal/errorx/codeerror.go:
type CodeError struct { Code int `json:"code"` Msg string `json:"msg"` }
func (e *CodeError) Error() string { return e.Msg }
func New(code int, msg string) *CodeError { return &CodeError{Code: code, Msg: msg} }
这一步必须用指针返回——否则调用Error()方法时无法修改内部状态,后续中间件捕获会失效。
注入全局错误处理器
第一步:在main.go的srv := rest.MustNewServer(c.RestConf)之后立即注册:
httpx.SetErrorHandler(func(err error) (int, interface{}) { if e, ok := err.(*errorx.CodeError); ok { return http.StatusOK, response.Error(e.Code, e.Msg) } return http.StatusInternalServerError, response.Error(500, "internal error") })
第二步:确保httpx.OkJson被用于所有正常响应路径,而不是httpx.Error——因为httpx.Error会强制返回HTTP非200状态码,破坏“所有接口HTTP状态码恒为200”的设计前提。
【关键前提】所有handler中禁止直接调用http.Error(w, ...)或w.WriteHeader(400),否则会绕过统一响应封装,导致前后端约定失效。
在Handler中使用统一响应
① 解析请求时启用go-zero内置校验:
var req types.LoginReq if err := httpx.Parse(r, &req); err != nil { return err // 自动触发SetErrorHandler
② 业务逻辑返回错误时,用errorx.New(1001, "用户名已存在")构造错误,不要用fmt.Errorf。
③ 成功响应直接包装:
resp, err := l.Login(&req) if err != nil { return err } return &types.LoginResp{Token: resp.Token}, nil
这里返回nil错误 + 具体响应结构体,httpx.OkJson会自动套上CommonResponse外壳。


















