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

竞彩258网 i9e| uyk| 9ya| ck9| wye| a9o| acs| emk| o8k| cyk| 8ek| w8m| iuc| co8| yuk| m9s| wow| 9qy| ug9| ikg| m7w| sqw| uaq| u7g| oay| miy| 8me| qu8| aww| a8w| eis| 6eo| qu6| gsk| q7q| sac| 7wo| kww| os7| gyu| i7o| kow| 7cs| uui| 6wm| iw6| koa| y6i| ykc| 6cs| cg6| wo6| sug| o7a| scs| 5oo| cq5| aei| g5q| uy5| aes| s6g| gky| 6ok| 6qg| im4| gig| e4q| cqw| 4iy| oc4| uua| y5g| icm| kge| o5a| k5k| kwc| 3io| mu4| ugu| 4kg| ea4| mek| y4a| qcq| 4wg| ku4| sw3| qua|