1概要

このチュートリアルでは、実用的な例を見ながら、Spring WebFluxプロジェクトでエラーを処理するために利用できるさまざまな戦略について調べます。

また、ある戦略を他の戦略よりも使用し、最後に完全なソースコードへのリンクを提供することが有利な場合があることを指摘します。


2例の設定

Mavenの設定は、Spring Webfluxの紹介を提供しているhttps://www.baeldung.com/spring-webflux[以前の記事]と同じです。

この例では、


クエリパラメータとしてユーザー名を受け取り、結果として “Hello username”

を返す

** RESTfulエンドポイントを使用します。

まず、

/hello

リクエストを、渡されたハンドラ内の

handleRequest

というメソッドにルーティングするルーター関数を作成しましょう。

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

次に、

sayHello()

メソッドを呼び出し、その結果を

ServerResponse

bodyに含める/返す方法を見つける

handleRequest()

メソッドを定義します。

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

最後に、

sayHello()

メソッドは、「Hello」

String

とユーザー名を連結した簡単なユーティリティメソッドです。

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

ユーザー名がリクエストの一部として存在する限り。エンドポイントが

“/hello?username = Tonni

“と呼ばれる場合、このエンドポイントは常に正しく機能します。

ただし、ユーザー名を指定せずに同じエンドポイントを呼び出す場合は** 。

“/hello”、

これは例外をスローします。

以下では、WebFluxでこの例外を処理するためにコードをどこでどのように再編成できるかを見ていきます。


3機能レベルでのエラー処理

機能レベルでエラーを処理するために、

Mono

および

Flux

APIに組み込まれた2つの主要な演算子があります。

それらとそれらの使い方を簡単に調べてみましょう。

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

を使用できる3つの方法があります。

  • 動的フォールバック値を計算する

  • 代替方法で代替パスを実行する

  • エラーをキャッチ、ラップ、そして再スローします。カスタムビジネス例外として

値を計算する方法を見てみましょう。

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」という文字列に追加したStringを返します。

次に、エラーが発生したときにフォールバックメソッドを呼び出します。

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エラーを処理することを選ぶことができます。** これを行うには、2つのステップを踏む必要があるだけです:

  • グローバルエラーレスポンス属性をカスタマイズする

  • グローバルエラーハンドラを実装する

私たちのハンドラが投げる例外は自動的に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;
    }

}

ここでは、例外が発生したときにstatus:

BAD

REQUEST

およびメッセージ:“

username is required__”がエラー属性の一部として返されるようにします。

次に、

Global Error Handler

を実装しましょう。このために、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()

メソッドにルーティングすることを明確に述べています。

最後に、エラー属性を取得してそれらをサーバー応答本体内に挿入します。

これにより、エラーの詳細、HTTPステータス、およびマシンクライアントの例外メッセージを含むJSON応答が生成されます。ブラウザクライアントの場合は、同じデータをHTML形式で表示する「ホワイトラベル」エラーハンドラがあります。これはもちろんカスタマイズできます。


5結論

この記事では、Spring WebFluxプロジェクトでエラーを処理するために使用可能なさまざまな戦略を検討し、ある戦略を他の戦略よりも使用すると有利な場合があることを指摘しました。

約束されているように、この記事に付随する完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-5-reactive[GitHubで利用可能]です。