在Golang项目里,标准库提供的error接口仅包含Error() string方法,实际业务中我们经常需要区分不同错误类型并附带业务错误码。为错误添加错误码能让调用方快速判断异常种类,也利于日志监控和前后端协作。

为什么需要错误码
原生error大多以字符串描述问题,例如fmt.Errorf("用户不存在")。当接口返回该错误时,前端只能展示文字,无法用程序逻辑区分是用户问题还是系统问题。引入错误码后,我们可以用数字或字符串常量标识错误类别。
- 便于程序分支判断,不用字符串匹配
- 统一错误语义,支持多语言映射
- 方便链路追踪与告警统计
设计自定义错误类型
我们可以定义一个结构体实现error接口,同时携带Code和Message字段。如下代码展示基础定义:
package errs
type BizError struct {
Code int
Message string
}
func (e *BizError) Error() string {
return e.Message
}
// New 创建带错误码的错误
func New(code int, msg string) *BizError {
return &BizError{Code: code, Message: msg}
}
包装原生error并提取错误码
很多底层函数返回普通error,我们需要将其转为带码错误。可以写一个辅助函数,若已是BizError则直接返回,否则包装为系统错误码。
package errs
import "errors"
const SysErr = 5000
func Wrap(err error, code int, msg string) error {
if err == nil {
return nil
}
if _, ok := err.(*BizError); ok {
return err
}
return &BizError{Code: code, Message: msg + ": " + err.Error()}
}
// CodeOf 提取错误中的错误码,非业务错误返回系统码
func CodeOf(err error) int {
var be *BizError
if errors.As(err, &be) {
return be.Code
}
return SysErr
}
使用示例
在业务代码中抛出和捕获错误码的方式如下:
package main
import (
"errors"
"fmt"
"errs"
)
func getUser(id int) error {
if id <= 0 {
return errs.New(1001, "用户ID非法")
}
return errors.New("db connection failed")
}
func main() {
err := getUser(0)
fmt.Println("错误码:", errs.CodeOf(err))
err2 := getUser(1)
wrapped := errs.Wrap(err2, 2000, "查询用户失败")
fmt.Println("包装后错误码:", errs.CodeOf(wrapped))
}
错误码规范建议
为避免混乱,团队应约定错误码段。例如下表所示:
| 区间 | 含义 |
|---|---|
| 1000-1999 | 用户相关错误 |
| 2000-2999 | 订单相关错误 |
| 5000 | 系统未知错误 |
通过自定义错误结构与辅助函数,Golang项目即可优雅地附加错误码,提升错误处理可读性与扩展性。