目录
实现统一数据格式
测试
原因分析
解决方案
- 🎥 个人主页:Dikz12
- 📕格言:吾愚多不敏,而愿加学
- 欢迎大家👍点赞✍评论⭐收藏
实现统一数据格式
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 {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic 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的时候会出错呢?就不得不去看源码分析了.
这些转换器是有先后顺序的,是用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 {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType, Class selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {//body 是返回的结果//当返回结果为Result类型时,就直接返回bodyif (body instanceof Result) {return body;}//返回结果为String类型, 使⽤SpringBoot内置提供的Jackson来实现信息的序列化if (body instanceof String) {return objectMapper.writeValueAsString(Result.success(body));}return Result.success(body); //返回 Result 类型的数据}
}