Spring Boot中实现各API统一响应返回的最佳方式是什么?
API 统一响应格式实现方案解答
方案优先级结论
优先推荐使用ResponseBodyAdvice实现全局统一响应,该方案是目前Spring生态下的最优解,远优于每个Controller手动返回SuccessDTO/ErrorDTO的方式
两种方案对比
- 手动返回DTO方案
- 优势:无需额外全局配置,逻辑直观,仅适合接口量少于20个的微型项目
- 劣势:代码冗余度极高,每个接口都需要重复写包装逻辑,后续调整响应结构需要全量修改所有Controller接口,维护成本极高,极易出现格式不一致的问题
ResponseBodyAdvice方案- 优势:完全无侵入,Controller层只需返回业务数据本身,不需要关心响应包装逻辑,后续调整统一响应格式仅需修改全局处理器一处代码,维护成本极低,能100%保证所有接口响应格式一致
- 劣势:需要额外处理不需要统一包装的特殊接口(如文件下载、第三方回调接口等),可通过自定义注解做排除,实现成本极低
ResponseBodyAdvice完整实现示例
1. 定义统一响应实体类
import lombok.Data; @Data public class CommonResult<T> { private Integer statusCode; private String message; private T results; // 成功响应构造方法 public static <T> CommonResult<T> success(T data, String message) { CommonResult<T> result = new CommonResult<>(); result.setStatusCode(200); result.setMessage(message); result.setResults(data); return result; } // 失败响应构造方法 public static <T> CommonResult<T> fail(Integer errorCode, String errorMessage) { CommonResult<T> result = new CommonResult<>(); result.setStatusCode(errorCode); result.setMessage(errorMessage); result.setResults((T) new Object[0]); // 失败时返回空数组符合要求 return result; } }
2. 定义可选的排除包装注解
用于不需要统一响应格式的特殊接口,加在对应Controller方法上即可跳过包装
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface IgnoreResponseWrap { }
3. 实现全局响应处理器
import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; // 指定要扫描的Controller包路径,避免拦截到第三方框架接口 @ControllerAdvice(basePackages = "com.yourproject.business.controller") public class GlobalResponseHandler implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 方法加了IgnoreResponseWrap注解就不做包装 return !returnType.hasMethodAnnotation(IgnoreResponseWrap.class); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 如果返回值已经是CommonResult类型(比如异常处理器返回的结果),直接返回避免重复包装 if (body instanceof CommonResult) { return body; } // 正常业务返回统一包装为成功响应 return CommonResult.success(body, "操作成功"); } }
4. 配合全局异常处理器实现失败响应
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { // 处理自定义业务异常 @ExceptionHandler(BusinessException.class) public CommonResult<?> handleBusinessException(BusinessException e) { return CommonResult.fail(e.getCode(), e.getMessage()); } // 处理系统级异常 @ExceptionHandler(Exception.class) public CommonResult<?> handleSystemException(Exception e) { return CommonResult.fail(500, "系统内部错误,请联系管理员"); } }
常见问题处理
如果项目中存在返回String类型的接口,会因为Spring默认的StringHttpMessageConverter类型转换逻辑报错,解决方案有两种:
- 所有返回String的接口提前手动包装为
CommonResult类型 - 调整Spring MVC的消息转换器顺序,把
MappingJackson2HttpMessageConverter放在StringHttpMessageConverter前面
内容的提问来源于stack exchange,提问作者Nishant
相关产品推荐
相关产品推荐

