Spring Boot REST接口统一响应重构的核心是通过ResponseBodyAdvice自动包装成功响应、RestControllerAdvice统一处理异常,实现 ApiResponse 与 ErrorResponse 分离、HTTP状态码语义优先、业务代码零侵入。

Spring Boot 中 REST 接口统一响应的重构,核心是把原来散落在每个 Controller 里的 ResponseEntity 手动包装、try-catch 异常处理、重复的 {code, msg, data} 拼装逻辑,收拢成全局、声明式、可复用的机制。不靠改业务代码,而靠 Spring MVC 的扩展点来“自动补全”。
响应体结构先标准化
统一响应类不是越复杂越好,关键是字段语义清晰、前后端对齐、便于序列化:
@Data
public class ApiResponse<t> {
private int code; // HTTP 语义码(200/400/500)或业务码(如 1001)
private String message; // 用户友好的提示,非技术堆栈
private T data; // 成功时携带业务数据,失败时为 null
private long timestamp; // 可选,用于排查时序问题
}</t>
⚠️ 注意:不要让 ApiResponse 同时承载成功和错误场景(比如把 400 错误也塞进 data=null + code=400)。HTTP 状态码才是第一语义——成功走 2xx,错误走 4xx/5xx,响应体结构可以不同。
用 ResponseBodyAdvice 自动包装成功响应
实现 ResponseBodyAdvice,拦截所有控制器返回值,自动套上 ApiResponse:
@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<object> {
@Override
public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType) {
// 排除已明确返回 ApiResponse 的方法(避免二次包装)
return !ApiResponse.class.isAssignableFrom(returnType.getParameterType());
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class extends HttpMessageConverter>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 若 controller 已返回 ApiResponse,直接放行
if (body instanceof ApiResponse) {
return body;
}
// 其他情况:自动包装为 success 响应
return ApiResponse.success(body);
}
}</object>
这样 Controller 就能写得干净:
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // 直接返回 User 对象,无需 new ApiResponse(...)
}
用 RestControllerAdvice 统一处理异常
定义业务异常(如 BusinessException)、参数校验异常(MethodArgumentNotValidException)、系统异常(RuntimeException),再集中拦截并转为标准错误响应:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<errorresponse> handleBusinessException(BusinessException e) {
ErrorResponse error = new ErrorResponse(400, e.getMessage(), Collections.emptyList());
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<errorresponse> handleValidationException(MethodArgumentNotValidException e) {
List<string> errors = e.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
ErrorResponse error = new ErrorResponse(400, "参数校验失败", errors);
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<errorresponse> handleUnexpected(Exception e) {
ErrorResponse error = new ErrorResponse(500, "服务内部错误", Collections.emptyList());
// 记录日志,但不暴露 stacktrace 给前端
log.error("Uncaught exception", e);
return ResponseEntity.status(500).body(error);
}
}</errorresponse></string></errorresponse></errorresponse>
其中 ErrorResponse 是独立的错误响应类,字段精简、不含泛型:
@Data
public class ErrorResponse {
private int status; // HTTP 状态码(400/404/500)
private String message;
private List<string> errors; // 可选,如字段级校验提示
}</string>
状态码设计要分层,别全挤在 200 里
-
2xx:仅表示 HTTP 层面成功(如200 OK,201 Created,204 No Content) -
4xx:客户端问题(400 Bad Request,401 Unauthorized,403 Forbidden,404 Not Found) -
5xx:服务端问题(500 Internal Server Error,503 Service Unavailable)
业务状态(如“余额不足”“订单已取消”)不应靠 code=200 + data.code=1002 来表达,而应配合 400 或自定义 4xx 状态码,并在 message 或 errors 中说明。
配合 Swagger / Apifox 更好落地
在 ApiResponse 和 ErrorResponse 上加 @ApiModel 和 @ApiModelProperty 注解,Swagger 能自动生成准确的响应示例;Apifox 导入 OpenAPI 后,前端可直接生成类型定义(如 TypeScript interface),减少手动适配。
不复杂但容易忽略。










