目录
实现统一数据格式
测试
原因分析
解决方案
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 类型的数据 } }