Spring Web Reactive | 2. WebClient | 2.1. Configuration
The easiest way to create a WebClient is to use one of the static factory methods.
WebClient.create()WebClient.create(String baseUrl)
You can use WebClient.builder() with additional options.
uriBuilderFactory: A customizedUriBuilderFactoryto use as the base URL.defaultUriVariables: Default values used when expanding URI templates.defaultHeader: Headers used for every request.defaultCookie: Cookies used for every request.defaultRequest: Customize every request with aConsumer.filter: Client filters for every request.exchangeStrategies: Customization of HTTP message readers/writers.clientConnector: Settings for the HTTP client library.
For example:
Java
WebClient client = WebClient.builder()
.codecs(configurer -> ... )
.build();
Kotlin
val webClient = WebClient.builder()
.codecs { configurer -> ... }
.build()
Once built, WebClient is immutable. However, you can clone it and create a modified copy as follows.
Java
WebClient client1 = WebClient.builder()
.filter(filterA).filter(filterB).build();
WebClient client2 = client1.mutate()
.filter(filterC).filter(filterD).build();
// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD
Kotlin
val client1 = WebClient.builder()
.filter(filterA).filter(filterB).build()
val client2 = client1.mutate()
.filter(filterC).filter(filterD).build()
// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD
2.1.1. MaxInMemorySize
Codecs have limits for buffering data in memory in order to address memory issues in applications. By default, this is set to 256KB. If that is not enough, the following error occurs.
org.springframework.core.io.buffer.DataBufferLimitException : Exceeded limit on max bytes to buffer
To change the default codec limit, configure the following.
Java
WebClient webClient = WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
.build();
Kotlin
val webClient = WebClient.builder()
.codecs { configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024) }
.build()
2.1.2. Reactor Netty
To customize Reactor Netty settings, provide a preconfigured HttpClient.
Java
HttpClient httpClient = HttpClient.create().secure(sslSpec -> ...);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Kotlin
val httpClient = HttpClient.create().secure { ... }
val webClient = WebClient.builder()
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()
Resources
By default, HttpClient uses global Reactor Netty resources in reactor.netty.http.HttpResources, including event loop threads and the connection pool. This is the recommended mode because fixed, shared resources are preferred for concurrent execution of event loops. In this mode, global resources remain active until the process exits.
If the timing is aligned with the server process, you generally do not need to shut them down explicitly. However, if a server can be started and stopped inside the process, for example a Spring MVC application deployed as a WAR, you can declare a Spring-managed Bean of type ReactorResourceFactory with globalResources=true, the default, and confirm that Reactor Netty is global. As shown in the following example, resources are shut down when the Spring ApplicationContext closes.
Java
@Bean
public ReactorResourceFactory reactorResourceFactory() {
return new ReactorResourceFactory();
}
Kotlin
@Bean
fun reactorResourceFactory() = ReactorResourceFactory()
You can also choose not to use global Reactor Netty resources. However, in this mode you are responsible for ensuring that all Reactor Netty client and server instances use shared resources, as shown in the following example.
Java
@Bean
public ReactorResourceFactory resourceFactory() {
ReactorResourceFactory factory = new ReactorResourceFactory();
factory.setUseGlobalResources(false); // (1)
return factory;
}
@Bean
public WebClient webClient() {
Function<HttpClient, HttpClient> mapper = client -> {
// Further customizations...
};
ClientHttpConnector connector =
new ReactorClientHttpConnector(resourceFactory(), mapper); // (2)
return WebClient.builder().clientConnector(connector).build(); // (3)
}
Kotlin
@Bean
fun resourceFactory() = ReactorResourceFactory().apply {
isUseGlobalResources = false // (1)
}
@Bean
fun webClient(): WebClient {
val mapper: (HttpClient) -> HttpClient = {
// Further customizations...
}
val connector = ReactorClientHttpConnector(resourceFactory(), mapper) // (2)
return WebClient.builder().clientConnector(connector).build() // (3)
}
- (1) Create resources independent of global resources.
- (2) Use the
ReactorClientHttpConnectorconstructor with the resource factory. - (3) Connect the connector to
WebClient.Builder.
Timeouts
To configure the connection timeout:
Java
import io.netty.channel.ChannelOption;
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Kotlin
import io.netty.channel.ChannelOption
val httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
val webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
To configure read or write timeouts:
Java
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
HttpClient httpClient = HttpClient.create()
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10)));
// Create WebClient...
Kotlin
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler
val httpClient = HttpClient.create()
.doOnConnected { conn -> conn
.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10))
}
// Create WebClient...
To set the response timeout for all requests:
Java
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// Create WebClient...
Kotlin
val httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// Create WebClient...
To set the response timeout for a specific request:
Java
WebClient.create().get()
.uri("https://example.org/path")
.httpRequest(httpRequest -> {
HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
reactorRequest.responseTimeout(Duration.ofSeconds(2));
})
.retrieve()
.bodyToMono(String.class);
Kotlin
WebClient.create().get()
.uri("https://example.org/path")
.httpRequest { httpRequest: ClientHttpRequest ->
val reactorRequest = httpRequest.getNativeRequest<HttpClientRequest>()
reactorRequest.responseTimeout(Duration.ofSeconds(2))
}
.retrieve()
.bodyToMono(String::class.java)
2.1.3. Jetty
The following example shows how to customize Jetty HttpClient settings.
Java
HttpClient httpClient = new HttpClient();
httpClient.setCookieStore(...);
WebClient webClient = WebClient.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
Kotlin
val httpClient = HttpClient()
httpClient.cookieStore = ...
val webClient = WebClient.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
By default, HttpClient creates its own resources (Executor, Scheduler, ByteBufferPool) and keeps them active until the process exits or stop() is called.
As shown in the following example, by declaring JettyResourceFactory as a Spring-managed Bean, you can share resources across multiple instances of Jetty clients and servers and reliably shut down resources when the Spring ApplicationContext closes.
Java
@Bean
public JettyResourceFactory resourceFactory() {
return new JettyResourceFactory();
}
@Bean
public WebClient webClient() {
HttpClient httpClient = new HttpClient();
// Further customizations...
ClientHttpConnector connector =
new JettyClientHttpConnector(httpClient, resourceFactory()); (1)
return WebClient.builder().clientConnector(connector).build(); (2)
}
Kotlin
@Bean
fun resourceFactory() = JettyResourceFactory()
@Bean
fun webClient(): WebClient {
val httpClient = HttpClient()
// Further customizations...
val connector = JettyClientHttpConnector(httpClient, resourceFactory()) (1)
return WebClient.builder().clientConnector(connector).build() (2)
}
- (1) Use the
JettyClientHttpConnectorconstructor with the resource factory. - (2) Connect the connector to
WebClient.Builder.
2.1.4. HttpComponents
The following example shows how to customize Apache HttpComponents HttpClient settings.
Java
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
clientBuilder.setDefaultRequestConfig(...);
CloseableHttpAsyncClient client = clientBuilder.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
WebClient webClient = WebClient.builder().clientConnector(connector).build();
Kotlin
val client = HttpAsyncClients.custom().apply {
setDefaultRequestConfig(...)
}.build()
val connector = HttpComponentsClientHttpConnector(client)
val webClient = WebClient.builder().clientConnector(connector).build()