跳到主要内容
版本:Next

统一返回与全局异常处理

生产级接口需要统一的响应格式和集中的异常处理,否则前端要面对千奇百怪的错误结构。Spring Boot 用 @RestControllerAdvice 优雅解决。

统一返回体​

public class Result<T> {

private int code; // 业务状态码:0 成功
private String message; // 提示信息
private T data; // 数据

public static <T> Result<T> ok(T data) {
Result<T> r = new Result<>();
r.code = 0;
r.message = "success";
r.data = data;
return r;
}

public static <T> Result<T> fail(int code, String message) {
Result<T> r = new Result<>();
r.code = code;
r.message = message;
return r;
}
// getter / setter
}

所有接口统一返回:

{
"code": 0,
"message": "success",
"data": { "id": 1, "username": "tom" }
}

自定义业务异常​

public class BizException extends RuntimeException {

private final int code;

public BizException(int code, String message) {
super(message);
this.code = code;
}

public int getCode() {
return code;
}
}

业务代码中直接抛出,不用每层 try-catch:

public User getById(Long id) {
User user = userMapper.selectById(id);
if (user == null) {
throw new BizException(1001, "用户不存在");
}
return user;
}

全局异常处理​

@RestControllerAdvice
public class GlobalExceptionHandler {

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

// 参数校验异常(@Valid 触发)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValid(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.findFirst()
.orElse("参数错误");
return Result.fail(400, msg);
}

// 兜底:所有未处理的异常
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.fail(500, "系统繁忙,请稍后重试");
}
}

工作原理​

Controller 抛出异常
→ DispatcherServlet 捕获
→ 查找 @RestControllerAdvice 中匹配的 @ExceptionHandler
→ 执行处理方法,返回统一 Result(JSON)
  • @RestControllerAdvice = @ControllerAdvice + @ResponseBody,全局拦截 Controller 层异常。
  • 匹配规则:优先匹配最具体的异常类型,找不到再向上匹配父类。

常用 HTTP 状态码约定​

场景code 建议
成功0
参数校验失败400
未登录 / token 失效401
无权限403
资源不存在404(或业务码 1xxx)
系统异常500
提示

HTTP 状态码与业务 code 可以分离:HTTP 层统一返回 200,业务成败由 body 中的 code 表达,便于前端统一拦截处理。