web-dev-qa-db-fra.com

Obtenir un message d'erreur de réponse API à l'aide de Web Client Mono dans Spring Boot

J'utilise webflux Mono (dans Spring boot 5) pour consommer une API externe. Je peux obtenir des données correctement lorsque le code d'état de réponse de l'API est 200, mais lorsque l'API renvoie une erreur, je ne suis pas en mesure de récupérer le message d'erreur de l'API. Le gestionnaire d'erreurs du client Web Spring affiche toujours le message sous la forme

ClientResponse has erroneous status code: 500 Internal Server Error, mais lorsque j'utilise PostMan, l'API renvoie cette réponse JSON avec le code d'état 500.

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

Ma demande en utilisant WebClient est la suivante

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

Ma question est, comment puis-je accéder à la réponse JSON lorsque l'API renvoie une erreur avec un code d'état de 500?

10
Mohale

Regardez .onErrorMap(), qui vous donne l'exception à regarder. Puisque vous pourriez également avoir besoin du corps () de l'échange () pour regarder, n'utilisez pas la récupération, mais

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

Si vous souhaitez récupérer les détails de l'erreur:

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

avec

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

et avec la classe ErrorDetails mappant le corps de l'erreur

Variante par demande:

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

Tout comme @Frischling l'a suggéré, j'ai changé ma demande pour qu'elle se présente comme suit

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

J'ai également noté qu'il existe quelques codes d'état de 1xx à 5xx, ce qui facilitera la gestion des erreurs pour différents cas

5
Mohale