一 前言
目前前后端分离的项目中,我们在controller层会统一格式封装结果给前端。如果我们在每个方法中手动封装Result,无疑是增加了额外的工作量。  那么有没有一种方式我们只返回它相应的数据。对于结果可以自动帮我们封装,且还可以对某个方法或者某个类下所有方法封装或者不封装。 答案是肯定的。利用@RestControllerAdvice和 ResponseBodyAdvice将Result返回对象统一拦截处理。
二 @RestControllerAdvice注解和 ResponseBodyAdvice接口说明
@RestControllerAdvice:是一个组合注解,包含@ControllerAdvice 和@ResponseBody 。
ResponseBodyAdvice :允许在@ResponseBody或ResponseEntity控制器方法执行之后,但在使用HttpMessageConverter编写body之前定制响应。
简单理解:ResponseBodyAdvice接口是在controller层方法执行之后,在response返回给前端数据之前对reponse的数据进行处理,可以对数据进行统一的处理,从而可以使返回数据格式一致。
三 具体实现
3.1 统一返回数据格式代码
3.1.1 ResponseResult
@RestControllerAdvice
public class ResponseResult implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.getDeclaringClass().isAnnotationPresent(ResponseNotIntercept.class)) {
return false;
}
if (returnType.getMethod().isAnnotationPresent(ResponseNotIntercept.class)) {
return false;
}
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Result) {
return body;
}
if (body instanceof String) {
return JSON.toJSONString(Result.success(body));
}
return Result.success(body);
}
}
说明: 实现ResponseBodyAdvice接口 需要重写supports,beforeBodyWrite方法; supports: 是否支持给定的控制器方法返回类型和选定的HttpMessageConverter类型;若不支持则就不会对数据进行做统一处理,就像上面代码,若加了@ResponseNotIntercept注解,则不会进行拦截(@ResponseNotIntercept是自己自定义的一个注解)
- 参数:
returnType :返回类型; converterType :选择的转换器类型 - 返回:若返回结果为true,则调用beforeBodyWrite
beforeBodyWrite: 在选择HttpMessageConverter之后以及在调用其write方法之前调用。
- 参数:
body :你传入的数据; returnType :controller层方法返回的类型; selectedContentType :通过内容协商选择的内容类型; selectedConverterType :选择要写入响应的转换器类型; request/reponse :当前请求和响应; - 返回:传入的数据或修改的(可能是新的)实例。
3.1.2 ResponseNotIntercept
自定义不被拦截注解,灵活控制对某个方法不封装Result
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseNotIntercept {
String value() default "";
}
3.2 统一返回对象Result
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer status;
private String message;
private T data;
public static <T> Result<T> success(T data) {
return new Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), data);
}
public static <T> Result<T> success(String message, T data) {
return new Result<>(ResultEnum.SUCCESS.getCode(), message, data);
}
public static Result<?> failed() {
return new Result<>(ResultEnum.COMMON_FAILED.getCode(), ResultEnum.COMMON_FAILED.getMessage(), null);
}
public static Result<?> failed(String message) {
return new Result<>(ResultEnum.COMMON_FAILED.getCode(), message, null);
}
public static Result<?> failed(IResult errorResult) {
return new Result<>(errorResult.getCode(), errorResult.getMessage(), null);
}
public static Result<?> failed(Integer code, String message) {
return new Result<>(code, message, null);
}
public static <T> Result<T> instance(Integer code, String message, T data) {
Result<T> result = new Result<>();
result.setStatus(code);
result.setMessage(message);
result.setData(data);
return result;
}
}
3.3 controller层方法测试
@RestController
@RequestMapping("/CommentRest")
public class CommentRestController {
@GetMapping("getId")
public Integer getId() {
return 1;
}
@GetMapping("getOne")
public TestVO getOne() {
TestVO testVO = new TestVO("1","测试标题","无内容","小明");
return testVO;
}
@DeleteMapping("delete")
public String delete() {
return "删除成功";
}
@PutMapping("save")
@ResponseNotIntercept
public void save() {
System.out.println("无返回值 = ");
}
}
3.3.1 getId() 数值返回值测试

3.3.2 getOne()对象返回值测试结果

3.3.2 save()无返回值测试结果

3.3.2 delete()字符串返回值测试结果(注意此处为重点)
上述测试都很顺利,当返回的值是字符串时发现报错了,并不是预期的字符串。  控制台报错信息
2022-11-01 19:44:26.962 ERROR 36416 --- [nio-9098-exec-2] c.w.advice.ControllerExceptionHandler : com.wxl52d41.result.Result cannot be cast to java.lang.String
java.lang.ClassCastException: com.wxl52d41.result.Result cannot be cast to java.lang.String
at org.springframework.http.converter.StringHttpMessageConverter.addDefaultHeaders(StringHttpMessageConverter.java:44) ~[spring-web-5.3.22.jar:5.3.22]
通过错误日志分析说,Result 这个类不能够转换成String。我明明返回的是字符串怎么就是报这个错了呢,其他怎么就没有问题。一顿百度,最终debug发现了端倪。 SpringBoot统一返回处理出现cannot be cast to java.lang.String异常 解决方案添加如下代码  再次测试,成功返回
|