Spring Cloud Gateway 全局通用異常處理

為什么需要全局異常處理

在傳統(tǒng) Spring Boot 應(yīng)用中孵奶, 我們 @ControllerAdvice 來(lái)處理全局的異常送挑,進(jìn)行統(tǒng)一包裝返回


// 摘至 spring cloud alibaba console 模塊處理
@ControllerAdvice
public class ConsoleExceptionHandler {

    @ExceptionHandler(AccessException.class)
    private ResponseEntity<String> handleAccessException(AccessException e) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
    }
}

例如: ③ 處應(yīng)用調(diào)用數(shù)據(jù)庫(kù)異常欺嗤,通過(guò) @ControllerAdvice 包裝異常請(qǐng)求響應(yīng)給客戶(hù)端


但在微服務(wù)架構(gòu)下汽摹, 例如 ② 處 網(wǎng)關(guān)調(diào)用業(yè)務(wù)微服務(wù)失斖月摹(轉(zhuǎn)發(fā)失敗渣刷、調(diào)用異常鹦肿、轉(zhuǎn)發(fā)失敗)辅柴,在應(yīng)用設(shè)置的 @ControllerAdvice 將失效箩溃,因?yàn)榱髁扛緵](méi)有轉(zhuǎn)發(fā)到應(yīng)用上處理。

如上圖: 模擬所有路由斷言都不匹配 404 , 和 spring boot 默認(rèn)保持一致的錯(cuò)誤輸出頁(yè)面碌嘀。 顯然我們?cè)诰W(wǎng)關(guān)同樣配置 @ControllerAdvice 是不能解決問(wèn)題,因?yàn)?spring cloud gateway 是基于 webflux 反應(yīng)式編程涣旨。

解決方法

默認(rèn)處理流程

  • ExceptionHandlingWebHandler 作為 spring cloud gateway 最核心 WebHandler 的一部分會(huì)進(jìn)行異常處理的過(guò)濾
public class ExceptionHandlingWebHandler extends WebHandlerDecorator {
    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        Mono<Void> completion;
        try {
            completion = super.handle(exchange);
        }
        catch (Throwable ex) {
            completion = Mono.error(ex);
        }

     // 獲取全局的 WebExceptionHandler 執(zhí)行
        for (WebExceptionHandler handler : this.exceptionHandlers) {
            completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
        }
        return completion;
    }
}
  • 默認(rèn)實(shí)現(xiàn) DefaultErrorWebExceptionHandler
public class DefaultErrorWebExceptionHandler  {

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
     // 根據(jù)客戶(hù)端 `accpet` 請(qǐng)求頭決定返回什么資源,如上瀏覽器返回的是 頁(yè)面
        return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
    }
}

// 模擬指定 `accpet` 情況
curl --location --request GET 'http://localhost:9999/adminx/xx' \  18:09:23
     --header 'Accept: application/json'
{"timestamp":"2020-05-24 18:09:24","path":"/adminx/xx","status":404,"error":"Not Found","message":null,"requestId":"083c48e3-2"}?

重寫(xiě) ErrorWebExceptionHandler

/**
 * @author lengleng
 * @date 2020/5/23
 * <p>
 * 網(wǎng)關(guān)異常通用處理器股冗,只作用在webflux 環(huán)境下 , 優(yōu)先級(jí)低于 {@link ResponseStatusExceptionHandler} 執(zhí)行
 */
@Slf4j
@Order(-1)
@RequiredArgsConstructor
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
    private final ObjectMapper objectMapper;

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();

        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        // header set
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        if (ex instanceof ResponseStatusException) {
            response.setStatusCode(((ResponseStatusException) ex).getStatus());
        }

        return response
                .writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    try {
                        return bufferFactory.wrap(objectMapper.writeValueAsBytes(R.failed(ex.getMessage())));
                    } catch (JsonProcessingException e) {
                        log.warn("Error writing response", ex);
                        return bufferFactory.wrap(new byte[0]);
                    }
                }));
    }
}

總結(jié)

  • 重寫(xiě)的 DefaultErrorWebExceptionHandler 優(yōu)先級(jí)一定要小于內(nèi)置 ResponseStatusExceptionHandler 經(jīng)過(guò)它處理的獲取對(duì)應(yīng)錯(cuò)誤類(lèi)的 響應(yīng)碼
  • 其他擴(kuò)展 可以參考 SentinelBlockExceptionHandler sentinel 整合網(wǎng)關(guān)的處理霹陡,不過(guò)整體和默認(rèn)的異常處理沒(méi)有什么區(qū)別
  • 基礎(chǔ)環(huán)境說(shuō)明:Spring Cloud Hoxton.SR4 & Spring Boot 2.3.0
  • 具體實(shí)現(xiàn)代碼參考:https://gitee.com/log4j/pig
    image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市止状,隨后出現(xiàn)的幾起案子烹棉,更是在濱河造成了極大的恐慌,老刑警劉巖怯疤,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浆洗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡集峦,警方通過(guò)查閱死者的電腦和手機(jī)伏社,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)抠刺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人摘昌,你說(shuō)我怎么就攤上這事速妖。” “怎么了聪黎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵买优,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我挺举,道長(zhǎng),這世上最難降的妖魔是什么烘跺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任湘纵,我火速辦了婚禮,結(jié)果婚禮上滤淳,老公的妹妹穿的比我還像新娘梧喷。我一直安慰自己,他們只是感情好脖咐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布铺敌。 她就那樣靜靜地躺著,像睡著了一般屁擅。 火紅的嫁衣襯著肌膚如雪偿凭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天派歌,我揣著相機(jī)與錄音弯囊,去河邊找鬼。 笑死胶果,一個(gè)胖子當(dāng)著我的面吹牛匾嘱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播早抠,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼霎烙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蕊连?” 一聲冷哼從身側(cè)響起悬垃,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎甘苍,沒(méi)想到半個(gè)月后盗忱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡羊赵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年趟佃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扇谣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闲昭,死狀恐怖罐寨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情序矩,我是刑警寧澤鸯绿,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站簸淀,受9級(jí)特大地震影響瓶蝴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜租幕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一舷手、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧劲绪,春花似錦男窟、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至颤枪,卻和暖如春汗捡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背畏纲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工凉唐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人霍骄。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓台囱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親读整。 傳聞我的和親對(duì)象是個(gè)殘疾皇子簿训,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345