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());
}
私は実際にその振る舞いに驚いています、それはバグですか?
最初のサンプルはMono
(つまり最大で1つの値)を使用しているため、Mono<ServerResponse>
でうまく機能します-値はメモリ内で非同期に解決され、結果に応じて異なる応答を返しますビジネス例外を手動で処理します。
Flux
(つまり、0..N値)の場合、いつでもエラーが発生する可能性があります。
この場合、collectList
演算子を使用してFlux<String>
をMono<List<String>>
に変換できますが、大きな警告があります。すべての要素がメモリにバッファリングされます。コントローラ/クライアントがストリーミングデータに依存しているかどうかのデータストリームが重要である場合、これはここでは最良の選択ではありません。
この問題に対するより良い解決策がないのではないかと思います。その理由は次のとおりです。Flux
中にエラーが発生する可能性があるため、HTTPのステータスと応答を変更できる保証はありません。すでにネットワーク上でフラッシュされています。これは、SpringMVCを使用してInputStream
またはResource
を返す場合にすでに当てはまります。
Spring Bootエラー処理機能は、エラーページを書き込んでHTTPステータスを変更しようとしますが(ErrorWebExceptionHandler
およびクラスの実装を参照)、応答がすでにコミットされている場合は、エラー情報をログに記録し、HTTPを通知します。ステータスが間違っていた可能性があります。
この問題を解決する別の方法を見つけて、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エラーコードを返します。