I migrated some code that uses the declarative http interface support of Spring from WebClient
to RestClient
as the underlying implementation. Our code started failing on the staging server after that. I managed to trace the problem to a difference in handling of the base url between RestClient
and WebClient
.
The difference is easily seen via this test program:
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class Test {
public static void main(String[] args) throws InterruptedException {
WebClient webClient = WebClient.builder()
.baseUrl("numbersapi.com").build();
Mono<String> bodilessEntity = webClient.get().uri("/42").retrieve().bodyToMono(String.class);
String block = bodilessEntity.block();
System.out.println("block = " + block);
RestClient restClient = RestClient.builder()
.baseUrl("numbersapi.com")
.build();
String viaRestClient = restClient.get().uri("/42").retrieve().body(String.class);
System.out.println("viaRestClient = " + viaRestClient);
}
}
Note how the base url does not specify a scheme (http
or https
) in both cases. However, with WebClient
, this is not an issue and the call to the remote service is done. With RestClient
, an exception is thrown:
Exception in thread "main" java.lang.IllegalArgumentException: URI with undefined scheme
at java.net.http/jdk.internal.net.http.common.Utils.newIAE(Utils.java:326)
at java.net.http/jdk.internal.net.http.HttpRequestBuilderImpl.checkURI(HttpRequestBuilderImpl.java:79)
at java.net.http/jdk.internal.net.http.HttpRequestBuilderImpl.uri(HttpRequestBuilderImpl.java:71)
at java.net.http/jdk.internal.net.http.HttpRequestBuilderImpl.uri(HttpRequestBuilderImpl.java:43)
at org.springframework.http.client.JdkClientHttpRequest.buildRequest(JdkClientHttpRequest.java:136)
at org.springframework.http.client.JdkClientHttpRequest.executeInternal(JdkClientHttpRequest.java:95)
at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:468)
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.retrieve(DefaultRestClient.java:439)
Maybe it should not have worked in the first place with WebClient
, I don't know. But maybe it would be good to have the same behaviour?
Comment From: bclozel
Note: we had reports saying the opposite for WebClient
in #31033.
Comment From: poutsma
This is a consequence of the fact that WebClient uses Reactor Netty by default, which allows for non-absolute URIs, and that RestClient uses the JDK HttpClient by default, which does not accept non-absolute URIs.
Even if we would fix this in RestClient, for instance by setting a scheme if not present, we would still not be in the same situation as WebClient, because we have do not make a similar check there; we simple pass it to Reactor Netty.