目录
实现统一数据格式
测试
原因分析
解决方案
- 🎥 个人主页: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 {
@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的时候会出错呢?就不得不去看源码分析了.
这些转换器是有先后顺序的,是用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 类型的数据
}
}