为了统一接口响应的报文,现实了ResponseBodyAdvice接口,通过这个接口的实现类来统一处理报文
public class ResponseResultInterceptor implements HandlerInterceptor {
//标记名称
public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod = (HandlerMethod) handler;
final Class<?> clazz = handlerMethod.getBeanType();
// 判断是否在类对象上添加了注解
if(clazz.isAnnotationPresent(RestController.class) || clazz.isAnnotationPresent(ResponseBody.class)){
// 设置此请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口进行判断
request.setAttribute(RESPONSE_RESULT_ANN, true);
}
}
return true;
}
}
/**
* 使用 @ControllerAdvice & ResponseBodyAdvice
* 拦截Controller方法默认返回参数,统一处理返回值/响应体
*/
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
/**
* 需要忽略的地址
*/
private static String[] ignores = new String[]{
//过滤swagger相关的请求的接口,不然swagger会提示base-url被拦截
"/swagger-resources",
"/v2/api-docs"
};
/**
* 判断url是否需要拦截
* @param uri
* @return
*/
private boolean ignoring(String uri) {
for (String string : ignores) {
if (uri.contains(string)) {
return true;
}
}
return false;
}
/**
* 判断是否要执行 beforeBodyWrite 方法,true为执行,false不执行
*/
@Override
public boolean supports(MethodParameter method, Class<? extends HttpMessageConverter<?>> arg1) {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = sra.getRequest();
// 判断请求是否有包装标记
return request.getAttribute(RESPONSE_RESULT_ANN) != null;
}
/**
* 对返回值做包装处理
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter arg1, MediaType arg2,
Class<? extends HttpMessageConverter<?>> arg3, ServerHttpRequest request, ServerHttpResponse response) {
//判断url是否需要拦截
if (this.ignoring(request.getURI().toString())) {
return body;
}
if (body instanceof Result) {
return body;
}
return Result.success(body);
}
}
然而在测试返回值是String类型的时候,程序抛出一个类转换的异常:
java.lang.ClassCastException: cn.huolala.bme.spi.common.model.Result cannot be cast to java.lang.String
at org.springframework.http.converter.StringHttpMessageConverter.addDefaultHeaders(StringHttpMessageConverter.java:44)
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:211)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:290)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:181)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:123)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at cn.huolala.bme.spi.web.config.CorsFilter.doFilter(CorsFilter.java:51)
at cn.huolala.bme.spi.web.config.CorsFilter$$FastClassBySpringCGLIB$$104bd17c.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at cn.huolala.bme.plugin.core.aspect.SpiInterceptorInvokerImpl.invoke(SpiInterceptorInvokerImpl.java:16)
at sun.reflect.GeneratedMethodAccessor124.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at cn.huolala.bme.plugin.core.aop.SpiInvocation.process(SpiInvocation.java:43)
at cn.huolala.bme.plugin.log.LogPlugin.intercept(LogPlugin.java:56)
at cn.huolala.bme.plugin.core.aop.SpiPluginHandler.invoke(SpiPluginHandler.java:30)
at com.sun.proxy.$Proxy201.invoke(Unknown Source)
at cn.huolala.bme.plugin.core.aspect.SpiInterceptorPointcut.invoke(SpiInterceptorPointcut.java:54)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
at cn.huolala.bme.spi.web.config.CorsFilter$$EnhancerBySpringCGLIB$$ddd3bac8.doFilter(<generated>)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke$original$qNsflBfP(StandardHostValve.java:139)
at org.apache.catalina.core.StandardHostValve.invoke$original$qNsflBfP$accessor$sPsJTqrz(StandardHostValve.java)
at org.apache.catalina.core.StandardHostValve$auxiliary$b0wAX8bs.call(Unknown Source)
at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:88)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at com.alibaba.ttl.TtlRunnable.run(TtlRunnable.java:59)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
奇怪的是使用swagger请求时候没有问题,在浏览器中直接请求就发生类型转换异常。
发现这个跟MessageConverter有关系。Springmvc内部定义了九个不同的MessageConverter用来处理不同的返回值。在AbstractMessageConverterMethodProcessor类下面的writeWithMessageConverters方法可以看出来,每个MessageConverer是根据返回类型和媒体类型来选择处理的MessageConverter的,下面是代码片段:
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
if (((GenericHttpMessageConverter) messageConverter).canWrite(
declaredType, valueType, selectedMediaType)) {
outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
inputMessage, outputMessage);
if (outputValue != null) {
addContentDispositionHeader(inputMessage, outputMessage);
((GenericHttpMessageConverter) messageConverter).write(
outputValue, declaredType, selectedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
"\" using [" + messageConverter + "]");
}
}
return;
}
}
else if (messageConverter.canWrite(valueType, selectedMediaType)) {
outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
inputMessage, outputMessage);
if (outputValue != null) {
addContentDispositionHeader(inputMessage, outputMessage);
((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
"\" using [" + messageConverter + "]");
}
}
return;
}
}
}
终于找到原因:swagger请求时类型是application/json, 浏览器请求时类型是text/xml,使用StringMessageConverter。
解决方式&参考文章:https://blog.csdn.net/weixin_33961829/article/details/92143322
网友评论