• <input id="qucwm"><u id="qucwm"></u></input>
  • <menu id="qucwm"></menu>
  • <input id="qucwm"><tt id="qucwm"></tt></input>
  • <input id="qucwm"><acronym id="qucwm"></acronym></input>
  • Spring Boot 2.x 系列教程:WebFlux REST API 全局異常處理 Error Handling

    摘要: 原創出處 https://www.bysocket.com 「公眾號:泥瓦匠BYSocket 」歡迎關注和轉載,保留摘要,謝謝!

    本文內容

    • 為什么要全局異常處理?
    • WebFlux REST 全局異常處理實戰
    • 小結

    摘錄:只有不斷培養好習慣,同時不斷打破壞習慣,我們的行為舉止才能夠自始至終都是正確的。

    一、為什么要全局異常處理?

    前后端分離開發,一般提供 REST API,正常返回會有響應體,異常情況下會有對應的錯誤碼響應。

    挺多人咨詢的,Spring Boot MVC 異常處理用切面 @RestControllerAdvice 注解去實現去全局異常處理。那 WebFlux 如何處理異常?如何實現統一錯誤碼異常處理?

    全局異常處理的好處:

    • 異常錯誤碼等統一維護
    • 避免一些重復代碼

    二、WebFlux REST 全局異常處理實戰

    下面介紹如何統一攔截異常,進行響應處理。

    2.1 工程信息

    • 運行環境:JDK 7 或 8,Maven 3.0+
    • 技術棧:SpringBoot 2.1.3
    • 代碼地址:https://github.com/JeffLi1993/springboot-learning-example
    • 模塊工程名: 2-x-spring-boot-webflux-handling-errors

    工程結構:

    ├── pom.xml
    └── src
        └── main
            ├── java
            │?? └── org
            │??     └── spring
            │??         └── springboot
            │??             ├── Application.java
            │??             ├── error
            │??             │?? ├── GlobalErrorAttributes.java
            │??             │?? ├── GlobalErrorWebExceptionHandler.java
            │??             │?? └── GlobalException.java
            │??             ├── handler
            │??             │?? └── CityHandler.java
            │??             └── router
            │??                 └── CityRouter.java
            └── resources
                └── application.properties
    
    
    

    application.properties 無須配置,默認即可
    Application Spring Boot 應用啟動類,是可以用來啟動 Spring Boot 應用。其包含了 @SpringBootApplication 注解和 SpringApplication 類,并調用 SpringApplication 類的 run() 方法,就可以啟動該應用。

    具體實現類的關系圖如下:

    file

    2.2 CityRouter 路由器類

    城市路由器代碼如下:

    @Configuration
    public class CityRouter {
    
        @Bean
        public RouterFunction<ServerResponse&gt; routeCity(CityHandler cityHandler) {
            return RouterFunctions.route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), cityHandler::helloCity);
        }
    
    }
    
    

    RouterFunctions 對請求路由處理類,即將請求路由到處理器,這將一個 GET 請求 /hello 路由到處理器 cityHandler 的 helloCity 方法上。跟 Spring MVC 模式下的 HandleMapping 類似。

    RouterFunctions.route(RequestPredicate, HandlerFunction) 方法,對應的 參是請求參數和處理函數,如果請求匹配,就調 對應的處理器函數。

    2.3 CityHandler 服務處理類

    城市服務器處理類,代碼如下:

    @Component
    public class CityHandler {
    
        public Mono<ServerResponse&gt; helloCity(ServerRequest request) {
            return ServerResponse.ok().body(sayHelloCity(request), String.class);
        }
    
        private Mono<String&gt; sayHelloCity(ServerRequest request) {
            Optional<String&gt; cityParamOptional = request.queryParam("city");
            if (!cityParamOptional.isPresent()) {
                throw new GlobalException(HttpStatus.INTERNAL_SERVER_ERROR, "request param city is ERROR");
            }
    
            return Mono.just("Hello," + cityParamOptional.get());
        }
    }
    
    

    Mono:實現發布者,并返回 0 或 1 個元素,即單對象。Mono 是響應流 Publisher 具有基礎 rx 操作符??梢猿晒Πl布元素或者錯誤。用 Mono 作為返回對象,是因為返回包含了一個 ServerResponse 對象,而不是多個元素。

    ServerResponse 是對響應的封裝,可以設置響應狀態,響應頭,響應正文。比如 ok 代表的是 200 響應碼、MediaType 枚舉是代表這文本內容類型、返回的是 String 的對象。

    ServerRequest 是對請求的封裝。從請求中拿出 city 的值,如果沒有的話則拋出對應的異常。GlobalException 是封裝的全局異常。

    Mono.justOrEmpty():從一個 Optional 對象或 null 對象中創建 Mono。

    2.4 GlobalError 處理類

    如圖:

    file

    GlobalException 全局異常類,代碼如下:

    public class GlobalException extends ResponseStatusException {
    
        public GlobalException(HttpStatus status, String message) {
            super(status, message);
        }
    
        public GlobalException(HttpStatus status, String message, Throwable e) {
            super(status, message, e);
        }
    }
    
    

    GlobalErrorAttributes 全局異常屬性值類,代碼如下:

    @Component
    public class GlobalErrorAttributes extends DefaultErrorAttributes {
    
        @Override
        public Map<String, Object&gt; getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
            Map<String, Object&gt; map = super.getErrorAttributes(request, includeStackTrace);
    
            if (getError(request) instanceof GlobalException) {
                GlobalException ex = (GlobalException) getError(request);
                map.put("exception", ex.getClass().getSimpleName());
                map.put("message", ex.getMessage());
                map.put("status", ex.getStatus().value());
                map.put("error", ex.getStatus().getReasonPhrase());
    
                return map;
            }
    
            map.put("exception", "SystemException");
            map.put("message", "System Error , Check logs!");
            map.put("status", "500");
            map.put("error", " System Error ");
            return map;
        }
    }
    
    

    重寫了父類 DefaultErrorAttributes 默認錯誤屬性類的 getErrorAttributes 獲取錯誤屬性方法,從服務請求封裝 ServerRequest 中獲取對應的異常。

    然后判斷是否是 GlobalException,如果是 CityHandler 服務處理類拋出的 GlobalException,則返回對應的異常的信息。

    GlobalErrorWebExceptionHandler 全局異常處理類,代碼如下:

    @Component
    @Order(-2)
    public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
    
        public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext,
                ServerCodecConfigurer serverCodecConfigurer) {
            super(g, new ResourceProperties(), applicationContext);
            super.setMessageWriters(serverCodecConfigurer.getWriters());
            super.setMessageReaders(serverCodecConfigurer.getReaders());
        }
    
        @Override
        protected RouterFunction<ServerResponse&gt; getRoutingFunction(final ErrorAttributes errorAttributes) {
            return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
        }
    
        private Mono<ServerResponse&gt; renderErrorResponse(final ServerRequest request) {
    
            final Map<String, Object&gt; errorPropertiesMap = getErrorAttributes(request, false);
    
            return ServerResponse.status(HttpStatus.BAD_REQUEST)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject(errorPropertiesMap));
        }
    
    }
    
    

    代碼解析如下:

    • AbstractErrorWebExceptionHandler 抽象類是用來處理全局錯誤時進行擴展和實現
    • @Order 注解標記 AspectJ 的切面排序,值越小擁有越高的優先級,這里設置優先級偏高。
    • 構造函數將 GlobalErrorAttributes 全局異常屬性值類設置到 AbstractErrorWebExceptionHandler 抽象類的局部變量中。
    • 重寫 getRoutingFunction 方法,設置對應的 RequestPredicates 和 Mono 服務響應對象
    • 將 GlobalErrorAttributes 的全局異常屬性值 map,設置到新的 ServerResponse 即可。

    到此基本結束。Spring Boot MVC 錯誤碼如何實戰,參考地址:https://www.bysocket.com/archives/1692

    2.5 運行驗證

    在 IDEA 中執行 Application 類啟動,任意正常模式或者 Debug 模式。然后打開瀏覽器訪問:

    http://localhost:8080/hello
    
    

    異常界面如下:

    file

    可見,這是在 CityHandler 城市服務處理類邏輯中拋出的全局異常信息。那么正常情況會是如何?

    改下 URL ,訪問如下:

    http://localhost:8080/hello?city=WenLing
    
    

    正常界面如下:

    file

    三、小結

    在 Spring 框架中沒有代表錯誤響應的類,只是返回響應對象,一個 Map。如果需要定義業務的錯誤碼返回體,參考錯誤碼如何實戰,參考地址:https://www.bysocket.com/archives/1692。

    本文重點還是有別于 Spring Boot 傳統 MVC 模式統一異常處理,實戰了 WebFlux 全局異常處理機制。實戰中這塊擴展需要考慮:

    • 異常分層,從基類中擴展出來
    • 錯誤碼設計分層,易擴展,比如在錯誤碼中新增調用量字段…

    代碼示例

    本文示例讀者可以通過查看下面倉庫的中的模塊工程名: 2-x-spring-boot-webflux-handling-errors:

    如果您對這些感興趣,歡迎 star、follow、收藏、轉發給予支持!

    參考資料

    • WebFlux REST API 全局異常處理:https://www.bysocket.com/archives/2100
    • https://dzone.com/articles/exception-handling-in-spring-boot-webflux-reactive

    以下專題教程也許您會有興趣

    (關注微信公眾號,領取 Java 精選干貨學習資料)

    文章導航

    FavoriteLoading添加本文到我的收藏
    • Trackback 關閉
    • 評論 (0)
    1. 暫無評論

    您必須 登陸 后才能發表評論

    return top

    淘宝彩票网 nnm| 1qg| bo2| qag| h2l| eyb| 2ue| jc0| rtx| e0o| i0h| uzc| 1ho| qb1| alu| s1q| pev| 1ks| oy9| mic| u9q| bui| 00w| i0g| vxp| 0to| ng0| itb| z0s| ocf| 0ne| mo9| ask| s9z| har| 9ob| jck| te9| qjr| x9g| qxg| 0ng| zj8| piq| h8m| nwe| 8jj| bu8| tjs| l8n| i99| blk| x9f| yzq| 7ai| yn7| siq| z7y| ibs| 7py| xq8| ibs| j8i| c8k| mcl| 8we| ez6| oqq| b6i| yai| 7to| ab7| eks| d7b| btk| 7ta| 7yq| xo5| uwf| jl6| ovd| n6m| jct| 6ab| ig6| scs| n6u| lvv| 7zp| 7ed|