[[webflux-client]]
= WebClient

The `spring-webflux` module includes a reactive, non-blocking client for HTTP requests
with a functional-style API client and Reactive Streams support. `WebClient` depends on a
lower level HTTP client library to execute requests and that support is pluggable.

`WebClient`
uses the same <<web-reactive.adoc#webflux-codecs,codecs>> as WebFlux server applications do, and
shares a common base package, some common APIs, and infrastructure with the
server <<web-reactive.adoc#webflux-fn,functional web framework>>.
The API exposes Reactor `Flux` and `Mono` types, also see
<<web-reactive.adoc#webflux-reactive-libraries>>. By default it uses
it uses https://github.com/reactor/reactor-netty[Reactor Netty] as the HTTP client
library but others can be plugged in through a custom `ClientHttpConnector`.

By comparison to the <<integration.adoc#rest-resttemplate,RestTemplate>>, the
`WebClient` is:

* non-blocking, reactive, and supports higher concurrency with less hardware resources.
* provides a functional API that takes advantage of Java 8 lambdas.
* supports both synchronous and asynchronous scenarios.
* supports streaming up or down from a server.

For most concurrent scenarios, e.g. a sequence of possibly inter-dependent HTTP calls,
or for making remote calls from the server-side, prefer using the `WebClient`.




[[webflux-client-retrieve]]
== Retrieve

The `retrieve()` method is the easiest way to get a response body and decode it:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	WebClient client = WebClient.create("http://example.org");

	Mono<Person> result = client.get()
			.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
			.retrieve()
			.bodyToMono(Person.class);
----

You can also get a stream of objects decoded from the response:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	Flux<Quote> result = client.get()
			.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
			.retrieve()
			.bodyToFlux(Quote.class);
----

By default, responses with 4xx or 5xx status codes result in an error of type
`WebClientResponseException` but you can customize that:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	Mono<Person> result = client.get()
			.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
			.retrieve()
			.onStatus(HttpStatus::is4xxServerError, response -> ...)
			.onStatus(HttpStatus::is5xxServerError, response -> ...)
			.bodyToMono(Person.class);
----




[[webflux-client-exchange]]
== Exchange

The `exchange()` method provides more control. The below example is equivalent
to `retrieve()` but also provides access to the `ClientResponse`:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	Mono<Person> result = client.get()
			.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
			.exchange()
			.flatMap(response -> response.bodyToMono(Person.class));
----

At this level you can also create a full `ResponseEntity`:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	Mono<ResponseEntity<Person>> result = client.get()
			.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
			.exchange()
			.flatMap(response -> response.toEntity(Person.class));
----

Note that unlike `retrieve()`, with `exchange()` there are no automatic error signals for
4xx and 5xx responses. You have to check the status code and decide how to proceed.

[CAUTION]
====
When using `exchange()` you must always use any of the body or toEntity methods of
`ClientResponse` to ensure resources are released and to avoid potential issues with HTTP
connection pooling. You can use `bodyToMono(Void.class)` if no response content is
expected. However keep in mind that if the response does have content, the connection
will be closed and will not be placed back in the pool.
====




[[webflux-client-body]]
== Request body

The request body can be encoded from an Object:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	Mono<Person> personMono = ... ;

	Mono<Void> result = client.post()
			.uri("/persons/{id}", id)
			.contentType(MediaType.APPLICATION_JSON)
			.body(personMono, Person.class)
			.retrieve()
			.bodyToMono(Void.class);
----

You can also have a stream of objects encoded:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	Flux<Person> personFlux = ... ;

	Mono<Void> result = client.post()
			.uri("/persons/{id}", id)
			.contentType(MediaType.APPLICATION_STREAM_JSON)
			.body(personFlux, Person.class)
			.retrieve()
			.bodyToMono(Void.class);
----

Or if you have the actual value, use the `syncBody` shortcut method:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	Person person = ... ;

	Mono<Void> result = client.post()
			.uri("/persons/{id}", id)
			.contentType(MediaType.APPLICATION_JSON)
			.syncBody(person)
			.retrieve()
			.bodyToMono(Void.class);
----



[[webflux-client-body-form]]
=== Form data

To send form data, provide a `MultiValueMap<String, String>` as the body. Note that the
content is automatically set to `"application/x-www-form-urlencoded"` by the
`FormHttpMessageWriter`:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	MultiValueMap<String, String> formData = ... ;

	Mono<Void> result = client.post()
			.uri("/path", id)
			.syncBody(formData)
			.retrieve()
			.bodyToMono(Void.class);
----

You can also supply form data in-line via `BodyInserters`:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	import static org.springframework.web.reactive.function.BodyInserters.*;

	Mono<Void> result = client.post()
			.uri("/path", id)
			.body(fromFormData("k1", "v1").with("k2", "v2"))
			.retrieve()
			.bodyToMono(Void.class);
----



[[webflux-client-body-multipart]]
=== Multipart data

To send multipart data, provide a `MultiValueMap<String, ?>` where values are either an
Object representing the part body, or an `HttpEntity` representing the part body and
headers. `MultipartBodyBuilder` can be used to build the parts:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	MultipartBodyBuilder builder = new MultipartBodyBuilder();
	builder.part("fieldPart", "fieldValue");
	builder.part("filePart", new FileSystemResource("...logo.png"));
	builder.part("jsonPart", new Person("Jason"));

	MultiValueMap<String, HttpEntity<?>> parts = builder.build();

	Mono<Void> result = client.post()
			.uri("/path", id)
			.syncBody(parts)
			.retrieve()
			.bodyToMono(Void.class);
----

Note that the content type for each part is automatically set based on the extension of the
file being written or the type of Object. If you prefer you can also be more explicit and
specify the content type for each part.

You can also supply multipart data in-line via `BodyInserters`:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	import static org.springframework.web.reactive.function.BodyInserters.*;

	Mono<Void> result = client.post()
			.uri("/path", id)
			.body(fromMultipartData("fieldPart", "value").with("filePart", resource))
			.retrieve()
			.bodyToMono(Void.class);
----



[[webflux-client-builder]]
== Builder options

A simple way to create `WebClient` is through the static factory methods `create()` and
`create(String)` with a base URL for all requests. You can also use `WebClient.builder()`
for access to more options.

To customize the underlying HTTP client:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	SslContext sslContext = ...

	ClientHttpConnector connector = new ReactorClientHttpConnector(
			builder -> builder.sslContext(sslContext));

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

To customize the <<web-reactive.adoc#webflux-codecs,HTTP codecs>> used for encoding and
decoding HTTP messages:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	ExchangeStrategies strategies = ExchangeStrategies.builder()
			.codecs(configurer -> {
				// ...
			})
			.build();

	WebClient webClient = WebClient.builder()
			.exchangeStrategies(strategies)
			.build();
----

The builder can be used to insert <<webflux-client-filter>>.

Explore the `WebClient.Builder` in your IDE for other options related to URI building,
default headers (and cookies), and more.

After the `WebClient` is built, you can always obtain a new builder from it, in order to
build a new `WebClient`, based on, but without affecting the current instance:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	WebClient modifiedClient = client.mutate()
			// user builder methods...
			.build();
----




[[webflux-client-filter]]
== Filters

`WebClient` supports interception style request filtering:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	WebClient client = WebClient.builder()
			.filter((request, next) -> {
				ClientRequest filtered = ClientRequest.from(request)
						.header("foo", "bar")
						.build();
				return next.exchange(filtered);
			})
			.build();
----

`ExchangeFilterFunctions` provides a filter for basic authentication:

[source,java,intent=0]
[subs="verbatim,quotes"]
----

	// static import of ExchangeFilterFunctions.basicAuthentication

	WebClient client = WebClient.builder()
			.filter(basicAuthentication("user", "pwd"))
			.build();
----

You can also mutate an existing `WebClient` instance without affecting the original:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	WebClient filteredClient = client.mutate()
			.filter(basicAuthentication("user", "pwd")
			.build();
----




[[webflux-client-testing]]
== Testing

To test code that uses the `WebClient`, you can use a mock web server such as the
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer]. To see example
use, check
https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java[WebClientIntegrationTests]
in the Spring Framework tests, or the
https://github.com/square/okhttp/tree/master/samples/static-server[static-server]
sample in the OkHttp repository.
