统一返回与全局异常处理
生产级接口需要统一的响应格式和集中的异常处理,否则前端要面对千奇百怪的错误结构。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 表达,便于前端统一拦截处理。