Issue
In a Spring Boot application, I'm using WebClient
to invoke a POST request to a remote application. The method currently looks like this:
// Class A
public void sendNotification(String notification) {
final WebClient webClient = WebClient.builder()
.defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.build();
webClient.post()
.uri("http://localhost:9000/api")
.body(BodyInserters.fromValue(notification))
.retrieve()
.onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
.toBodilessEntity()
.block();
log.info("Notification delivered successfully");
}
// Class B
public void someOtherMethod() {
sendNotification("test");
}
The use case is: A method in another class calls sendNotification
and should handle any error, i.e. any non 2xx status or if the request couldn't even be sent.
But I'm struggling with the concept of handling errors in the WebClient
. As far as I understood, the following line would catch any HTTP status other than 2xx/3xx and then return a Mono.error
with the NotificationException
(a custom exception extending Exception
).
onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
But how could someOtherMethod()
handle this error scenario? How could it process this Mono.error
? Or how does it actually catch the NotificationException
if sendNotification
doesn't even throw it in the signature?
Solution
Well, there are many ways to handle errors, it really depends on what you want to do in case of an error.
In your current setup, the solution is straightforward: first, NotificationException
should extend RuntimeException
, thus, in case of an HTTP error, .block()
will throw a NotificationException
. It is a good practice to add it in the signature of the method, accompanied with a Javadoc entry.
In another method, you just need to catch the exception and do what you want with it.
/**
* @param notification
* @throws NotificationException in case of a HTTP error
*/
public void sendNotification(String notification) throws NotificationException {
final WebClient webClient = WebClient.builder()
.defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.build();
webClient.post()
.uri("http://localhost:9000/api")
.body(BodyInserters.fromValue(notification))
.retrieve()
.onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
.toBodilessEntity()
.block();
log.info("Notification delivered successfully");
}
public void someOtherMethod() {
try {
sendNotification("test");
} catch (NotificationException e) {
// Treat exception
}
}
In a more reactive style, you could return a Mono
and use onErrorResume()
.
public Mono<Void> sendNotification(String notification) {
final WebClient webClient = WebClient.builder()
.defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.build();
return webClient.post()
.uri("http://localhost:9000/api")
.body(BodyInserters.fromValue(notification))
.retrieve()
.onStatus(HttpStatus::isError, clientResponse -> Mono.error(NotificationException::new))
.bodyToMono(Void.class);
}
public void someOtherMethod() {
sendNotification("test")
.onErrorResume(NotificationException.class, ex -> {
log.error(ex.getMessage());
return Mono.empty();
})
.doOnSuccess(unused -> log.info("Notification delivered successfully"))
.block();
}
Answered By - Auktis
Answer Checked By - Robin (JavaFixing Admin)