错误处理:gerror 创建/包装/Is/Equal 与错误码思路
引言
Go 原生错误只是 error 接口,缺少堆栈与分类信息。GoFrame 的 gerror 在错误上叠加了堆栈、错误码等能力,gcode 提供错误码定义,二者是框架错误处理的基石。
概念
- 创建错误:
gerror.New("...")/gerror.Newf(...),错误自带调用堆栈。 - 包装错误:
gerror.Wrap(err, "...")保留原始错误并追加一层描述,便于逐层上报。 - 取根因:
gerror.Cause(err)返回链条最底层的原始错误。 - 比较错误:
gerror.Is(err, target)沿包装链查找是否包含目标错误;gerror.Equal(err1, err2)判断两个错误是否相等(比较规则以官方文档 gerror 章节为准)。 - 错误码:
gcode.New(code, message)定义业务错误码,gerror.NewCode(code, ...)创建带码错误;框架自身的错误也都带 gcode 错误码。
代码示例
创建与包装:
package main
import (
"fmt"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// 定义业务错误码(建议避开框架内置码段,自行规划编号)
var ErrUserNotFound = gerror.NewCode(gcode.New(10001, "用户不存在"))
func findUser(id int) error {
if id <= 0 {
return gerror.New("非法用户 ID")
}
return nil
}
func main() {
err := findUser(0)
// 包装:保留原错误与堆栈,同时追加上下文
wrapped := gerror.Wrap(err, "查询用户失败")
fmt.Println(wrapped) // 打印整条错误链
fmt.Println(gerror.Stack(wrapped))
// 取出根因
fmt.Println(gerror.Cause(wrapped))
// 与带码错误比较
err2 := ErrUserNotFound
fmt.Println(gerror.Is(err2, ErrUserNotFound))
fmt.Println(gerror.Equal(err2, ErrUserNotFound))
}
在 Web/服务层把错误码带出去,是"统一错误返回"的基础:
func handler() error {
_, err := findUser(-1)
if err != nil {
// 转成带业务错误码的错误,交给统一返回处理
return gerror.WrapCode(gcode.New(10001, "用户不存在"), err, "查询用户")
}
return nil
}
注意点
- 用
Wrap而非直接fmt.Errorf("%v: %w", ...)的%w方式包装,能同时保留 gerror 的堆栈与错误码。 gerror.Is与gerror.Equal的判断维度(错误码、消息、链上查找)以官方文档为准,涉及跨包比较时先确认比较对象。- 自定义业务错误码建议统一编号段管理(如 1xxxxx 业务、9xxxxx 系统),便于日志与告警归类。
- 框架内组件返回的错误大多实现了 gerror 接口,可直接用
gerror.Cause提取底层原因。
小结
gerror 负责"错误长什么样"(堆栈、链、码),gcode 负责"错误怎么分类"。养成"底层 Wrap、上层判断、出口带码"的习惯,错误处理会清晰很多。完整方法清单以官方文档 goframe.org 的 gerror 章节为准。