【】在Spring WebFlux中處理錯誤


原文鏈接:http://www.baeldung.com/spring-webflux-errors

1. 概覽

在本教程中,我們通過一個實際的例子來看一下可用於處理Spring WebFlux項目中的錯誤的各種策略

我們還將指出在哪種情況下使用一種策略會比另外一種好,在本文最后將提供所有源碼的下載地址。

2. 配置實例

上一篇文章 previous article 中已經提到了maven的配置, 並對 Spring Webflux做了簡單的介紹。

在這個例子中,我們為一個 RESTful 端點加上一個名為 username 的查詢參數,並以“Hello username”作為結果返回。

First, let’s create a router function that routes the /hello request to a method named handleRequest in the passed-in handler:

首先,讓我們創建一個路由器函數,將/hello請求路由名為handleRequest的方法中:

@Bean
public RouterFunction<ServerResponse> routeRequest(Handler handler) {
    return RouterFunctions.route(RequestPredicates.GET("/hello")
      .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), 
        handler::handleRequest);
    }

接下來,我們將定義handleRequest()方法,該方法調用sayHello()方法並在ServerResponse主體中包含/返回其結果的方法:

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return
      //...
        sayHello(request)
      //...
}

最后,sayHello()是一個簡單的實用工具方法,它將“Hello”和 username 連接起來返回。

private Mono<String> sayHello(ServerRequest request) {
    //...
    return Mono.just("Hello, " + request.queryParam("name").get());
    //...
}

只要用 username 作為我們請求的一部分存在,例如使用“/hello?username=Tonni”訪問,我們的端點就可以正確運行。

然而,如果我們調用"/hello"的時候沒有使用 username 這個參數,它會拋出一個異常。

下面,我們將看看我們在何處如何重新組織我們的代碼才能在WebFlux中處理此異常。

3. 在函數級別處理錯誤

Mono和Flux API內置了兩個關鍵操作符,用於處理功能級別的錯誤。

讓我們簡要地探討它們及其用法。

3.1. 使用 onErrorReturn

當出現錯誤時,我們可以使用 onErrorReturn()來返回一個靜態的默認值。

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .onErrorReturn("Hello Stranger")
      .flatMap(s -> ServerResponse.ok()
      .contentType(MediaType.TEXT_PLAIN)
      .syncBody(s));
}

sayHello()拋出異常時,函數就會默認返回"Hello Stranger"。

3.2. 使用onErrorResume

使用onErrorResume處理錯誤有三種方式:

  • 計算動態返回值
  • 使用fallback方法 跳轉到備份路徑
  • 捕獲,包裝和重新拋出錯誤,例如 作為自定義業務異常

讓我們看看怎么楊計算一個值:

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .flatMap(s -> ServerResponse.ok()
      .contentType(MediaType.TEXT_PLAIN)
          .syncBody(s))
        .onErrorResume(e -> Mono.just("Error " + e.getMessage())
          .flatMap(s -> ServerResponse.ok()
            .contentType(MediaType.TEXT_PLAIN)
            .syncBody(s)));
}

在這里,每當sayHello()拋出異常時,我們將返回一個字符串,該字符串由附加到字符串“Error”的動態獲取的錯誤消息組成。

接下來,當錯誤發生時我們調用 fallback 方法:

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .flatMap(s -> ServerResponse.ok()
      .contentType(MediaType.TEXT_PLAIN)
      .syncBody(s))
      .onErrorResume(e -> sayHelloFallback()
      .flatMap(s ->; ServerResponse.ok()
      .contentType(MediaType.TEXT_PLAIN)
      .syncBody(s)));
}

在這里,只要sayHello()拋出異常,我們就會調用替代方法sayHelloFallback()。

使用onErrorResume()的最后一個選項是捕獲,包裝和重新拋出錯誤,例如 作為NameRequiredException

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return ServerResponse.ok()
      .body(sayHello(request)
      .onErrorResume(e -> Mono.error(new NameRequiredException(
        HttpStatus.BAD_REQUEST, 
        "username is required", e))), String.class);
}

在這里,只要sayHello()拋出異常,我們就會拋出一個自定義異常,並帶有消息:"username is required"。

4. 全局級別的錯誤處理

到目前為止,我們提供的所有示例都在函數級別上處理了錯誤處理。

但是,我們可以選擇在全局范圍內處理我們的WebFlux錯誤。 要做到這一點,我們只需要采取兩個步驟:

  • 自定義全局錯誤響應屬性
  • 實現全局錯誤處理程序

我們的處理程序拋出的異常將被自動轉換為HTTP狀態和JSON錯誤正文。 要自定義這些,我們可以簡單地擴展DefaultErrorAttributes類並覆蓋其getErrorAttributes()方法:

public class GlobalErrorAttributes extends DefaultErrorAttributes{
     
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, 
      boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(
          request, includeStackTrace);
        map.put("status", HttpStatus.BAD_REQUEST);
        map.put("message", "username is required");
        return map;
    }
 
}

在這里,我們希望狀態:BAD_REQUEST和消息:"username is required"在發生異常時作為錯誤屬性的一部分返回。

接下來,讓我們實現全局錯誤處理程序。 為此,Spring提供了一個方便的AbstractErrorWebExceptionHandler類,供我們在處理全局錯誤時進行擴展和實現:

@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends
    AbstractErrorWebExceptionHandler {
 
    // constructors
 
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(
      ErrorAttributes errorAttributes) {
 
        return RouterFunctions.route(
          RequestPredicates.all(), this::renderErrorResponse);
    }
 
    private Mono<ServerResponse> renderErrorResponse(
       ServerRequest request) {
 
       Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);
 
       return ServerResponse.status(HttpStatus.BAD_REQUEST)
         .contentType(MediaType.APPLICATION_JSON_UTF8)
         .body(BodyInserters.fromObject(errorPropertiesMap));
    }
}

在這個例子中,我們將全局錯誤處理程序的順序設置為-2。 這是為了給它一個比在@Order(-1)注冊的DefaultErrorWebExceptionHandler更高的優先級。

errorAttributes對象將是我們在Web異常處理程序的構造函數中傳遞的副本的精確副本。 理想情況下,這應該是我們自定義的Error Attributes類。

然后,我們清楚地說明我們想要將所有錯誤處理請求路由到renderErrorResponse()方法。

最后,我們獲取錯誤屬性並將它們插入服務器響應主體中。

然后,它會生成一個JSON響應,其中包含錯誤,HTTP狀態和計算機客戶端的異常消息的詳細信息。 對於瀏覽器客戶端,它有一個“whitelabel”錯誤處理程序,它以HTML格式呈現相同的數據。 當然,這可以是定制的。

5. 結尾

在本文中,我們研究了可用於處理Spring WebFlux項目中的錯誤的各種策略,並指出了使用一種策略而不是另一種策略的優勢。

正如所承諾的那樣,本文附帶的完整源代碼可以在 GitHub獲得。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM