使用ResponseBodyAdvice返回值为String出现cannot be cast to java.lang.String异常
背景
由于项目中为了全局返回统一的JSON格式,使用ResponseBodyAdvice进行拦截,拦截的时候会将返回的信息统一一个对象返回到前端。但是有的同事将一个String的响应对象返回,结果报错
com.example.demoweb.config.ApiResponse cannot be cast to java.lang.String
ResponseBodyAdvice 拦截器实现
ServletResponseBodyAdvice
package com.example.demoweb.config;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* @author tengwang8
*/
@ControllerAdvice
@Slf4j
public class ServletResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Autowired
private ApplicationContext applicationContext;
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ApiResponse businessException(BusinessException e) {
log.error(" business exception code = {} ,msg = {}", e.getCode(), e.getMsg());
return ApiResponse.failed(e.getCode(), e.getMsg());
}
/**
* 参数绑定异常
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = BindException.class)
@ResponseBody
public ApiResponse handleBindException(BindException e) {
log.error("params bind exception: ", e);
return wrapperBindingResult(e.getBindingResult());
}
/**
* 参数校验异常,将校验失败的所有异常组合成一条错误信息
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public ApiResponse handleValidException(MethodArgumentNotValidException e) {
log.error("params valid exception:", e);
return wrapperBindingResult(e.getBindingResult());
}
/**
* 包装绑定异常结果
*
* @param bindingResult 绑定结果
* @return 异常结果
*/
private ApiResponse wrapperBindingResult(BindingResult bindingResult) {
StringBuilder msg = new StringBuilder();
for (ObjectError error : bindingResult.getAllErrors()) {
msg.append(", ");
if (error instanceof FieldError) {
msg.append(((FieldError) error).getField()).append(": ");
}
msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());
}
return ApiResponse.failed(ResultCode.UNAUTHORIZED, msg.substring(2));
}
/**
* 这里直接返回true,表示对任何handler的responsebody都调用beforeBodyWrite方法
*
* @param methodParameter
* @param aClass
* @return
*/
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
// 修改responsebody
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (o instanceof ApiResponse) {
return o;
} else {
return ApiResponse.success(o);
}
}
/**
* 未定义异常
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ApiResponse handleException(Exception e) {
log.error("handleException :", e);
return ApiResponse.failed(ResultCode.FAILED);
}
}
TestDemoController
/**
* @author tengwang8
* @date 2022年09月19日 11:02
*/
@RestController
public class TestDemoController {
@GetMapping("/tt")
@ResponseBody
public String test(){
return "sadsadsad";
}
}
控制台输出:
java.lang.ClassCastException: com.example.demoweb.config.ApiResponse cannot be cast to java.lang.String
at org.springframework.http.converter.StringHttpMessageConverter.addDefaultHeaders(StringHttpMessageConverter.java:44) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:211) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:293) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:183) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:135) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1070) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) [spring-webmvc-5.3.22.jar:5.3.22]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) [tomcat-embed-core-9.0.65.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) [spring-webmvc-5.3.22.jar:5.3.22]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) [tomcat-embed-core-9.0.65.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-embed-websocket-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) [spring-web-5.3.22.jar:5.3.22]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) [spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) [spring-web-5.3.22.jar:5.3.22]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) [spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) [spring-web-5.3.22.jar:5.3.22]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.65.jar:9.0.65]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_102]
通过错误信息进行分析
StringHttpMessageConverter
我们看到有返回string的消息转换器报错的异常,我们断点看一下:
然后我们这边到是对字符串是重新包装成了一个对象,所以就会报错。
我们再尝试断点看一下如果返回值是实体对象的时候,发现这个转换器是org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
:
那说明Spring 内部是通过不同的消息转换器(MessageConverter)来处理不同的返回值,每个MessageConverer
是根据返回类型和媒体类型来选择处理的。
解决方案
其实解决很简单,判断一下返回值是否是String,如果是String的话就将对象处理为Json串就好了,废话不多说,直接上代码:文章来源:https://www.toymoban.com/news/detail-536251.html
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (o instanceof ApiResponse) {
return o;
} else {
ApiResponse<Object> success = ApiResponse.success(o);
return o instanceof String ? JSONUtil.toJsonStr(success) : success;
}
}
总结
这个问题虽然解决了,其实我是觉得不应该这里随便写这个返回,应该按照规范来定义返回,String
类型的数据适不适合返回的,这样不便于对接口响应字段的理解,所以这个虽说是技术问题,实际是规范问题,应该规范大家响应都需要返回实体对象,不管是返回一个字段还是多个字段,必须要使用实体返回。文章来源地址https://www.toymoban.com/news/detail-536251.html
到了这里,关于使用ResponseBodyAdvice返回值为String出现cannot be cast to java.lang.String异常的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!