SpringBoot统一返回处理出现cannot be cast to java.lang.String异常

Posted 伏加特遇上西柚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot统一返回处理出现cannot be cast to java.lang.String异常相关的知识,希望对你有一定的参考价值。

SpringBoot统一返回处理出现cannot be cast to java.lang.String异常

一 问题出现背景:

在使用@RestControllerAdvice和实现ResponseBodyAdvicecontroller层统一返回封装时。当返回字符串时会报 “cannot be cast to java.lang.String” 异常,返回其他类型就无任何问题。

二 解决方案

如果返回的是字符串直接手动封装返回对象转成json字符串返回即可。

完整代码

@RestControllerAdvice
public class ResponseResult implements ResponseBodyAdvice<Object> 
    /**
     * 支持注解@ResponseNotIntercept,使某些方法无需使用Result封装
     *
     * @param returnType 返回类型
     * @param converterType  选择的转换器类型
     * @return true 时会执行beforeBodyWrite方法,false时直接返回给前端
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) 
        if (returnType.getDeclaringClass().isAnnotationPresent(ResponseNotIntercept.class)) 
            //若在类中加了@ResponseNotIntercept 则该类中的方法不用做统一的拦截
            return false;
        
        if (returnType.getMethod().isAnnotationPresent(ResponseNotIntercept.class)) 
            //若方法上加了@ResponseNotIntercept 则该方法不用做统一的拦截
            return false;
        
        return true;
    

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) 
        if (body instanceof Result) 
            // 提供一定的灵活度,如果body已经被包装了,就不进行包装
            return body;
        
        if (body instanceof String) 
            //解决返回值为字符串时,不能正常包装
            return JSON.toJSONString(Result.success(body));
        
        return Result.success(body);
    

三 异常原因分析

原因:

SpringMVC 默认会注册一些自带的HttpMessageConvertor (从先后顺序排列分别为ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter,SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter) ,后端服务使用Restful API的形式,前后端得规范一般是json格式,SpringMVC 自带MappingJackson2HttpMessageConverter,在依赖中引入 jackson 包后,容器会把MappingJackson2HttpMessageConverter自动注册到 messageConverters链的末尾

当返回的数据是非字符串时使用的 MappingJackson2HttpMessageConverter 写入返回对象。
当返回的数据是字符串时,此处得方法是要去循环遍历HttpMessageConverter集,因为StringHttpMessageConverter会先被遍历到,这时会认为StringHttpMessageConverter可以使用,在返回Result是使用((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);此方法是父类方法body参数类型为Object,实际调用的为StringHttpMessageConverter中的addDefaultHeaders(HttpHeaders headers, String s, @Nullable MediaType type)方法,使用String类型的s来接收Result类型的body,类型不匹配则出现Result cannot be cast to java.lang.String异常。

源码详细分析:

正常返回:

  • 步骤一:遍历messageConverters去判断到MappingJackson2HttpMessageConverter
    GenericHttpMessageConverter类型的converter
  • 步骤二:进一步判断到MappingJackson2HttpMessageConverter可以写入对象类型的数据。
  • 步骤三:调用beforeBodyWriter方法将原有的TestVO对象数据封装到Result对象中。
  • 步骤四:调用MappingJackson2HttpMessageConverter中的wirte方法(代码中用接口类型接收的)
  • 步骤五:通过MappingJackson2HttpMessageConverter继承关系发现其write方法在父类AbstractHttpMessageConverter中,在write方法中调用本类中addDefaultHeaders方法向输出消息添加默认报头。(此处应注意)
  • 步骤六:将封装好的Result对象返回给前端

返回为字符串异常

  • 步骤一:遍历messageConverters去判断到StringHttpMessageConverter是null;
  • 步骤二:进一步判断到StringHttpMessageConverter可以写入String类型的数据。
  • 步骤三:调用beforeBodyWriter方法将原有的String类型数据封装到Result对象中。
  • 步骤四:调用StringHttpMessageConverter中的wirte方法(代码中用接口类型接收的)
  • 步骤五
  • 调用父类AbstractHttpMessageConverter中的write方法,由于StringHttpMessageConverter重写了addDefaultHeaders方法,故write中调用子类中的addDefaultHeaders。由于父类中参数t为对象类型,对应子类中接收的s为String类型故会出现类型转换异常Result cannot be cast to java.lang.String(此处应注意)

springboot定义统一的返回异常提示数据格式

一 描述

1.1 没有加全局异常处理

1.这里设置一个字符串为空指针异常,然后看看返回给前端的信息。

 2.返回结果

 3.效果看起来不友好的提示

1.2 添加全局异常处理

1.代码:添加一个全局异常处理类

@ControllerAdvice
public class GlobalExceptionHandler 

    //指定出现什么异常执行这个方法
    @ExceptionHandler(Exception.class)
    @ResponseBody //为了返回数据
    public R error(Exception e) 
        e.printStackTrace();
        return R.error().message("亲,发生错了!!!..");
    

 3.结果

 

以上是关于SpringBoot统一返回处理出现cannot be cast to java.lang.String异常的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot全局异常统一处理反参标准化

SpringBoot全局异常统一处理反参标准化

SpringBoot系列优雅的处理统一异常处理与统一结果返回

SpringBoot2.0统一返回Rest风格数据结构与统一异常处理

springboot处理统一返回json数据格式

SpringBoot 统一功能处理