不能直接用 exception.getmessage() 做前端提示,因其内容随环境、版本、日志级别变化且不可控;错误码必须稳定、可枚举、无歧义,如"user_not_found",与message解耦,通过errorcode枚举统一管理,并由baseexception强制绑定,跨服务需集中定义并语义映射。

为什么不能直接用 Exception.getMessage() 做前端提示
因为消息内容会随环境、版本、甚至日志级别变化,比如 NullPointerException 在不同 JDK 版本里报的文本可能带堆栈片段或不带;本地调试时你写了“用户不存在”,上线后被中间件吞掉再抛出,变成“Remote call failed”。前端拿到的就只是个不可控字符串,没法做国际化、重试判断、埋点统计。
- 错误码必须是稳定、可枚举、无语义歧义的字符串或数字,比如
"USER_NOT_FOUND"或4001 - 错误信息(message)只用于开发排查,应和错误码解耦,写在配置文件或枚举类里,不硬编码在
throw处 - HTTP 接口返回时,必须把
errorCode字段单独透出,不能塞进message里解析
如何定义一个可维护的 ErrorCode 枚举类
别用常量接口(interface ErrorCode)或散落的 public static final String,它们无法约束使用、没法查漏、IDE 不提示。枚举是唯一能兼顾类型安全、可遍历、可扩展的方案。
- 每个枚举项至少包含三个字段:
code(唯一整型或字符串)、message(默认提示)、httpStatus(如HttpStatus.NOT_FOUND) - 避免用纯数字码,比如
1001,容易冲突且无业务含义;推荐前缀 + 数字,如"AUTH_TOKEN_EXPIRED"或"ORDER_2003" - 枚举构造器里不要调用外部服务或读配置文件——类加载阶段失败会导致整个应用起不来
- 示例:
public enum ErrorCode { USER_NOT_FOUND(4001, "用户不存在", HttpStatus.NOT_FOUND), ORDER_PAID(4002, "订单已支付,不可重复操作", HttpStatus.CONFLICT); private final int code; private final String message; private final HttpStatus httpStatus; // 构造 + getter 略 }
怎么让异常抛出时自动绑定 ErrorCode
手动 new 异常再 setCode 是反模式:易漏、难统一、破坏封装。应该让异常类型本身携带错误码能力。
- 定义统一异常基类
BaseException,构造函数强制传入ErrorCode枚举 - 所有业务异常继承它,禁止直接 throw
RuntimeException或IllegalArgumentException - 全局异常处理器(如 Spring 的
@ControllerAdvice)只处理BaseException及其子类,其他未捕获异常走 500 逻辑 - 注意:不要在
BaseException里重写getMessage()返回errorCode.message—— 这会让日志丢失原始上下文,建议保留 cause 和 stackTrace,仅对外响应时取errorCode.message
错误码分域管理与跨服务传递的坑
一个微服务系统里,USER_ 前缀的码可能在用户中心定义,但订单服务调用失败时也得返回它。如果各自维护一份枚举,很快就会出现同码异义、同义异码、缺失翻译等问题。
立即学习“Java免费学习笔记(深入)”;
- 错误码定义必须集中在一个独立 module(如
common-errorcode),所有服务依赖它,禁止复制粘贴 - 跨服务 RPC 调用时,下游返回的
ErrorCode不能原样透传给前端——要映射成当前服务语义一致的码,比如订单服务调用户中心失败,不应返回USER_LOCKED,而应转为ORDER_USER_STATUS_INVALID - HTTP 网关层要做错误码清洗:过滤敏感信息(如数据库错误码
MYSQL_1062)、统一降级码(如超时统一转SYSTEM_TIMEOUT)、补全缺失的httpStatus
最麻烦的不是定义规则,而是让所有人每次抛异常前都打开那个枚举类找码——所以 IDE 模板、CI 卡点(如扫描 new RuntimeException)、以及 PR 时自动检查新增异常是否继承 BaseException,比文档管用得多。










