Configuration

The simplest way to create a WebClient is through one of the static factory methods:

  • WebClient.create()

  • WebClient.create(String baseUrl)

The above methods use the Reactor Netty HttpClient with default settings and expect io.projectreactor.netty:reactor-netty to be on the classpath.

You can also use WebClient.builder() with further options:

  • uriBuilderFactory: Customized UriBuilderFactory to use as a base URL.

  • defaultHeader: Headers for every request.

  • defaultCookie: Cookies for every request.

  • defaultRequest: Consumer to customize every request.

  • filter: Client filter for every request.

  • exchangeStrategies: HTTP message reader/writer customizations.

  • clientConnector: HTTP client library settings.

The following example configures HTTP codecs:

Java
WebClient client = WebClient.builder()
		.exchangeStrategies(builder -> {
				return builder.codecs(codecConfigurer -> {
					//...
				});
		})
		.build();
Kotlin
val webClient = WebClient.builder()
		.exchangeStrategies { strategies ->
			strategies.codecs {
				//...
			}
		}
		.build()

Once built, a WebClient instance is immutable. However, you can clone it and build a modified copy without affecting the original instance, as the following example shows:

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

MaxInMemorySize

Spring WebFlux configures limits for buffering data in-memory in codec to avoid application memory issues. By the default this is configured to 256KB and if that’s not enough for your use case, you’ll see the following:

org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer

You can configure this limit on all default codecs with the following code sample:

Java
WebClient webClient = WebClient.builder()
		.exchangeStrategies(builder ->
			builder.codecs(codecs ->
				codecs.defaultCodecs().maxInMemorySize(2 * 1024 * 1024)
			)
		)
		.build();
Kotlin
val webClient = WebClient.builder()
	.exchangeStrategies { builder ->
			builder.codecs {
				it.defaultCodecs().maxInMemorySize(2 * 1024 * 1024)
			}
	}
	.build()

Reactor Netty

To customize Reactor Netty settings, simple provide a pre-configured 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 participates in the global Reactor Netty resources held in reactor.netty.http.HttpResources, including event loop threads and a connection pool. This is the recommended mode, since fixed, shared resources are preferred for event loop concurrency. In this mode global resources remain active until the process exits.

If the server is timed with the process, there is typically no need for an explicit shutdown. However, if the server can start or stop in-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) to ensure that the Reactor Netty global resources are shut down when the Spring ApplicationContext is closed, as the following example shows:

Java
@Bean
public ReactorResourceFactory reactorResourceFactory() {
	return new ReactorResourceFactory();
}
Kotlin
@Bean
fun reactorResourceFactory() = ReactorResourceFactory()

You can also choose not to participate in the global Reactor Netty resources. However, in this mode, the burden is on you to ensure that all Reactor Netty client and server instances use shared resources, as the following example shows:

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)
}
1 Create resources independent of global ones.
2 Use the ReactorClientHttpConnector constructor with resource factory.
3 Plug the connector into the WebClient.Builder.
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 ones.
2 Use the ReactorClientHttpConnector constructor with resource factory.
3 Plug the connector into the WebClient.Builder.

Timeouts

To configure a connection timeout:

Java
import io.netty.channel.ChannelOption;

HttpClient httpClient = HttpClient.create()
		.tcpConfiguration(client ->
				client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000));
Kotlin
import io.netty.channel.ChannelOption

val httpClient = HttpClient.create()
		.tcpConfiguration { it.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)}

To configure a read and/or write timeout values:

Java
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;

HttpClient httpClient = HttpClient.create()
		.tcpConfiguration(client ->
				client.doOnConnected(conn -> conn
						.addHandlerLast(new ReadTimeoutHandler(10))
						.addHandlerLast(new WriteTimeoutHandler(10))));
Kotlin
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler

val httpClient = HttpClient.create().tcpConfiguration {
	it.doOnConnected { conn -> conn
			.addHandlerLast(ReadTimeoutHandler(10))
			.addHandlerLast(WriteTimeoutHandler(10))
	}
}

Jetty

The following example shows how to customize Jetty HttpClient settings:

Java
HttpClient httpClient = new HttpClient();
httpClient.setCookieStore(...);
ClientHttpConnector connector = new JettyClientHttpConnector(httpClient);

WebClient webClient = WebClient.builder().clientConnector(connector).build();
Kotlin
val httpClient = HttpClient()
httpClient.cookieStore = ...
val connector = JettyClientHttpConnector(httpClient)

val webClient = WebClient.builder().clientConnector(connector).build();

By default, HttpClient creates its own resources (Executor, ByteBufferPool, Scheduler), which remain active until the process exits or stop() is called.

You can share resources between multiple instances of the Jetty client (and server) and ensure that the resources are shut down when the Spring ApplicationContext is closed by declaring a Spring-managed bean of type JettyResourceFactory, as the following example shows:

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)
}
1 Use the JettyClientHttpConnector constructor with resource factory.
2 Plug the connector into the WebClient.Builder.
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 JettyClientHttpConnector constructor with resource factory.
2 Plug the connector into the WebClient.Builder.