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
和实现ResponseBodyAdvice
做controller
层统一返回封装时。当返回字符串时会报 “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系列优雅的处理统一异常处理与统一结果返回