web-dev-qa-db-ja.com

Spring Webflux:Webclient:エラー時にボディを取得

私はこのように春のwebfluxからwebclientを使用しています:

WebClient.create()
            .post()
            .uri(url)
            .syncBody(body)
            .accept(MediaType.APPLICATION_JSON)
            .headers(headers)
            .exchange()
            .flatMap(clientResponse -> clientResponse.bodyToMono(tClass));

順調です。次に、呼び出しているWebサービスからのエラー(Ex 500内部エラー)を処理します。通常、私は「ストリーム」にdoOnErrorを追加し、ステータスコードをテストするためにThrowableをisuします。

しかし、私の問題は、私が使用したいメッセージを提供しているので、ウェブサービスによって提供されるボディを取得したいということです。

何が起こってもflatMapを実行し、ステータスコードをテストして本体を非シリアル化するかどうかを確認しています。

10
adrien le roy

これを書いている時点では、5xxエラーが原因で基になるNettyレイヤーから例外が発生することはありません。 https://github.com/spring-projects/spring-framework/commit/b0ab84657b712aac59951420f4e9d696c3d84ba2 を参照してください

4
Arjen Poutsma

私はこのようなことをします:

Mono<ClientResponse> responseMono = requestSpec.exchange()
            .doOnNext(response -> {
                HttpStatus httpStatus = response.statusCode();
                if (httpStatus.is4xxClientError() || httpStatus.is5xxServerError()) {
                    throw new WebClientException(
                            "ClientResponse has erroneous status code: " + httpStatus.value() +
                                    " " + httpStatus.getReasonPhrase());
                }
            });

その後:

responseMono.subscribe(v -> { }, ex -> processError(ex));
5
Artem Bilan

あなたもこれを行うことができます

return webClient.getWebClient()
 .post()
 .uri("/api/Card")
 .body(BodyInserters.fromObject(cardObject))
 .exchange()
 .flatMap(clientResponse -> {
     if (clientResponse.statusCode().is5xxServerError()) {
        clientResponse.body((clientHttpResponse, context) -> {
           return clientHttpResponse.getBody();
        });
     return clientResponse.bodyToMono(String.class);
   }
   else
     return clientResponse.bodyToMono(String.class);
});

その他の例については、この記事をお読みください link 、エラー処理で同様の問題が発生したときに役立つことがわかりました

4
Mohale

最終的に何が起こっているのか理解しました。デフォルトでは、Nettyのhttpclient(HttpClientRequest)はサーバーエラー(応答5XX)で失敗し、クライアントエラー(4XX)では失敗しないように構成されています。

ここで行ったのは、AbstractClientHttpRequestとClientHttpConnectorを拡張して、httpclientが希望どおりに動作するように構成し、WebClientを呼び出すときにカスタムClientHttpConnectorを使用することです。

 WebClient.builder().clientConnector(new CommonsReactorClientHttpConnector()).build();
3
adrien le roy

私は同じような状況に直面したばかりで、4xx/5xx応答を受け取っていても、webClientが例外をスローしないことがわかりました。私の場合、最初にwebclientを使用して応答を取得するための呼び出しを行い、それが2xx応答を返している場合は、応答からデータを抽出し、それを使用して2番目の呼び出しを行います。最初の呼び出しが2xx以外の応答を受け取っている場合は、例外をスローします。それは例外をスローしていないので、最初の呼び出しが失敗し、2番目の呼び出しが引き続き実行されるためです。だから私がしたことは

return webClient.post().uri("URI")
    .header(HttpHeaders.CONTENT_TYPE, "XXXX")
    .header(HttpHeaders.ACCEPT, "XXXX")
    .header(HttpHeaders.AUTHORIZATION, "XXXX")
    .body(BodyInserters.fromObject(BODY))
    .exchange()
    .doOnSuccess(response -> {
        HttpStatus statusCode = response.statusCode();
        if (statusCode.is4xxClientError()) {
            throw new Exception(statusCode.toString());
        }
        if (statusCode.is5xxServerError()) {
            throw new Exception(statusCode.toString());
        }
    )
    .flatMap(response -> response.bodyToMono(ANY.class))
    .map(response -> response.getSomething())
    .flatMap(something -> callsSecondEndpoint(something));
}
1
Fai Ho