全局异常处理器让接口错误只说一种话

zjc 于 2026-08-11 发布

接口写多了以后,最怕的不是异常本身,而是异常的形状不一致:有的返回 Tomcat 默认错误页,有的把完整堆栈给前端,有的只在日志里留一句模糊提示。这个项目把 WebMVC 服务的异常处理收敛到 service-commonGlobalExceptionHandler,业务模块引入公共包后自动生效。

一、处理边界

处理器使用 @RestControllerAdvice,优先级可以理解为:

BusinessException
参数校验异常
请求格式 / 路径 / 方法异常
未预期异常兜底

这个顺序很重要。业务异常最了解自己,应该原样透传 codemessage;框架异常则应该翻译成统一的 ApiResponse,避免前端理解 Spring 内部异常。

二、把校验错误拼成可读信息

@RequestBody 校验失败、表单绑定失败、@RequestParam 约束失败,最终都被转换成类似下面的提示:

username: 用户名不能为空; age: 年龄必须在 1 到 120 之间

核心做法是从 FieldErrorConstraintViolation 中取出字段和默认消息,再用分号拼接。这样前端可以直接展示,不需要解析异常类名。

三、框架异常的分类

常见异常都有明确归属:

其中 favicon.ico 的 404 会被静默处理。浏览器会自动请求它,如果每次都打 warn,日志很快就会被无价值信息填满。

四、兜底异常不要泄露堆栈

未预期异常统一记录完整堆栈,但响应里只返回稳定结构和提示:

@ExceptionHandler(Exception.class)
public ApiResponse<Void> handleException(Exception e) {
    log.error("未预期异常", e);
    String message = e.getMessage() != null
            ? e.getMessage()
            : ApiResponseEnum.INTERNAL_ERROR.message();
    return ApiResponse.failure(ApiResponseEnum.INTERNAL_ERROR.code(), message);
}

https://github.com/springvortex/spring-cloud-alibaba/blob/release/v1.0.0/service-common/src/main/java/com/zjc/common/exception/GlobalExceptionHandler.java

完整堆栈给开发者,稳定 JSON 给调用方,这是全局异常处理的基本分工。

五、经验总结

全局异常处理适合放在公共模块,但不代表业务代码可以到处 try-catch。业务失败优先抛 BusinessException ;只有需要转换错误、补充上下文或清理资源时,才在局部捕获。