I am trying to replace the existing client code with RestTemplate
with a WebClient
. For that reason, most of the calls need to be blocking, so that the main portion of the application does not need to change. When it comes to error handling this poses a bit of a problem. There are several cases that have to be covered:
- In a successful case the response contains a JSON object of type A
- In an error case (HTTP status 4xx or 5xx) the response may contain a JSON object of type B
- On certain requests with response status 404 I need to return an empty
List
matching the type of a successful response
In order to produce the correct error (Exception
) the error response needs to be considered. So far I am unable to get my hands on the error body.
I am using this RestController
method to produce the error response:
@GetMapping("/error/404")
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity error404() {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse());
}
With this response object:
public class ErrorResponse {
private String message = "Error message";
public String getMessage() {
return message;
}
}
The WebClient
is defined as follows:
WebClient.builder()
.baseUrl("http://localhost:8081")
.clientConnector(connector)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
With the connector being of type CloseableHttpAsyncClient
(Apache Http client5).
From my test application I make the call like this:
public String get(int httpStatus) {
try {
return webClient.get()
.uri("/error/" + httpStatus)
.retrieve()
.onStatus(HttpStatus::isError, clientResponse -> {
clientResponse.bodyToMono(String.class).flatMap(responseBody -> {
log.error("Body from within flatMap within onStatus: {}", responseBody);
return Mono.just(responseBody);
});
return Mono.error(new RuntimeException("Resolved!"));
})
.bodyToMono(String.class)
.flatMap(clientResponse -> {
log.warn("Body from within flatMap: {}", clientResponse);
return Mono.just(clientResponse);
})
.block();
} catch (Exception ex) {
log.error("Caught Error: ", ex);
return ex.getMessage();
}
}
What I get is the RuntimeException
from the onStatus
return and of course the caught exception in the end.
I am missing the processing from the bodyToMono
from within the onStatus
. My suspicion is that this is not executed due to the blocking nature, as the response body is dealt with the bodyToMono
after the onStatus
.
When commenting out the onStatus
I would expect that we log the warning in the flatMap
, which does not happen either.
In the end I would like to define the handling of errors as a filter
so that the code does not need to be repeated on every call, but I need to get the error response body, so that the exception can be populated with the correct data.
How can I retrieve the error response in a synchronous WebClient
call?
This question is similar to Spring Webflux : Webclient : Get body on error, which has no accepted answer and some of the suggested approaches use methods that are no deprecated.