错误码设计:框架预留段与业务段划分
面试问法:GoFrame 的错误码体系怎么设计?框架预留了哪些段? 一句话结论:官方约定
<1000为框架预留段,业务错误码使用>1000;用gcode.Code承载“数字 + 消息 + 详情”,gerror.NewCode抛出并保留堆栈,统一响应中间件从错误链取码输出{code,message,data}。业务侧再按模块分段维护常量表,杜绝魔法数字。
段位划分建议
| 段位 | 归属 | 示例 |
|---|---|---|
| 0 | 成功/无错误(CodeNil) | code=0 表示成功 |
| <1000 | 框架预留(内置错误码与常用错误) | 具体数值以官方 gcode 文档为准 |
| 1000+ | 业务通用 | 1001 参数错、1002 未登录 |
| 2000+ | 按模块细分 | 用户 2xxx、订单 3xxx… |
落地三件套
// 1. 常量包:独立 internal/code 包集中管理
var (
UserNotExist = gcode.New(1001, "用户不存在", nil)
UserPasswordErr = gcode.New(1002, "用户名或密码错误", nil)
OrderClosed = gcode.New(2001, "订单已关闭", nil)
)
// 2. 业务层只抛业务码,不拼 HTTP 细节
if user == nil {
return nil, gerror.NewCode(code.UserNotExist) // gerror 保留堆栈
}
// 3. 统一响应中间件:成功 code=0;失败从错误链取码并记日志
func HandlerResponse(r *ghttp.Request) {
r.Middleware.Next()
if r.Response.BufferLength() > 0 { return } // 业务已写回
if err := r.GetError(); err != nil {
c := gerror.Code(err) // 链上带业务码则原样透出
g.Log().Errorf(r.Context(), "%+v", err)
r.Response.WriteJson(g.Map{"code": c.Code(), "message": err.Error()})
return
}
r.Response.WriteJson(g.Map{"code": 0, "message": "OK", "data": r.GetHandlerResponse()})
}
注意点
- HTTP 状态码与业务码解耦:业务失败可返回 200 + 非零 code;需要 401/403/404 语义时由中间件按错误码映射状态码。
- 对客户端展示的 message 要友好、不泄露内部细节;完整堆栈只进服务端日志。
- 需要错误归类/判断时用
gerror.Is/Equal或包装错误链,别用字符串匹配。
常见追问
- 追问:为什么业务码要留到 1000 以上?——避开框架内置码,避免中间件取码时把框架错误与业务错误混淆;数字本身无业务含义,配常量与文档表使用。
- 追问:错误文案要给前端直接展示吗?——最好分开:给用户看的提示与内部定位信息各存一份(code + userMessage + 日志 detail)。
记忆点
- 预留段 <1000 归框架、业务 >1000;gcode.New + gerror.NewCode + 统一响应中间件三件套。
- 内置码的具体数值与常量命名以官方 gcode/gerror 文档为准。