如何使用Golang自定义错误码_通过结构体或常量区分错误类型

推荐用结构体实现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 进行类型断言,提取结构体中的 CodeHTTPStatus,生成标准 JSON 响应。

  • 使用 errors.As() 判断是否为 *AppError,避免直接类型断言导致 panic
  • 默认 fallback:非 AppError 的 error(如 io.EOFjson.UnmarshalError)视为服务端内部错误,返回通用码(如 50000)和 500 状态
  • 可扩展:支持国际化(根据请求头选择 Message 语言版本)、自动上报错误码统计

避免常见误区

结构体错误不是“越重越好”,关键在职责清晰和易用性。

  • 不要把结构体设计成大而全的“错误中心”,只保留必要字段(码、状态、提示、原始 err)
  • 不要在 Error() 方法里返回 Message —— 它面向开发者,不是终端用户
  • 不要用常量替代错误类型:仅靠 if err == ErrNotFound 难以携带动态上下文(如用户 ID),结构体才能承载
  • 不要忽略错误链:用 fmt.Errorf("xxx: %w", err) 包装时,确保 AppError 支持 Unwrap() 方法以便调试