Calling REST Services
Spring Boot provides various convenient ways to call remote REST services.
If you are developing a non-blocking reactive application and you’re using Spring WebFlux, then you can use WebClient
.
If you prefer blocking APIs then you can use RestClient
or RestTemplate
.
WebClient
If you have Spring WebFlux on your classpath we recommend that you use WebClient
to call remote REST services.
The WebClient
interface provides a functional style API and is fully reactive.
You can learn more about the WebClient
in the dedicated section in the Spring Framework docs.
If you are not writing a reactive Spring WebFlux application you can use the RestClient instead of a WebClient .
This provides a similar functional API, but is blocking rather than reactive.
|
Spring Boot creates and pre-configures a prototype WebClient.Builder
bean for you.
It is strongly advised to inject it in your components and use it to create WebClient
instances.
Spring Boot is configuring that builder to share HTTP resources and reflect codecs setup in the same fashion as the server ones (see WebFlux HTTP codecs auto-configuration), and more.
The following code shows a typical example:
-
Java
-
Kotlin
import reactor.core.publisher.Mono;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
@Service
public class MyService {
private final WebClient webClient;
public MyService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("https://example.org").build();
}
public Mono<Details> someRestCall(String name) {
return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
}
}
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
@Service
class MyService(webClientBuilder: WebClient.Builder) {
private val webClient: WebClient
init {
webClient = webClientBuilder.baseUrl("https://example.org").build()
}
fun someRestCall(name: String?): Mono<Details> {
return webClient.get().uri("/{name}/details", name)
.retrieve().bodyToMono(Details::class.java)
}
}
WebClient Runtime
Spring Boot will auto-detect which ClientHttpConnector
to use to drive WebClient
depending on the libraries available on the application classpath.
In order of preference, the following clients are supported:
-
Reactor Netty
-
Jetty RS client
-
Apache HttpClient
-
JDK HttpClient
If multiple clients are available on the classpath, the most preferred client will be used.
The spring-boot-starter-webflux
starter depends on io.projectreactor.netty:reactor-netty
by default, which brings both server and client implementations.
If you choose to use Jetty as a reactive server instead, you should add a dependency on the Jetty Reactive HTTP client library, org.eclipse.jetty:jetty-reactive-httpclient
.
Using the same technology for server and client has its advantages, as it will automatically share HTTP resources between client and server.
Developers can override the resource configuration for Jetty and Reactor Netty by providing a custom ReactorResourceFactory
or JettyResourceFactory
bean - this will be applied to both clients and servers.
If you wish to override that choice for the client, you can define your own ClientHttpConnector
bean and have full control over the client configuration.
You can learn more about the WebClient
configuration options in the Spring Framework reference documentation.
WebClient Customization
There are three main approaches to WebClient
customization, depending on how broadly you want the customizations to apply.
To make the scope of any customizations as narrow as possible, inject the auto-configured WebClient.Builder
and then call its methods as required.
WebClient.Builder
instances are stateful: Any change on the builder is reflected in all clients subsequently created with it.
If you want to create several clients with the same builder, you can also consider cloning the builder with WebClient.Builder other = builder.clone();
.
To make an application-wide, additive customization to all WebClient.Builder
instances, you can declare WebClientCustomizer
beans and change the WebClient.Builder
locally at the point of injection.
Finally, you can fall back to the original API and use WebClient.create()
.
In that case, no auto-configuration or WebClientCustomizer
is applied.
WebClient SSL Support
If you need custom SSL configuration on the ClientHttpConnector
used by the WebClient
, you can inject a WebClientSsl
instance that can be used with the builder’s apply
method.
The WebClientSsl
interface provides access to any SSL bundles that you have defined in your application.properties
or application.yaml
file.
The following code shows a typical example:
-
Java
-
Kotlin
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
@Service
public class MyService {
private final WebClient webClient;
public MyService(WebClient.Builder webClientBuilder, WebClientSsl ssl) {
this.webClient = webClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
}
public Mono<Details> someRestCall(String name) {
return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
}
}
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
@Service
class MyService(webClientBuilder: WebClient.Builder, ssl: WebClientSsl) {
private val webClient: WebClient
init {
webClient = webClientBuilder.baseUrl("https://example.org")
.apply(ssl.fromBundle("mybundle")).build()
}
fun someRestCall(name: String?): Mono<Details> {
return webClient.get().uri("/{name}/details", name)
.retrieve().bodyToMono(Details::class.java)
}
}
RestClient
If you are not using Spring WebFlux or Project Reactor in your application we recommend that you use RestClient
to call remote REST services.
The RestClient
interface provides a functional style blocking API.
Spring Boot creates and pre-configures a prototype RestClient.Builder
bean for you.
It is strongly advised to inject it in your components and use it to create RestClient
instances.
Spring Boot is configuring that builder with HttpMessageConverters
and an appropriate ClientHttpRequestFactory
.
The following code shows a typical example:
-
Java
-
Kotlin
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder) {
this.restClient = restClientBuilder.baseUrl("https://example.org").build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
import org.springframework.boot.docs.io.restclient.restclient.ssl.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
@Service
class MyService(restClientBuilder: RestClient.Builder) {
private val restClient: RestClient
init {
restClient = restClientBuilder.baseUrl("https://example.org").build()
}
fun someRestCall(name: String?): Details {
return restClient.get().uri("/{name}/details", name)
.retrieve().body(Details::class.java)!!
}
}
RestClient Customization
There are three main approaches to RestClient
customization, depending on how broadly you want the customizations to apply.
To make the scope of any customizations as narrow as possible, inject the auto-configured RestClient.Builder
and then call its methods as required.
RestClient.Builder
instances are stateful: Any change on the builder is reflected in all clients subsequently created with it.
If you want to create several clients with the same builder, you can also consider cloning the builder with RestClient.Builder other = builder.clone();
.
To make an application-wide, additive customization to all RestClient.Builder
instances, you can declare RestClientCustomizer
beans and change the RestClient.Builder
locally at the point of injection.
Finally, you can fall back to the original API and use RestClient.create()
.
In that case, no auto-configuration or RestClientCustomizer
is applied.
You can also change the global HTTP client configuration. |
RestClient SSL Support
If you need custom SSL configuration on the ClientHttpRequestFactory
used by the RestClient
, you can inject a RestClientSsl
instance that can be used with the builder’s apply
method.
The RestClientSsl
interface provides access to any SSL bundles that you have defined in your application.properties
or application.yaml
file.
The following code shows a typical example:
-
Java
-
Kotlin
import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder, RestClientSsl ssl) {
this.restClient = restClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
import org.springframework.boot.autoconfigure.web.client.RestClientSsl
import org.springframework.boot.docs.io.restclient.restclient.ssl.settings.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
@Service
class MyService(restClientBuilder: RestClient.Builder, ssl: RestClientSsl) {
private val restClient: RestClient
init {
restClient = restClientBuilder.baseUrl("https://example.org")
.apply(ssl.fromBundle("mybundle")).build()
}
fun someRestCall(name: String?): Details {
return restClient.get().uri("/{name}/details", name)
.retrieve().body(Details::class.java)!!
}
}
If you need to apply other customization in addition to an SSL bundle, you can use the ClientHttpRequestFactorySettings
class with ClientHttpRequestFactoryBuilder
:
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder, SslBundles sslBundles) {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings
.ofSslBundle(sslBundles.getBundle("mybundle"))
.withReadTimeout(Duration.ofMinutes(2));
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder.detect().build(settings);
this.restClient = restClientBuilder.baseUrl("https://example.org").requestFactory(requestFactory).build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.ssl.SslBundles
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
import java.time.Duration
@Service
class MyService(restClientBuilder: RestClient.Builder, sslBundles: SslBundles) {
private val restClient: RestClient
init {
val settings = ClientHttpRequestFactorySettings.defaults()
.withReadTimeout(Duration.ofMinutes(2))
.withSslBundle(sslBundles.getBundle("mybundle"))
val requestFactory = ClientHttpRequestFactoryBuilder.detect().build(settings);
restClient = restClientBuilder
.baseUrl("https://example.org")
.requestFactory(requestFactory).build()
}
fun someRestCall(name: String?): Details {
return restClient.get().uri("/{name}/details", name).retrieve().body(Details::class.java)!!
}
}
RestTemplate
Spring Framework’s RestTemplate
class predates RestClient
and is the classic way that many applications use to call remote REST services.
You might choose to use RestTemplate
when you have existing code that you don’t want to migrate to RestClient
, or because you’re already familiar with the RestTemplate
API.
Since RestTemplate
instances often need to be customized before being used, Spring Boot does not provide any single auto-configured RestTemplate
bean.
It does, however, auto-configure a RestTemplateBuilder
, which can be used to create RestTemplate
instances when needed.
The auto-configured RestTemplateBuilder
ensures that sensible HttpMessageConverters
and an appropriate ClientHttpRequestFactory
are applied to RestTemplate
instances.
The following code shows a typical example:
-
Java
-
Kotlin
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
@Service
class MyService(restTemplateBuilder: RestTemplateBuilder) {
private val restTemplate: RestTemplate
init {
restTemplate = restTemplateBuilder.build()
}
fun someRestCall(name: String): Details {
return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
}
}
RestTemplateBuilder
includes a number of useful methods that can be used to quickly configure a RestTemplate
.
For example, to add BASIC authentication support, you can use builder.basicAuthentication("user", "password").build()
.
RestTemplate Customization
There are three main approaches to RestTemplate
customization, depending on how broadly you want the customizations to apply.
To make the scope of any customizations as narrow as possible, inject the auto-configured RestTemplateBuilder
and then call its methods as required.
Each method call returns a new RestTemplateBuilder
instance, so the customizations only affect this use of the builder.
To make an application-wide, additive customization, use a RestTemplateCustomizer
bean.
All such beans are automatically registered with the auto-configured RestTemplateBuilder
and are applied to any templates that are built with it.
The following example shows a customizer that configures the use of a proxy for all hosts except 192.168.0.5
:
-
Java
-
Kotlin
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
public class MyRestTemplateCustomizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
HttpRoutePlanner routePlanner = new CustomRoutePlanner(new HttpHost("proxy.example.com"));
HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
static class CustomRoutePlanner extends DefaultProxyRoutePlanner {
CustomRoutePlanner(HttpHost proxy) {
super(proxy);
}
@Override
protected HttpHost determineProxy(HttpHost target, HttpContext context) throws HttpException {
if (target.getHostName().equals("192.168.0.5")) {
return null;
}
return super.determineProxy(target, context);
}
}
}
import org.apache.hc.client5.http.classic.HttpClient
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner
import org.apache.hc.client5.http.routing.HttpRoutePlanner
import org.apache.hc.core5.http.HttpException
import org.apache.hc.core5.http.HttpHost
import org.apache.hc.core5.http.protocol.HttpContext
import org.springframework.boot.web.client.RestTemplateCustomizer
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.web.client.RestTemplate
class MyRestTemplateCustomizer : RestTemplateCustomizer {
override fun customize(restTemplate: RestTemplate) {
val routePlanner: HttpRoutePlanner = CustomRoutePlanner(HttpHost("proxy.example.com"))
val httpClient: HttpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build()
restTemplate.requestFactory = HttpComponentsClientHttpRequestFactory(httpClient)
}
internal class CustomRoutePlanner(proxy: HttpHost?) : DefaultProxyRoutePlanner(proxy) {
@Throws(HttpException::class)
public override fun determineProxy(target: HttpHost, context: HttpContext): HttpHost? {
if (target.hostName == "192.168.0.5") {
return null
}
return super.determineProxy(target, context)
}
}
}
Finally, you can define your own RestTemplateBuilder
bean.
Doing so will replace the auto-configured builder.
If you want any RestTemplateCustomizer
beans to be applied to your custom builder, as the auto-configuration would have done, configure it using a RestTemplateBuilderConfigurer
.
The following example exposes a RestTemplateBuilder
that matches what Spring Boot’s auto-configuration would have done, except that custom connect and read timeouts are also specified:
-
Java
-
Kotlin
import java.time.Duration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyRestTemplateBuilderConfiguration {
@Bean
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
return configurer.configure(new RestTemplateBuilder())
.connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(2));
}
}
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.Duration
@Configuration(proxyBeanMethods = false)
class MyRestTemplateBuilderConfiguration {
@Bean
fun restTemplateBuilder(configurer: RestTemplateBuilderConfigurer): RestTemplateBuilder {
return configurer.configure(RestTemplateBuilder()).connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(2))
}
}
The most extreme (and rarely used) option is to create your own RestTemplateBuilder
bean without using a configurer.
In addition to replacing the auto-configured builder, this also prevents any RestTemplateCustomizer
beans from being used.
You can also change the global HTTP client configuration. |
RestTemplate SSL Support
If you need custom SSL configuration on the RestTemplate
, you can apply an SSL bundle to the RestTemplateBuilder
as shown in this example:
-
Java
-
Kotlin
import org.springframework.boot.docs.io.restclient.resttemplate.Details;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
this.restTemplate = restTemplateBuilder.sslBundle(sslBundles.getBundle("mybundle")).build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
import org.springframework.boot.docs.io.restclient.resttemplate.Details
import org.springframework.boot.ssl.SslBundles
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
@Service
class MyService(restTemplateBuilder: RestTemplateBuilder, sslBundles: SslBundles) {
private val restTemplate: RestTemplate
init {
restTemplate = restTemplateBuilder.sslBundle(sslBundles.getBundle("mybundle")).build()
}
fun someRestCall(name: String): Details {
return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
}
}
HTTP Client Detection for RestClient and RestTemplate
Spring Boot will auto-detect which HTTP client to use with RestClient
and RestTemplate
depending on the libraries available on the application classpath.
In order of preference, the following clients are supported:
-
Apache HttpClient
-
Jetty HttpClient
-
Reactor Netty HttpClient
-
JDK client (
java.net.http.HttpClient
) -
Simple JDK client (
java.net.HttpURLConnection
)
If multiple clients are available on the classpath, and not global configuration is provided, the most preferred client will be used.
Global HTTP Client Configuration
If the the auto-detected HTTP client does not meet your needs, you can use the spring.http.client.factory
property to pick a specific factory.
For example, if you have Apache HttpClient on your classpath, but you prefer Jetty’s HttpClient
you can add use the following:
-
Properties
-
YAML
spring.http.client.factory=jetty
spring:
http:
client:
factory: jetty
You can also set properties to change defaults that will be applied to all clients. For example, you may want to change timeouts and if redirects are followed:
-
Properties
-
YAML
spring.http.client.connect-timeout=2s
spring.http.client.read-timeout=1s
spring.http.client.redirects=dont-follow
spring:
http:
client:
connect-timeout: 2s
read-timeout: 1s
redirects: dont-follow
For more complex customizations, you can declare your own ClientHttpRequestFactoryBuilder
bean which will cause auto-configuration to back off.
This can be useful when you need to customize some of the internals of the underlying HTTP library.
For example, the following will use a JDK client configured with a specific ProxySelector
:
-
Java
-
Kotlin
import java.net.ProxySelector;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyClientHttpConfiguration {
@Bean
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(ProxySelector proxySelector) {
return ClientHttpRequestFactoryBuilder.jdk()
.withHttpClientCustomizer((builder) -> builder.proxy(proxySelector));
}
}
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.net.ProxySelector
import java.net.http.HttpClient
@Configuration(proxyBeanMethods = false)
class MyClientHttpConfiguration {
@Bean
fun clientHttpRequestFactoryBuilder(proxySelector: ProxySelector): ClientHttpRequestFactoryBuilder<*>? {
return ClientHttpRequestFactoryBuilder.jdk()
.withHttpClientCustomizer { builder -> builder.proxy(proxySelector) }
}
}