最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

聊聊Spring?Cloud?Gateway過濾器精確控制異常返回問題

 更新時間:2021年11月25日 08:52:48   作者:程序員欣宸  
這篇文章主要介紹了Spring?Cloud?Gateway過濾器精確控制異常返回問題,本篇任務(wù)就是分析上述現(xiàn)象的原因,通過閱讀源碼搞清楚返回碼和響應(yīng)body生成的具體邏輯,需要的朋友可以參考下

歡迎訪問我的GitHub

這里分類和匯總了欣宸的全部原創(chuàng)(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽在《Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容》一文中,咱們通過filter成功修改請求body的內(nèi)容,當(dāng)時留下個問題:在filter中如果發(fā)生異常(例如請求參數(shù)不合法),拋出異常信息的時候,調(diào)用方收到的返回碼和body都是Spring Cloud Gateway框架處理后的,調(diào)用方無法根據(jù)這些內(nèi)容知道真正的錯誤原因,如下圖:

本篇任務(wù)就是分析上述現(xiàn)象的原因,通過閱讀源碼搞清楚返回碼和響應(yīng)body生成的具體邏輯

  • 這里將分析結(jié)果提前小結(jié)出來,如果您很忙碌沒太多時間卻又想知道最終原因,直接關(guān)注以下小結(jié)即可:
  1. Spring Cloud Gateway應(yīng)用中,有個ErrorAttributes類型的bean,它的getErrorAttributes方法返回了一個map
  2. 應(yīng)用拋出異常時,返回碼來自上述map的status的值,返回body是整個map序列化的結(jié)果
  3. 默認(rèn)情況下ErrorAttributes的實現(xiàn)類是DefaultErrorAttributes
  • 再看上述map的status值(也就是response的返回碼),在DefaultErrorAttributes是如何生成的:

先看異常對象是不是ResponseStatusException類型

  1. 如果是ResponseStatusException類型,就調(diào)用異常對象的getStatus方法作為返回值
  2. 如果不是ResponseStatusException類型,再看異常類有沒有ResponseStatus注解,
  3. 如果有,就取注解的code屬性作為返回值
  4. 如果異常對象既不是ResponseStatusException類型,也沒有ResponseStatus注解,就返回500

最后看map的message字段(也就是response body的message字段),在DefaultErrorAttributes是如何生成的:

  1. 異常對象是不是BindingResult類型
  2. 如果不是BindingResult類型,就看是不是ResponseStatusException類型
  3. 如果是,就用getReason作為返回值
  4. 如果也不是ResponseStatusException類型,就看異常類有沒有ResponseStatus注解,如果有就取該注解的reason屬性作為返回值
  5. 如果通過注解取得的reason也無效,就返回異常的getMessage字段

上述內(nèi)容就是本篇精華,但是并未包含分析過程,如果您對Spring Cloud源碼感興趣,請允許欣宸陪伴您來一次短暫的源碼閱讀之旅

Spring Cloud Gateway錯誤處理源碼

首先要看的是配置類ErrorWebFluxAutoConfiguration.java,這里面向spring注冊了兩個實例,每個都非常重要,咱們先關(guān)注第一個,也就是說ErrorWebExceptionHandler的實現(xiàn)類是DefaultErrorWebExceptionHandler:

處理異常時,會通過FluxOnErrorResume調(diào)用到這個ErrorWebExceptionHandler的handle方法處理,該方法在其父類AbstractErrorWebExceptionHandler.java中,如下圖,紅框位置的代碼是關(guān)鍵,異常返回內(nèi)容就是在這里決定的:

展開這個getRoutingFunction方法,可見會調(diào)用renderErrorResponse來處理響應(yīng):

@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
		return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
	}

打開renderErrorResponse方法,如下所示,真相大白了!

protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
  // 取出所有錯誤信息
  Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
  
  // 構(gòu)造返回的所有信息 
  return ServerResponse
           // 控制返回碼
           .status(getHttpStatus(error))
           // 控制返回ContentType
           .contentType(MediaType.APPLICATION_JSON)
           // 控制返回內(nèi)容
           .body(BodyInserters.fromValue(error));
}

通過上述代碼,咱們得到兩個重要結(jié)論:

  • 返回給調(diào)用方的狀態(tài)碼,取決于getHttpStatus方法的返回值
  • 返回給調(diào)用方的body,取決于error的內(nèi)容

都已經(jīng)讀到了這里,自然要看看getHttpStatus的內(nèi)部,如下所示,status來自入?yún)ⅲ?/p>

protected int getHttpStatus(Map<String, Object> errorAttributes) {
  return (int) errorAttributes.get("status");
}
  • 至此,咱們可以得出一個結(jié)論:getErrorAttributes方法的返回值是決定返回碼和返回body的關(guān)鍵!
  • 來看看這個getErrorAttributes方法的廬山真面吧,在DefaultErrorAttributes.java中(回憶剛才看ErrorWebFluxAutoConfiguration.java的時候,前面曾提到里面的東西都很重要,也包括errorAttributes方法):
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map<String, Object> errorAttributes = this.getErrorAttributes(request, options.isIncluded(Include.STACK_TRACE));
        if (Boolean.TRUE.equals(this.includeException)) {
            options = options.including(new Include[]{Include.EXCEPTION});
        }

        if (!options.isIncluded(Include.EXCEPTION)) {
            errorAttributes.remove("exception");
        }

        if (!options.isIncluded(Include.STACK_TRACE)) {
            errorAttributes.remove("trace");
        }

        if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
            errorAttributes.put("message", "");
        }

        if (!options.isIncluded(Include.BINDING_ERRORS)) {
            errorAttributes.remove("errors");
        }

        return errorAttributes;
    }

篇幅所限,就不再展開上述代碼了,直接上結(jié)果吧:

  • 返回碼來自determineHttpStatus的返回
  • message字段來自determineMessage的返回打開determineHttpStatus方法,終極答案揭曉,請關(guān)注中文注釋:
private HttpStatus determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        // 異常對象是不是ResponseStatusException類型
        return error instanceof ResponseStatusException 
        // 如果是ResponseStatusException類型,就調(diào)用異常對象的getStatus方法作為返回值
        ? ((ResponseStatusException)error).getStatus() 
        // 如果不是ResponseStatusException類型,再看異常類有沒有ResponseStatus注解,
        // 如果有,就取注解的code屬性作為返回值
        : (HttpStatus)responseStatusAnnotation.getValue("code", HttpStatus.class)
        // 如果異常對象既不是ResponseStatusException類型,也沒有ResponseStatus注解,就返回500
        .orElse(HttpStatus.INTERNAL_SERVER_ERROR);
    }

另外,message字段的內(nèi)容也確定了:

 private String determineMessage(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        // 異常對象是不是BindingResult類型
        if (error instanceof BindingResult) {
            // 如果是,就用getMessage作為返回值
            return error.getMessage();
        } 
        // 如果不是BindingResult類型,就看是不是ResponseStatusException類型
        else if (error instanceof ResponseStatusException) {
            // 如果是,就用getReason作為返回值
            return ((ResponseStatusException)error).getReason();
        } else {
            // 如果也不是ResponseStatusException類型,
            // 就看異常類有沒有ResponseStatus注解,如果有就取該注解的reason屬性作為返回值
            String reason = (String)responseStatusAnnotation.getValue("reason", String.class).orElse("");
            if (StringUtils.hasText(reason)) {
                return reason;
            } else {
                // 如果通過注解取得的reason也無效,就返回異常的getMessage字段
                return error.getMessage() != null ? error.getMessage() : "";
            }
        }
    }
  • 至此,源碼分析已完成,最終的返回碼和返回內(nèi)容究竟如何控制,相信聰明的您心里應(yīng)該有數(shù)了,下一篇《實戰(zhàn)篇》咱們趁熱打鐵,寫代碼試試精確控制返回碼和返回內(nèi)容
  • 提前劇透,接下來的《實戰(zhàn)篇》會有以下內(nèi)容呈現(xiàn):
  • 直接了當(dāng),控制返回碼和body中的error字段
  • 小小攔路虎,見招拆招
  • 簡單易用,通過注解控制返回信息
  • 終極方案,完全定制返回內(nèi)容

到此這篇關(guān)于Spring?Cloud?Gateway過濾器精確控制異常返回(分析篇)的文章就介紹到這了,更多相關(guān)Spring?Cloud?Gateway過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java面試題沖刺第十七天--基礎(chǔ)篇3

    Java面試題沖刺第十七天--基礎(chǔ)篇3

    這篇文章主要為大家分享了最有價值的三道java基礎(chǔ)面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-08-08
  • SpringBoot+Quartz實現(xiàn)動態(tài)定時任務(wù)

    SpringBoot+Quartz實現(xiàn)動態(tài)定時任務(wù)

    這篇文章主要為大家詳細(xì)介紹了springBoot+Quartz實現(xiàn)動態(tài)定時任務(wù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • 如何在springboot中引入?yún)?shù)校驗

    如何在springboot中引入?yún)?shù)校驗

    一般我們判斷前端傳過來的參數(shù),需要對某些值進(jìn)行判斷,是否滿足條件,而springboot相關(guān)的參數(shù)校驗注解,可以解決我們這個問題,本文給大家介紹如何在springboot中引入?yún)?shù)校驗,感興趣的朋友一起看看吧
    2023-12-12
  • Mybatis或Mybatis-Plus框架的xml文件中特殊符號的使用詳解

    Mybatis或Mybatis-Plus框架的xml文件中特殊符號的使用詳解

    這篇文章主要介紹了Mybatis或Mybatis-Plus框架的xml文件中特殊符號的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Eclipse不自動編譯java文件的終極解決方法

    Eclipse不自動編譯java文件的終極解決方法

    這篇文章主要介紹了Eclipse不自動編譯java文件的終極解決方法,需要的朋友可以參考下
    2015-12-12
  • 實例分析Java單線程與多線程

    實例分析Java單線程與多線程

    本篇文章通過代碼實例給大家詳細(xì)講述了Java單線程與多線程的相關(guān)原理和知識點總結(jié),需要的朋友可以學(xué)習(xí)下。
    2018-02-02
  • 詳解Java并發(fā)包中線程池ThreadPoolExecutor

    詳解Java并發(fā)包中線程池ThreadPoolExecutor

    ThreadPoolExecutor是Java語言對于線程池的實現(xiàn)。線程池技術(shù)使線程在使用完畢后不回收而是重復(fù)利用。如果線程能夠復(fù)用,那么我們就可以使用固定數(shù)量的線程來解決并發(fā)問題,這樣一來不僅節(jié)約了系統(tǒng)資源,而且也會減少線程上下文切換的開銷
    2021-06-06
  • Java對文本文件MD5加密并ftp傳送到遠(yuǎn)程主機目錄的實現(xiàn)方法

    Java對文本文件MD5加密并ftp傳送到遠(yuǎn)程主機目錄的實現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于Java對文本文件MD5加密并ftp傳送到遠(yuǎn)程主機目錄的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • 詳解springboot 使用c3p0數(shù)據(jù)庫連接池的方法

    詳解springboot 使用c3p0數(shù)據(jù)庫連接池的方法

    本篇文章主要介紹了springboot 使用c3p0數(shù)據(jù)庫連接池的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • 記一次線上SpringCloud Feign請求服務(wù)超時異常排查問題

    記一次線上SpringCloud Feign請求服務(wù)超時異常排查問題

    這篇文章主要介紹了記一次線上SpringCloud Feign請求服務(wù)超時異常排查問題,本項目與下游項目均注冊在Eureka上面,對這個1秒就超時感到很迷惑,于是開始查閱底層源碼之旅。需要的朋友可以參考下
    2022-01-01

最新評論

水富县| 贵州省| 珠海市| 长顺县| 淮北市| 通江县| 施甸县| 东城区| 阳原县| 玉门市| 义乌市| 禹城市| 巴林右旗| 呼和浩特市| 仙桃市| 揭阳市| 天全县| 岐山县| 台南市| 湖南省| 乌鲁木齐县| 乳山市| 大丰市| 莎车县| 昭通市| 额尔古纳市| 聂荣县| 英山县| 奉化市| 尖扎县| 宁阳县| 庄浪县| 方正县| 湘潭市| 墨江| 灌阳县| 浠水县| 五家渠市| 新安县| 屏山县| 无为县|