推荐用结构体实现error接口来定义自定义错误码,如AppError含Code、HTTPStatus、Message和Err字段,配合常量枚举与统一响应封装,确保可识别、可扩展、安全可控。

在 Go 中实现自定义错误码,核心是让错误具备可识别的类型、可携带的上下文(如码值、消息、HTTP 状态等),并支持统一处理(比如日志记录、API 响应封装)。推荐用 结构体实现 error 接口 的方式,比纯常量更灵活、可扩展;常量可作为辅助,用于定义清晰的错误码枚举。
用结构体实现可携带错误码的 error 类型
定义一个结构体(如 AppError),内嵌错误信息字段,并实现 Error() 方法。这样既能满足 Go 的 error 接口契约,又能附加业务语义。
- 包含字段:错误码(
Code int)、HTTP 状态码(HTTPStatus int)、用户提示(Message string)、原始错误(Err error,用于链式错误追踪) -
Error()方法返回用户不友好的底层错误描述(供日志/调试用),而非Message,避免敏感信息外泄 - 提供构造函数(如
NewAppError(code, msg, httpStatus))和包装函数(如Wrap(err, code, msg))提升可读性
用常量定义错误码枚举,提升可维护性
将所有业务错误码集中定义为具名常量,避免魔法数字散落各处。配合 iota 可自动递增,也支持显式赋值以预留扩展空间。
- 按模块或场景分组,例如:
const ( UserNotFound = 1001; InvalidPassword = 1002; OrderAlreadyPaid = 2001 ) - 可搭配字符串映射(
map[int]string)或方法(func (c ErrorCode) String() string)实现错误码转描述 - 注意:常量本身不构成 error,需与结构体组合使用(如
return &AppError{Code: UserNotFound, Message: "用户不存在"})
统一错误处理与响应封装
在 HTTP handler 或中间件中,对返回的 error 进行类型断言,提取结构体中的 Code 和 HTTPStatus,生成标准 JSON 响应。
立即学习“go语言免费学习笔记(深入)”;
- 使用
errors.As()判断是否为*AppError,避免直接类型断言导致 panic - 默认 fallback:非
AppError的 error(如io.EOF、json.UnmarshalError)视为服务端内部错误,返回通用码(如 50000)和 500 状态 - 可扩展:支持国际化(根据请求头选择
Message语言版本)、自动上报错误码统计
避免常见误区
结构体错误不是“越重越好”,关键在职责清晰和易用性。
- 不要把结构体设计成大而全的“错误中心”,只保留必要字段(码、状态、提示、原始 err)
- 不要在
Error()方法里返回Message—— 它面向开发者,不是终端用户 - 不要用常量替代错误类型:仅靠
if err == ErrNotFound难以携带动态上下文(如用户 ID),结构体才能承载 - 不要忽略错误链:用
fmt.Errorf("xxx: %w", err)包装时,确保AppError支持Unwrap()方法以便调试










