web-dev-qa-db-ja.com

webfluxで例外を処理して適切なHTTPコードを返す

WebFluxの機能エンドポイントを使用しています。 onErrorResumeを使用して、サービスレイヤーから送信された例外をHTTPエラーコードに変換します。

public Mono<String> serviceReturningMonoError() {
    return Mono.error(new RuntimeException("error"));  
}

public Mono<ServerResponse> handler(ServerRequest request) {
    return serviceReturningMonoError().flatMap(e -> ok().syncBody(e))
                            .onErrorResume( e -> badRequest(e.getMessage()));
}

サービスがMonoを返すとすぐにうまく機能します。フラックスを返すサービスの場合、どうすればよいですか?

public Flux<String> serviceReturningFluxError() {
    return Flux.error(new RuntimeException("error"));  
}

public Mono<ServerResponse> handler(ServerRequest request) {
    ???
}

編集

以下のアプローチを試しましたが、残念ながら機能しません。 Flux.errorはonErrorResumeによって処理されず、フレームワークに伝播されます。 http応答のシリアル化中に例外がボックス化されていない場合、Spring Boot Exception管理はそれをキャッチし、500に変換します。

public Mono<ServerResponse> myHandler(ServerRequest request) {
      return ok().contentType(APPLICATION_JSON).body( serviceReturningFluxError(), String.class)
             .onErrorResume( exception -> badRequest().build());
}

私は実際にその振る舞いに驚いています、それはバグですか?

6
Nicolas Barbé

最初のサンプルはMono(つまり最大で1つの値)を使用しているため、Mono<ServerResponse>でうまく機能します-値はメモリ内で非同期に解決され、結果に応じて異なる応答を返しますビジネス例外を手動で処理します。

Flux(つまり、0..N値)の場合、いつでもエラーが発生する可能性があります。

この場合、collectList演算子を使用してFlux<String>Mono<List<String>>に変換できますが、大きな警告があります。すべての要素がメモリにバッファリングされます。コントローラ/クライアントがストリーミングデータに依存しているかどうかのデータストリームが重要である場合、これはここでは最良の選択ではありません。

この問題に対するより良い解決策がないのではないかと思います。その理由は次のとおりです。Flux中にエラーが発生する可能性があるため、HTTPのステータスと応答を変更できる保証はありません。すでにネットワーク上でフラッシュされています。これは、SpringMVCを使用してInputStreamまたはResourceを返す場合にすでに当てはまります。

Spring Bootエラー処理機能は、エラーページを書き込んでHTTPステータスを変更しようとしますが(ErrorWebExceptionHandlerおよびクラスの実装を参照)、応答がすでにコミットされている場合は、エラー情報をログに記録し、HTTPを通知します。ステータスが間違っていた可能性があります。

2
Brian Clozel

この問題を解決する別の方法を見つけて、bodyメソッド内で例外をキャッチし、それをResponseStatusExceptionにマッピングしました。

public Mono<ServerResponse> myHandler(ServerRequest request) {
    return ok().contentType(MediaType.APPLICATION_JSON)
            .body( serviceReturningFluxError()
                    .onErrorMap(RuntimeException.class, e -> new ResponseStatusException( BAD_REQUEST, e.getMessage())), String.class);
}

このアプローチでは、Springは応答を適切に処理し、予期されるHTTPエラーコードを返します。

2
Nicolas Barbé