简体   繁体   中英

How to do Exception Handling for WebFlux using Springboot?

I have 3 micro-service applications. I am trying to do 2 get call async using webclient from reactive package and then combine them whenever I get a response.

Sample code for that: (referred from - https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-synchronous )

Mono<Person> personMono = client.get().uri("/person/{id}", personId)
        .retrieve().bodyToMono(Person.class);

Mono<List<Hobby>> hobbiesMono = client.get().uri("/person/{id}/hobbies", personId)
        .retrieve().bodyToFlux(Hobby.class).collectList();

Map<String, Object> data = Mono.zip(personMono, hobbiesMono, (person, hobbies) -> {
            Map<String, String> map = new LinkedHashMap<>();
            map.put("person", personName);
            map.put("hobbies", hobbies);
            return map;
        })
        .block();

My question is how can I add exception handling to the get calls?

How do I check if I got a 404 or 204 or something else?

I have tried:

  1. Adding.onStatus() to the GET calls
    .onStatus(HttpStatus::is4xxClientError, clientResponse ->
             Mono.error(new Data4xxException(String.format(
                "Could not GET data with id: %s from another app, due to error: 
                 %s", key, clientResponse))))
    .onStatus(HttpStatus::is5xxServerError, clientResponse ->
          Mono.error(new Data5xxException(
              String.format("For Data %s, Error Occurred: %s", key, clientResponse))))
  1. Adding exceptionhandlers - but I exactly dont have a controller so this does not seem to be working.
@ExceptionHandler(WebClientException.class)
    public Exception handlerWebClientException(WebClientException webClientException) {
        return new Data4xxException("Testing", webClientException);
    }
  1. Added a class with ControllerAdvice and ExceptionHandler within it
@ControllerAdvice
public class WebFluxExceptionHandler {

    @ExceptionHandler(WebClientException.class)
    public Exception handlerWebClientException(WebClientException webClientException) {
        return new Data4xxException("Testing", webClientException);
    }
}

But I don't see them printed in the spring-boot logs.

The Mono.zip.block() method just returns null and does not actually throw any exception.

How do I get the zip method to throw the exception and not return null?

The way to do it is using onErrorMap in the following way:

Mono<Person> personMono = client.get()
.uri("/person/{id}", personId)
.retrieve()
.bodyToMono(Person.class)
.onErrorMap((Throwable error) -> error);

onErrorMap will make the Mono to actually throw an error when Zip blocks, terminating zip and letting spring or any other class that you want to handle the exception.

You arn't very clear when you ask

"How do I get the zip method to throw the exception and not return null?"

In webflux you commonly dont throw exceptions, you propagate exceptions and then handle them. Why? because we are dealing with streams of data, if you throw an exception, the stream ends, client disconnects and the event chain stops.

We still want to maintain the stream of data and handle bad data as it flows through.

You can handle errors using the doOnError method.

.onStatus(HttpStatus::is4xxClientError, clientResponse ->
         Mono.error(new Data4xxException(String.format(
            "Could not GET data with id: %s from another app, due to error: 
             %s", key, clientResponse))))

Mono.zip( .. ).doOnError( //Handle your error, log or whatever )

If you want to do something more specific you'll have to update your question with how you want your errors to be handled.

The retrieve() method in WebClient throws a WebClientResponseException whenever a response with status code 4xx or 5xx is received.

Unlike the retrieve() method, the exchange() method does not throw exceptions in the case of 4xx or 5xx responses. You need to check the status codes yourself and handle them in the way you want to.

   Mono<Object> result = webClient.get().uri(URL).exchange().log().flatMap(entity -> {
        HttpStatus statusCode = entity.statusCode();
        if (statusCode.is4xxClientError() || statusCode.is5xxServerError())
        {
            return Mono.error(new Exception(statusCode.toString()));
        }
        return Mono.just(entity);
    }).flatMap(clientResponse -> clientResponse.bodyToMono(JSONObject.class))

Reference: https://www.callicoder.com/spring-5-reactive-webclient-webtestclient-examples/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM