Java – Catch exceptions in Spring Web Flux

Catch exceptions in Spring Web Flux… here is a solution to the problem.

Catch exceptions in Spring Web Flux

I have the following class for calling time-consuming operations through a web service:

@Service
public class InferenceService {

@Autowired
    private final WebClient inferenceClient;

public ByteArrayResource invoke(FileSystemResource resource) throws HttpException {
        Mono<ByteArrayResource> mono = inferenceClient
                .post()
                .uri(INFERENCE_URI)
                .contentType(MediaType.valueOf("application/zip"))
                .body(BodyInserters.fromResource(resource))
                .accept()
                .retrieve()
                .bodyToMono(ByteArrayResource.class);

ByteArrayResource zip = mono.block();
        return zip;
    }
}

If an exception occurs during a Web service call, such as UnknownHostException, it is wrapped in a ReactiveException. I want my class to catch the original exception and rethrow it as an HttpException so that I can log it, handled by the Spring exception handler, returning a 500 status from my Controller, rather than having trouble reading a stack trace with a suppressed exception in my logs. I wrote something like this:

@Service
public class InferenceService {

@Autowired
    private final WebClient inferenceClient;

public ByteArrayResource invoke(FileSystemResource resource) throws HttpException {
        Mono<ByteArrayResource> mono = inferenceClient
                .post()
                .uri(INFERENCE_URI)
                .contentType(MediaType.valueOf("application/zip"))
                .body(BodyInserters.fromResource(resource))
                .accept()
                .retrieve()
                .onStatus(HttpStatus::is5xxServerError, this::getStatus500Error)
                .bodyToMono(ByteArrayResource.class);

try {
            ByteArrayResource zip = mono.block();
            return zip;
        } catch (RuntimeException e) {
            throw extractFromReactiveException(e);
        }
    }

private Mono<? extends Throwable> getStatus500Error(ClientResponse clientResponse) {
        return Mono.error(new InternalServerError(getStatusErrorMsg(clientResponse)));
    }

private HttpException extractFromReactiveException(RuntimeException e) {
        if (e.getCause() instanceof HttpException) {
            return (HttpException) e.getCause();
        } else {
            return new InternalServerError(e);
        }
    }
}

But I felt that either there was a better way, or what I was trying to achieve was wrong. What is the right way?

Solution

You should not use blocks. The purpose of Reactor is to handle everything in the chain. So your method should return a Mono instead of the object itself.

If you want to change the exception, you should use .onErrorMap(throwable -> extractFromReactiveException(throwable));
After bodyToMono.

and make the extract method return HttpException instead of Mono.

Here is the documentation for finding the onErrorMap method and its overloads.
https://projectreactor.io/docs/core/release/api/

If you still have any questions, please feel free to ask!

Related Problems and Solutions