Spring Boot RESTful 接口统一返回与异常处理实践
在 RESTful 接口开发中,如果没有统一的返回结构和异常处理机制,往往会出现以下问题:
-
不同接口返回格式不一致
-
前端需要写大量
if-else判断 -
异常信息零散,难以维护
-
系统错误直接暴露给用户
因此,在 Spring Boot RESTful 项目中,统一返回结构 + 全局异常处理 是一项非常重要的工程实践。
一、为什么需要统一返回结构?
假设没有统一返回,接口可能长这样:
// 接口1
{
"id": 1,
"name": "Tom"
}
// 接口2
{
"success": true,
"data": {}
}
// 接口3
{
"error": "参数错误"
}
❌ 问题:
-
前端解析困难
-
错误处理不统一
-
接口不可预测
✅ 推荐的统一返回结构
{
"code": 200,
"message": "success",
"data": {}
}
| 字段 | 含义 |
|---|---|
| code | 业务状态码 |
| message | 提示信息 |
| data | 实际返回数据 |
二、定义统一返回对象(ApiResponse)
1️⃣ 创建通用返回类
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public ApiResponse() {}
public ApiResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}
public static <T> ApiResponse<T> success() {
return new ApiResponse<>(200, "success", null);
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
// getter & setter
}
2️⃣ Controller 中的使用方式
@GetMapping("/users/{id}")
public ApiResponse<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ApiResponse.success(user);
}
👉 Controller 只关注业务逻辑,不关心异常细节
三、业务状态码设计(推荐)
不要直接把 HTTP 状态码当业务码使用,建议单独定义业务码。
public interface ResultCode {
int SUCCESS = 200;
int PARAM_ERROR = 400;
int NOT_FOUND = 404;
int SERVER_ERROR = 500;
}
四、为什么要做全局异常处理?
如果不处理异常:
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // 可能抛异常
}
❌ 出现异常时:
-
返回 500
-
堆栈信息暴露
-
前端无法友好提示
五、Spring Boot 全局异常处理机制
Spring Boot 提供了 @ControllerAdvice + @ExceptionHandler 用于统一异常处理。
六、实现全局异常处理类
1️⃣ 创建自定义业务异常
public class BusinessException extends RuntimeException {
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
}
2️⃣ 全局异常处理类
@RestControllerAdvice
public class GlobalExceptionHandler {
// 业务异常
@ExceptionHandler(BusinessException.class)
public ApiResponse<Void> handleBusinessException(BusinessException e) {
return ApiResponse.error(e.getCode(), e.getMessage());
}
// 参数异常
@ExceptionHandler(IllegalArgumentException.class)
public ApiResponse<Void> handleIllegalArgumentException(IllegalArgumentException e) {
return ApiResponse.error(400, e.getMessage());
}
// 系统异常兜底
@ExceptionHandler(Exception.class)
public ApiResponse<Void> handleException(Exception e) {
return ApiResponse.error(500, "服务器内部错误");
}
}
七、Controller 中优雅地抛异常
@GetMapping("/users/{id}")
public ApiResponse<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
throw new BusinessException(404, "用户不存在");
}
return ApiResponse.success(user);
}
👉 Controller 无需 try-catch,逻辑更清晰。
八、参数校验异常统一处理(进阶)
结合 @Valid:
@PostMapping("/users")
public ApiResponse<Void> createUser(@Valid @RequestBody User user) {
return ApiResponse.success();
}
异常处理:
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<Void> handleValidationException(MethodArgumentNotValidException e) {
String msg = e.getBindingResult()
.getFieldError()
.getDefaultMessage();
return ApiResponse.error(400, msg);
}
九、HTTP 状态码 vs 业务状态码
推荐实践:
-
HTTP 状态码:表示请求是否成功(200 / 4xx / 5xx)
-
业务状态码:表示业务含义(用户不存在 / 权限不足)
通常:
HTTP/1.1 200 OK
{
"code": 40401,
"message": "用户不存在",
"data": null
}
十、统一返回与异常处理的好处
✅ 前后端协作成本低
✅ 接口风格统一、可维护性高
✅ 便于日志与埋点统计
✅ 适用于微服务与网关架构
十一、总结
在 Spring Boot RESTful 项目中:
-
统一返回结构 是接口规范的基础
-
全局异常处理 是系统健壮性的保障
-
Controller 应该 只处理业务逻辑
-
异常交给全局处理,返回交给统一模型
这是成熟后端项目的必备能力。