知識庫 / Spring WebFlux RSS 訂閱

處理 Spring WebFlux 中的錯誤

Reactive,Spring WebFlux
HongKong
7
01:44 PM · Dec 06 ,2025

1. 概述

在本教程中,我們將探討在 Spring WebFlux 項目中處理錯誤的各種策略,同時會通過一個實際示例進行講解。

我們還會指出在某些情況下使用一種策略優於另一種策略,並在末尾提供指向完整源代碼的鏈接。

2. 設置示例

Maven 的設置與我們上一篇文章相同,它為 Spring WebFlux 提供了一個介紹。

對於我們的示例,我們將使用一個 RESTful 端點,它接受用户名作為查詢參數,並返回 “Hello username”作為結果。

首先,讓我們創建一個路由器函數,該函數將 /hello 請求路由到傳入的處理器的 handleRequest 方法:

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

接下來,我們將定義 handleWithGlobalErrorHandler() 方法,該方法調用 sayHello() 方法,並找到將其結果包含在 ServerResponse 響應體中的方法:

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

最後,sayHello() 方法是一個簡單的實用方法,它將“Hello” 和用户名連接起來:

private Mono<String> sayHello(ServerRequest request) {
    try {
        return Mono.just("Hello, " + request.queryParam("name").get());
    } catch (Exception e) {
        return Mono.error(e);
    }
}

只要請求中包含用户名,例如如果端點被調用為“/hello?username=Tonni”,該端點將始終正常工作。

但是,如果調用相同的端點且未指定用户名,例如“/hello”,則會拋出異常。

下面,我們將探討如何在 WebFlux 中重新組織代碼以處理此異常。

3. 以函數級方式處理錯誤

Mono 和 Flux API 內置了兩個關鍵運算符,用於以函數級方式處理錯誤。

下面我們簡要地瞭解一下它們及其用法。

3.1. 處理錯誤使用 onErrorReturn

我們可以使用 onErrorReturn() 在發生錯誤時返回一個靜態默認值:

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

我們在這裏返回一個靜態的“Hello Stranger”,當有問題的拼接函數 sayHello() 拋出異常時。

3.2. 使用 onErrorResume 處理錯誤

有三種方法可以使用 onErrorResume 處理錯誤:

  • 計算動態備用值
  • 使用備用方法執行替代路徑
  • 捕獲、包裝和重新拋出錯誤,例如作為自定義業務異常

讓我們看看如何計算一個值:

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

我們返回一個字符串,該字符串包含動態獲取的錯誤消息,並將其與字符串“Error”連接,當 sayHello() 拋出異常時生效。

接下來,讓我們 在發生錯誤時調用備用方法

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

我們在這裏在 sayHelloFallback() 方法被調用時,當 sayHello() 拋出異常時。

最終選項是使用 onErrorResume(),即 捕獲、包裝和重新拋出錯誤,例如作為 NameRequiredException

public Mono<ServerResponse> handleWithErrorResumeAndCustomException(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. 全局層面的錯誤處理

到目前為止,我們展示的所有示例都集中在功能級的錯誤處理上。需要注意的是,功能級的錯誤處理適用於基於標註的配置以及我們在本文中使用的 RouterFunction

當然,我們可以使用標註來處理我們的 REST API 中的異常,但對於本文,我們將選擇使用全局錯誤處理程序來處理 WebFlux 中的錯誤。要做到這一點,我們只需要採取兩個步驟:

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

我們的處理程序拋出的異常將自動轉換為 HTTP 狀態碼和 JSON 錯誤主體。

要自定義這些內容,我們可以簡單地 擴展 DefaultErrorAttributes 並覆蓋其 getErrorAttributes() 方法:

public class GlobalErrorAttributes extends DefaultErrorAttributes{
    
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, 
      ErrorAttributeOptions options) {
        Map<String, Object> map = super.getErrorAttributes(
          request, options);
        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, 
         ErrorAttributeOptions.defaults());

       return ServerResponse.status(HttpStatus.BAD_REQUEST)
         .contentType(MediaType.APPLICATION_JSON)
         .body(BodyInserters.fromValue(errorPropertiesMap));
    }
}

在此示例中,我們設置了全局錯誤處理器的優先級為-2。 這是為了 使其優先級高於 默認 Web 異常處理程序,該程序已註冊在 @Order(-1)

errorAttributes 對象將與我們在 Web 異常處理程序的構造函數中傳遞的完全相同。 理想情況下,它應該是我們自定義的錯誤屬性類。

然後我們明確説明,我們希望將所有錯誤處理請求路由到 renderErrorResponse() 方法。

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

這會產生一個包含錯誤詳細信息、HTTP 狀態和異常消息的 JSON 響應,用於機器客户端。 對於瀏覽器客户端,它具有“白標”錯誤處理程序,以 HTML 格式渲染相同的數據。 當然,這可以自定義。

5. 結論

在本文中,我們探討了處理 Spring WebFlux 項目中錯誤的各種策略,並指出了在某些情況下使用一種策略優於另一種策略的優勢。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.