HTTP错误码转换应基于统一BizError结构体的Code字段用switch映射,集中管理在HTTPStatusFromBizCode函数中,按语义严格对应4xx/5xx状态码。

用 switch 映射做 HTTP 错误码转换,关键不是“写一堆 case”,而是把 switch 放在合适的位置、匹配合适的对象、配合分层错误结构——否则容易变成维护噩梦。
错误类型必须先统一为可识别的结构体
不能拿原始 error 或字符串去 switch。得让所有业务错误都实现同一个接口或继承同一基类,比如 BizError,它包含 Code、Message、HTTPCode 字段。
- DAO 层抛出数据库错误时,不直接 return errors.New("db timeout"),而是包装成 &code.DBTimeoutError{}
- 服务层校验失败,返回 &code.InvalidParamError{Field: "email"},而非 fmt.Errorf("invalid email")
- 所有这类结构体都实现了 error 接口,并支持 errors.Is / errors.As 断言
switch 应作用于错误码值,而非错误实例本身
在 handler 统一错误处理入口,用 errors.As 提取 BizError 后,对它的 Code 字段做 switch,而不是对整个 error 对象做 switch —— 这样既避免反射开销,又保持语义清晰。
- 例如:switch err.Code { case code.ErrUserNotFound: return http.StatusNotFound case code.ErrInvalidParam: return http.StatusBadRequest ... }
- 不推荐:switch err.(type) { case *DBTimeoutError: ... case *InvalidParamError: ... } —— 类型爆炸,新增错误就要改 switch
- Code 建议用 const 定义(如 const ErrUserNotFound = 1001),便于查表和文档对齐
映射逻辑要集中管理,禁止散落在各 handler 中
把 switch 块抽成一个独立函数,比如 HTTPStatusFromBizCode(code int) int,内部用 map 或 switch 都可以,但必须全局唯一入口。
- map 方式适合快速查表:var statusMap = map[int]int{1001: 404, 1002: 400, 2045: 422}
- switch 方式更适合带逻辑分支的场景:比如某些错误码在调试环境返回 500,在生产环境返回 400
- 未命中时默认返回 500,但必须打日志告警:“unknown biz code %d —— missing HTTP status mapping”
注意 HTTP 状态码语义边界,别越界映射
4xx 表示客户端责任,5xx 表示服务端故障。不能因为“都是错”,就把数据库超时(应是 503 或降级为 408)、权限不足(403)、参数错误(400)全映射成 400。
- 400:仅用于语法/格式问题(JSON 解析失败、字段类型不符)
- 403:明确鉴权通过但权限不足(如普通用户删管理员内容)
- 422:语义校验失败(如余额不足、库存为负、时间冲突)
- 503:依赖服务不可用且无法降级(如支付网关完全宕机)











