web-dev-qa-db-ja.com

Spring BootでWebクライアントモノを使用してAPI応答エラーメッセージを取得する

Webflux Mono(Spring boot 5)を使用して外部APIを使用しています。 API応答ステータスコードが200の場合、データをうまく取得できますが、APIがエラーを返した場合、APIからエラーメッセージを取得できません。 Spring webclientエラーハンドラは常にメッセージを次のように表示します

ClientResponse has erroneous status code: 500 Internal Server Errorが、PostManを使用すると、APIはこのJSON応答をステータスコード500で返します。

{
 "error": {
    "statusCode": 500,
    "name": "Error",
    "message":"Failed to add object with ID:900 as the object exists",
    "stack":"some long message"
   }
}

WebClientを使用した私のリクエストは次のとおりです

webClient.getWebClient()
            .post()
            .uri("/api/Card")
            .body(BodyInserters.fromObject(cardObject))
            .retrieve()
            .bodyToMono(String.class)
            .doOnSuccess( args -> {
                System.out.println(args.toString());
            })
            .doOnError( e ->{
                e.printStackTrace();
                System.out.println("Some Error Happend :"+e);
            });

私の質問は、APIがステータスコード500のエラーを返したときにJSON応答にアクセスするにはどうすればよいですか?

10
Mohale

.onErrorMap()を見てください。これは、見るべき例外を与えてくれます。調べるにはexchange()のbody()も必要になる可能性があるため、retrieveを使用しないでください。ただし、

.exchange().flatMap((ClientResponse) response -> ....);
3
Frischling

エラーの詳細を取得する場合:

WebClient webClient = WebClient.builder()
    .filter(ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
        if (clientResponse.statusCode().isError()) {
            return clientResponse.bodyToMono(ErrorDetails.class)
                    .flatMap(errorDetails -> Mono.error(new CustomClientException(clientResponse.statusCode(), errorDetails)));
        }
        return Mono.just(clientResponse);
    }))
    .build();

class CustomClientException extends WebClientException {
    private final HttpStatus status;
    private final ErrorDetails details;

    CustomClientException(HttpStatus status, ErrorDetails details) {
        super(status.getReasonPhrase());
        this.status = status;
        this.details = details;
    }

    public HttpStatus getStatus() {
        return status;
    }

    public ErrorDetails getDetails() {
        return details;
    }
}

エラー本体をマッピングするErrorDetailsクラスを使用

リクエストごとのバリアント:

webClient.get()
    .exchange()
    .map(clientResponse -> {
        if (clientResponse.statusCode().isError()) {
            return clientResponse.bodyToMono(ErrorDetails.class)
                    .flatMap(errorDetails -> Mono.error(new CustomClientException(clientResponse.statusCode(), errorDetails)));
        }
        return clientResponse;
    })
6
darrachequesne

@Frischlingが示唆したように、リクエストを次のように変更しました

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);
});

また、1xxから5xxのステータスコードがいくつかあることに注意しました。これにより、さまざまなケースでエラー処理が容易になります。

5
Mohale