Search code examples
javaexceptionspring-webfluxjava-11

Deal with java.lang.Errors in Spring's WebClient requests


I'm trying to retrieve data from a REST service using Spring's WebClient and trying to throw the 4XX and 5XX in a Error instead of instead of a RuntimeException.

return webclient. 
    .get()
    .uri(myURI)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .onStatus(HttpStatus::isError, handleError()
    .bodyToMono(String.class)
    .block();
private static Function<ClientResponse, Mono<? extends Throwable>> handleError() {
    return response -> response.bodyToMono(String.class).map(CustomError::new);
}

I'm trying to get this Error in a test but received a ReactiveException instead.

org.opentest4j.AssertionFailedError: Unexpected exception type thrown, 
Expected :class com.example.CustomError
Actual   :class reactor.core.Exceptions$ReactiveException

When I switch the extesion of CustomError from Error to RuntimeException the test pass.

Since the onStatus expcept a Function<ClientResponse, Mono<? extends Throwable>> I expected to be able to throw an Error in this call.

I'm working on a test library and a initial request is strictly necessary and without a successfully request the test must fail. A Error will be prevent anyone to catch any problem related with the REST request in their own tests.


Solution

  • You might want to try to return a Mono.error(new CustomError(response)); in your handleError() method. This way you've let the reactive pipeline know an error has occured and what to throw afterwards. Right now you just map to an Error object, which is not handled the same way as RuntimeExceptions.

    I would also say that it is probably better to stick to Exceptions instead of Errors. Errors tend to be unrecoverable situations for the program to run (OutOfMemoryError for example), whereas Exceptions are just exceptional cases that could be reacted to potentially (retrying for a 503 status code, or reauthenticating when you receive a 401 status code back).

    Example implementation

        public Mono<? extends Throwable> handleError(ClientResponse clientResponse) {
            String message = String.format("Service error: %s",
                    clientResponse.statusCode().getReasonPhrase());
            return Mono.error(new CustomError(message));
        }
    

    This is also a good answer: Correct way of throwing exceptions with Reactor