Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get API response error message using Web Client Mono in Spring Boot

I am using webflux Mono (in Spring boot 5) to consume an external API. I am able to get data well when the API response status code is 200, but when the API returns an error I am not able to retrieve the error message from the API. Spring webclient error handler always display the message as

ClientResponse has erroneous status code: 500 Internal Server Error, but when I use PostMan the API returns this JSON response with status code 500.

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

My request using WebClient is as follows

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

My question is, how can I get access to the JSON response when the API returns an Error with status code of 500?

like image 247
Mohale Avatar asked Mar 26 '18 06:03

Mohale


People also ask

How do I handle WebClient exceptions?

While Initialising WebClient As mentioned in the code block, whenever a 5XX/4XX Error occurs, we can throw a user defined exception, and then execute error handling logic based on those user defined exceptions. Once this error Handler is defined, we can add it in the WebClient Initialisation.

What exception does WebClient throw?

As you can see, ServiceException includes both a message and a status code. That's the exception that gets included in the Mono publisher. It's also the exception that gets thrown when the WebClient encounters a 405 HTTP status code.

How do you throw a spring error in WebFlux?

There are three ways that we can use onErrorResume to handle errors: Compute a dynamic fallback value. Execute an alternative path with a fallback method. Catch, wrap and re-throw an error, e.g., as a custom business exception.


2 Answers

If you want to retrieve the error details:

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

with

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

and with the ErrorDetails class mapping the error body

Per-request variant:

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;
    })
like image 165
darrachequesne Avatar answered Oct 06 '22 09:10

darrachequesne


Just as @Frischling suggested, I changed my request to look as follows

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

I also noted that there's a couple of status codes from 1xx to 5xx, which is going to make my error handling easier for different cases

like image 30
Mohale Avatar answered Oct 06 '22 09:10

Mohale