侧边栏壁纸
博主头像
qingtian博主等级

喜欢是一件细水流长的事,是永不疲惫的双向奔赴~!

  • 累计撰写 104 篇文章
  • 累计创建 48 个标签
  • 累计收到 1 条评论

使用全局返回处理类ResponseBodyAdvice

qingtian
2022-08-12 / 0 评论 / 0 点赞 / 722 阅读 / 2,804 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-08-12,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

@RestControllerAdvice ResponseBodyAdvice

使用 @RestControllerAdviceResponseBodyAdvice<Object> 返回 String 类型的报错

问题描述:在 controller 层 我只想关注返回的业务数据 而不想处理通用的响应格式,所以我用了 @RestControllerAdviceResponseBodyAdvice<Object> 来处理返回参数的包装,但是在返回 String 类型的数据时报错:

class com.imooc.ecommerce.vo.CommonResponse cannot be cast to class java.lang.String (com.imooc.ecommerce.vo.CommonResponse is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')

无法将通用的返回类映射到 String 类型上

controller 返回 advice

/**
 * @author qingtian
 * @description:
 * @Package com.imooc.ecommerce.advice
 * @date 2021/11/30 22:52
 */
@RestControllerAdvice(value = "com.imooc.ecommerce")
public class CommonResponseDataAdvice implements ResponseBodyAdvice<Object> {

    /**
     * 判断响应是否需要被包装
     */
    @Override
    public boolean supports(MethodParameter methodParameter,
                            Class<? extends HttpMessageConverter<?>> aClass) {

        //如果被IgnoreResponseAdvice注解进行标记则不需要对返回的响应体做处理
        if (methodParameter.getDeclaringClass()
                .isAnnotationPresent(IgnoreResponseAdvice.class)) {
            return false;
        }
        if (methodParameter.getMethod()
                .isAnnotationPresent(IgnoreResponseAdvice.class)) {
            return false;
        }
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter,
                                    MediaType mediaType,
                                    Class<? extends HttpMessageConverter<?>> aClass,
                                    ServerHttpRequest serverHttpRequest,
                                    ServerHttpResponse serverHttpResponse) {

        //定义最终返回的对象
        CommonResponse<Object> response = new CommonResponse<>(0,"");

        if (null == o) {
            return response;
        }else if (o instanceof CommonResponse) {
            response = (CommonResponse<Object>) o;
        }else {
            response.setData(o);
        }
        return response;
    }
}

controller 层代码

/**
 * @author Guank
 * @version 1.0
 * @description: 测试缓存 request 中的 body
 * @date 2022-08-11 23:47
 */
@RestController
@RequestMapping("/cache")
@Slf4j
public class RequestCacheController {

    @PostMapping("/requestCache")
    public String requestCache(@RequestBody UsernameAndPassword usernameAndPassword) {
        return usernameAndPassword.getPassword();
    }

问题分析

image-20220812012450821

  1. 断点打到这里 看见 HttpMessageConverter 这个参数类型传入的是 class org.springframework.http.converter.StringHttpMessageConverter

    怀疑是 Converter类型的原因,接着 debug

image-20220812012902522

  1. AbstractMessageConverterMethodProcessor类的

    image-20220812012935019

    这个方法中看到此时的 body 是 CommonResponse类型,valueTypeString类型的。接着往下看

    第229行代码,此时converterStringHttpMessageconverter,但是我的 body 是 CommonResponse类型,这里发生了转换失败

  2. 查看为什么converterStringHttpMessageconverter

    image-20220812013604568

    看到第188行代码,converter初始化,在第196行对其进行赋值,推测可能根据这个方法开头的

    image-20220812013745195

    valuevalueType有关,它发现valueString类型的,然后将converter设置成了 StringHttpMessageconverter 但是我们在对返回参数进行包装的时候已经将其包装成了 CommonResponse,所以出现转换失败。

解决方法

在所有的 HttpMessageConverter 实例集合中,StringHttpMessageConverter 要比其它的 Converter 排得靠前一些。我们需要将处理 Object 类型的 HttpMessageConverter 放得靠前一些。可以在configuration中完成。

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0,new MappingJackson2HttpMessageConverter());
    }
}
0

评论区