Spring Boot 统一异常处理与统一返回封装

为什么需要统一异常处理

没有统一异常处理时,Controller 里到处是 try-catch,前端拿到的错误格式五花八门:有的返回字符串,有的返回 500 裸异常,有的把堆栈直接抛给用户。Spring Boot 用 @RestControllerAdvice + @ExceptionHandler 可以把异常在一处收口,再配合统一返回体,前后端对接会清爽很多。

一、先定义统一返回结构

public class Result<T> {
    private int code;
    private String msg;
    private T data;

    public static <T> Result<T> ok(T data) {
        Result<T> r = new Result<>();
        r.code = 0; r.msg = "success"; r.data = data;
        return r;
    }
    public static Result<?> fail(int code, String msg) {
        Result<?> r = new Result<>();
        r.code = code; r.msg = msg;
        return r;
    }
    // getter/setter 省略
}

二、全局异常处理器

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBiz(BusinessException e) {
        return Result.fail(e.getCode(), e.getMessage());
    }

    // 参数校验失败
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleValid(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getFieldError().getDefaultMessage();
        return Result.fail(400, msg);
    }

    // 兜底:未知异常
    @ExceptionHandler(Exception.class)
    public Result<?> handleOther(Exception e) {
        // 生产环境不要暴露堆栈
        return Result.fail(500, "系统异常,请稍后重试");
    }
}

三、业务异常与校验

自定义异常携带业务码:

public class BusinessException extends RuntimeException {
    private int code;
    public BusinessException(int code, String msg) {
        super(msg); this.code = code;
    }
    // getter
}

Controller 里直接抛,不用自己包:

@PostMapping("/order")
public Result<Order> create(@Valid @RequestBody CreateOrderReq req) {
    if (req.getCount() <= 0)
        throw new BusinessException(1001, "购买数量必须大于0");
    return Result.ok(orderService.create(req));
}

@Valid 触发 Bean Validation,校验失败自动进 MethodArgumentNotValidException,返回字段级的友好提示。

四、几个注意点

  • 生产环境别把异常堆栈返回前端,统一返回”系统异常”即可,细节记日志。
  • 全局处理器建议放在独立的 package,确保被 Spring 扫描到。
  • 如果用了 Filter/Interceptor 里抛的异常,@RestControllerAdvice 拦不到,需要在 Filter 层单独处理。

小结

统一异常处理的核心就一句话:异常在一处收口,返回格式只有一种。配合 Result<T> 和分层异常,前端再也不用写一堆 if 判断错误结构,联调效率直接翻倍。

上一篇 NIFI 配置密钥
下一篇 SQL Server 执行计划解读与索引优化实战
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]