Spring Boot 2.x 系列教程:WebFlux REST API 全局異常處理 Error Handling

本文內容java

  • 爲何要全局異常處理?
  • WebFlux REST 全局異常處理實戰
  • 小結
摘錄:只有不斷培養好習慣,同時不斷打破壞習慣,咱們的行爲舉止纔可以自始至終都是正確的。

1、爲何要全局異常處理?

先後端分離開發,通常提供 REST API,正常返回會有響應體,異常狀況下會有對應的錯誤碼響應。react

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

全局異常處理的好處:github

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

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

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

2.1 工程信息

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

工程結構:spring

├── 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() 方法,就能夠啓動該應用。後端

具體實現類的關係圖以下:瀏覽器

圖片上傳失敗,見原文 https://www.bysocket.com/archives/2272springboot

2.2 CityRouter 路由器類

城市路由器代碼以下:服務器

@Configuration
public class CityRouter {
    
    @Bean
    public RouterFunction<ServerResponse> 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> helloCity(ServerRequest request) {
        return ServerResponse.ok().body(sayHelloCity(request), String.class);
    }
    
    private Mono<String> sayHelloCity(ServerRequest request) {
        Optional<String> 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 操做符。能夠成功發佈元素或者錯誤。用 Mono 做爲返回對象,是由於返回包含了一個 ServerResponse 對象,而不是多個元素。

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

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

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

2.4 GlobalError 處理類

如圖:

圖片上傳失敗,見原文 https://www.bysocket.com/archives/2272

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> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        Map<String, Object> 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> getRoutingFunction(final ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {

        final Map<String, Object> 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<ServerResponse> 服務響應對象
  • 將 GlobalErrorAttributes 的全局異常屬性值 map,設置到新的 ServerResponse 便可。

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

2.5 運行驗證

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

http://localhost:8080/hello

異常界面以下:

圖片上傳失敗,見原文 https://www.bysocket.com/archives/2272

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

改下 URL ,訪問以下:

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

正常界面以下:

圖片上傳失敗,見原文 https://www.bysocket.com/archives/2272

3、小結

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

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

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

代碼示例

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

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

參考資料

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

相關文章
相關標籤/搜索