I need to make a syncronous, blocking request, and I'm using Spring's WebClient
instead of Spring's RestTemplate
due to the latter being deprecated. I don't need the reactive features in this case, I just want to consume a REST API in a straightforward way without including additional dependencies.
I have the following code, which works as intended:
MyObject object = webClient.get()
.uri( myUri )
.retrieve()
.bodyToMono( MyObject.class )
.block()
However, I need to manage the cases when I either can't connect to the API, or if I connect but I get a 4xx/5xx code.
So, the straightforward way would be to just put the call inside a try/catch, and catch Spring's WebClientResponseException
, which is thrown by .bodyToMono
if it gets a 4xx/5xx code:
import org.springframework.web.reactive.function.client.WebClientResponseException;
try {
MyObject object = webClient.get()
.uri( myUri )
.retrieve()
.bodyToMono( MyObject.class )
.block()
}
catch ( WebClientResponseException e ) {
// Logic to handle the exception.
}
This works fine, but doesn't work if the connection is refused (say, if the URL is wrong or if the service is down). In this case, I get the following in my console:
reactor.core.Exceptions$ErrorCallbackNotImplemented: io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: /127.0.0.1:8090 Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: /127.0.0.1:8090 Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s): |_ checkpoint ⇢ Request to GET http://127.0.0.1:8090/test [DefaultWebClient] Stack trace: Caused by: java.net.ConnectException: finishConnect(..) failed: Connection refused at io.netty.channel.unix.Errors.throwConnectException(Errors.java:124) ~[netty-transport-native-unix-common-4.1.48.Final.jar:4.1.48.Final] (...)
I'm not sure which exception I need to catch to handle this case.
Besides the above, I also would like to throw a custom exception if the connection is refused and a different custom exception if I get an error code. In the second case, I tried using the .onStatus
method:
try {
MyObject object = webClient.get()
.uri( myUri )
.retrieve()
.onStatus( HttpStatus::is4xxClientError, response -> {
return Mono.error( new CustomClientException( "A client error ocurred" ) );
})
.bodyToMono( MyObject.class )
.block()
}
catch ( CustomClientException e ) {
// Logic to handle the exception.
}
But the exception is not caught inside the catch block, although the stack trace does appear on console.
Is there any way to handle 4xx/5xx codes and connection errors using a try/catch block, hopefully with custom exceptions? Or should I use a different web client and/or change my approach? I'm not familiar with reactive programming.
Thanks in advance.
ConnectException
to catch in order to handle this case. – FreehandWebClient
if you immediatelyblock()
? – CharadeConnectException
but I get a compiler error saying that it never gets thrown in the try block. – LoweringRestTemplate
is deprecated in Spring 5, so the docs recommend usingWebClient
, which supports both blocking and non-blocking requests. I know I won't get any performance benefit by usingblock()
, but I don't think that's neccesarily wrong in this case, and there's always the chance to refactor the code in the future. – LoweringonStatus
block? – RicardRestTemplate
toWebClient
in a way that requires the least amount of refactorization. In several cases, the try blocks not only contain the web request code, but also the logic to manipulate the received data, which can also throw exceptions, so the catch block handles all of the above. – LoweringonStatus
means moving the exception logic inside a method and call it inside bothonStatus
blocks (for 4xx and 5xx, separatedly), and I still need to keep the original try/catch block to handle the exceptions after the web request, again calling the same method. It's quite the task and doesn't seem very clean, but maybe there's no other way. – LoweringWebClient
separately, and "other logic" should have their own try catch block to handle their "problems". – Ricard