Springboot使用了ResponseBodyAdvice處理返回值異常

為了統(tǒng)一接口響應(yīng)的報文懂缕,現(xiàn)實了ResponseBodyAdvice接口,通過這個接口的實現(xiàn)類來統(tǒng)一處理報文

public class ResponseResultInterceptor implements HandlerInterceptor {
    //標(biāo)記名稱
    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)){
                // 設(shè)置此請求返回體王凑,需要包裝,往下傳遞聋丝,在ResponseBodyAdvice接口進(jìn)行判斷
                request.setAttribute(RESPONSE_RESULT_ANN, true);
            }
        }
        return true;
    }
}
/**
 * 使用 @ControllerAdvice & ResponseBodyAdvice
 * 攔截Controller方法默認(rèn)返回參數(shù)索烹,統(tǒng)一處理返回值/響應(yīng)體
 */
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {

    /**
     * 需要忽略的地址
     */
    private static String[] ignores = new String[]{
            //過濾swagger相關(guān)的請求的接口,不然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;
    }


    /**
     *  判斷是否要執(zhí)行 beforeBodyWrite 方法弱睦,true為執(zhí)行百姓,false不執(zhí)行
     */
    @Override
    public boolean supports(MethodParameter method, Class<? extends HttpMessageConverter<?>> arg1) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        // 判斷請求是否有包裝標(biāo)記
        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類型的時候,程序拋出一個類轉(zhuǎn)換的異常:

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請求時候沒有問題况木,在瀏覽器中直接請求就發(fā)生類型轉(zhuǎn)換異常垒拢。

發(fā)現(xiàn)這個跟MessageConverter有關(guān)系。Springmvc內(nèi)部定義了九個不同的MessageConverter用來處理不同的返回值火惊。在AbstractMessageConverterMethodProcessor類下面的writeWithMessageConverters方法可以看出來求类,每個MessageConverer是根據(jù)返回類型和媒體類型來選擇處理的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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惶岭,隨后出現(xiàn)的幾起案子寿弱,更是在濱河造成了極大的恐慌,老刑警劉巖按灶,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件症革,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸯旁,警方通過查閱死者的電腦和手機(jī)噪矛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羡亩,“玉大人摩疑,你說我怎么就攤上這事∥访” “怎么了雷袋?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我楷怒,道長蛋勺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任鸠删,我火速辦了婚禮抱完,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刃泡。我一直安慰自己巧娱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布烘贴。 她就那樣靜靜地躺著禁添,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桨踪。 梳的紋絲不亂的頭發(fā)上老翘,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音锻离,去河邊找鬼铺峭。 笑死,一個胖子當(dāng)著我的面吹牛汽纠,可吹牛的內(nèi)容都是我干的卫键。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼疏虫,長吁一口氣:“原來是場噩夢啊……” “哼永罚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卧秘,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呢袱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后翅敌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羞福,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年蚯涮,在試婚紗的時候發(fā)現(xiàn)自己被綠了治专。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡遭顶,死狀恐怖张峰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棒旗,我是刑警寧澤喘批,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響饶深,放射性物質(zhì)發(fā)生泄漏餐曹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一敌厘、第九天 我趴在偏房一處隱蔽的房頂上張望台猴。 院中可真熱鬧,春花似錦俱两、人聲如沸饱狂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗡官。三九已至,卻和暖如春毯焕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背磺樱。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工纳猫, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人竹捉。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓芜辕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親块差。 傳聞我的和親對象是個殘疾皇子侵续,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容