Spring Boot 统一数据返回格式的解决方案
作者:Dikz12
实现统一数据格式
统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现;
@ControllerAdvice : 表⽰控制器通知类.
比如:添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加
@ControllerAdvice 注解.
import com.example.demo.model.Result; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; 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; @ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { return Result.success(body); //返回 Result 类型的数据 } }
supports⽅法: 判断是否要执⾏beforeBodyWrite⽅法. true为执⾏, false不执⾏. 通过该⽅法可以 选择哪些类或哪些⽅法的response要进⾏处理, 其他的不进⾏处理.
beforeBodyWrite⽅法:对response⽅法进⾏具体操作处理.
测试
写一些不同的返回结果,看看哪些会出现问题!
import com.example.demo.model.BookInfo; import com.example.demo.model.Result; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class TestController { @RequestMapping("/t1") public Integer t1() { return 12334; } @RequestMapping("/t2") public String t2() { return "hello"; } @RequestMapping("/t3") public Boolean t3() { return true; } @RequestMapping("/t4") public BookInfo t4() { return new BookInfo(); } @RequestMapping("/t5") public Result t5() { return Result.success("success"); } }
1.返回结果为Integer,可以正确响应.
2.返回结果为String,结果显示内部错误.
控制台查看日志:
3.返回结果为Boolean,可以正常响应.
4.返回结果为BookInfo对象,可以正常响应.
5.返回结果为Result,发现又进行了一次封装.
问题:1.返回结果为String,不能正常进行处理了;2.返回结果为Result,又多进行了一次封装.
原因分析
这时候就会非常纳闷了,为什么就处理String的时候会出错呢?就不得不去看源码分析了.
SpringMVC (也就是在初始化时) 默认会注册⼀些⾃带的 HttpMessageConverter (转换器) (从先后顺序排列分别为 ByteArrayHttpMessageConverter , StringHttpMessageConverter , SourceHttpMessageConverter , SourceHttpMessageConverter , AllEncompassingFormHttpMessageConverter )
这些转换器是有先后顺序的,是用ArrayList存储的.会根据我们的返回结果挨个判断使用哪个转换器!!!
此时,就会发现问题就出现在 StringHttpMessageConverter.
处理的内容主要是在AbstractMessageConverterMethodProcessor 中 会有一个writeWithMessageConverters()方法.
通过getAdvice()拿到了beforBodyWrite 就会对body进行处理,处理完之后并没有结束(body变成了Result类型),此时,也并没有出错。会继续执行((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage).
由于 StringHttpMessageConverter 重写了addDefaultHeaders⽅法, 所以会执⾏⼦类的⽅法
然⽽⼦类 StringHttpMessageConverter 的addDefaultHeaders⽅法定义接收参数为String, 此
时t为Result类型, 所以出现类型不匹配" Result cannot be cast to java.lang.String "的异常.
解决方案
1.当返回结果为Result时,就直接返回body.
2.当返回结果为String时,采用SpringBoot内置提供的Jackson来实现信息的序列化
@ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { @Autowired private ObjectMapper objectMapper; @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @SneakyThrows @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { //body 是返回的结果 //当返回结果为Result类型时,就直接返回body if (body instanceof Result) { return body; } //返回结果为String类型, 使⽤SpringBoot内置提供的Jackson来实现信息的序列化 if (body instanceof String) { return objectMapper.writeValueAsString(Result.success(body)); } return Result.success(body); //返回 Result 类型的数据 } }
到此这篇关于Spring Boot 统一数据返回格式 分析 和 处理的文章就介绍到这了,更多相关Spring Boot 统一数据返回格式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!