Go中error是值而非异常,业务错误应返回error而非panic;panic仅用于致命错误且需recover;错误包装用%w保留原始类型;自定义错误须实现Error()方法;HTTP层需映射错误为状态码并脱敏敏感信息。
Go 里
不是异常,别用
拦错
Go 的错误处理逻辑和 Python/Java 完全不同:
是值,不是控制流。用
处理业务错误(比如文件不存在、参数校验失败)会导致调用栈炸开、无法被上层恢复,还容易掩盖真正该
的场景(如空指针解引用)。
常见错误现象:
被直接抛到 main 函数外,日志里只有一堆 goroutine stack trace,根本看不出是哪个模块在读配置。
业务错误一律返回
,由调用方决定重试、降级或透传
只用于程序无法继续运行的致命状态(如初始化失败、全局资源泄漏),且必须配
(仅限顶层 goroutine)
不要为了“简洁”把
写成
包装错误要用
+
,不是字符串拼接
错误链(error wrapping)是 Go 1.13 引入的关键能力,目的是保留原始错误类型和上下文。如果用
或
拼接,原始错误就丢了——你再也查不到底层是不是
,也无法用
或
判断。
使用场景:HTTP handler 中调用数据库,DB 层返回
,你想加一层 “user not found”,又得保留可判定性。
立即学习
“
go语言免费学习笔记(深入)
”;
正确写法:
错误写法:
检查包装后的错误是否为某类:
—— 只有用了
才有效
提取底层错误实例:
自定义错误类型要实现
方法,别只靠字段
当需要携带结构化信息(如错误码、trace ID、重试建议)时,光靠包装不够。但很多人只加字段不实现
,结果
打印出来是
这种难读格式,而且
无法匹配。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
性能影响:自定义错误对象本身开销极小;但若在高频路径(如每请求都 new 一个)中带大量字段或大结构体,会增加 GC 压力。
必须实现
,返回人类可读文本
如果需支持判定,额外实现
返回底层错误(用于
链式包装)
避免在
方法里做耗时操作(如 JSON 序列化、网络请求)
示例:
HTTP handler 里别裸露底层错误,要映射成状态码和响应体
把
或
直接塞进 HTTP 响应体,前端拿到的是 “permission denied” 或 “nil”,既不安全也不友好。更糟的是,有些错误(如 DB 连接超时)本该返回 503,却因没判断被当成 400 返回。
兼容性注意:不同中间件对错误的处理方式不同。Gin 默认把 panic 转 500,Echo 需手动注册
;标准库
则完全不管错误,全靠你自己写
分支。
建一个错误映射表:
用
匹配包装链里的任意一层,再查表定状态码
敏感错误(如 DB 密码错误、文件路径泄露)必须抹掉细节,统一返回泛化消息
别在 middleware 里吞掉所有
并返回 500——这会让前端无法区分是客户端错还是服务端错
最常被忽略的一点:错误变量命名。别叫
就完事,尤其在多 err 场景下(比如先查缓存再查 DB),
和
能立刻帮你避开「到底哪个 err 没判」的坑。
errorpanicerrorpanicpanicpanic: open config.yaml: no such file or directoryerrorpanicrecoverif err != nil { return err }if err != nil { panic(err) }fmt.Errorf%w+ " failed"fmt.Sprintfos.PathErrorerrors.Iserrors.Assql.ErrNoRowsreturn fmt.Errorf("get user %d: %w", id, err)return fmt.Errorf("get user %d: %s", id, err.Error())errors.Is(err, sql.ErrNoRows)%wvar perr *os.PathError; if errors.As(err, &perr) { ... }Error()Error()fmt.Println(err){Code:404 Message:"not found"}errors.Isfunc (e *MyError) Error() stringUnwrap() error%wError()type ValidationError struct { Code int; Field string }; func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: code %d", e.Field, e.Code) }os.IsPermission(err)redis.NilHTTPErrorHandlernet/httpif err != nilmap[error]int{sql.ErrNoRows: 404, io.ErrUnexpectedEOF: 400, ...}errors.IserrorerrcacheErrdbErr