[[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.

The `RestTemplate` is not a good fit for use in non-blocking applications, and therefore
Spring WebFlux application should always use the `WebClient`. The `WebClient` should also
be preferred in Spring MVC, in most high concurrency scenarios, and for composing a
sequence of remote, inter-dependent calls.




[[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("https://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);
----

When `onStatus` is used, if the response is expected to have content, then the `onStatus`
callback should consume it. If not, the content will be automatically drained to ensure
resources are released.




[[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, you need to provide a `MultiValueMap<String, ?>` whose values are
either Objects representing part content, or `HttpEntity` representing the content and
headers for a part. `MultipartBodyBuilder` provides a convenient API to prepare a
multipart request:

[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();
----

In most cases you do not have to specify the `Content-Type` for each part. The content
type is determined automatically based on the `HttpMessageWriter` chosen to serialize it,
or in the case of a `Resource` based on the file extension. If necessary you can
explicitly provide the `MediaType` to use for each part through one fo the overloaded
builder `part` methods.

Once a `MultiValueMap` is prepared, the easiest way to pass it to the the `WebClient` is
through the `syncBody` method:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
	MultipartBodyBuilder builder = ...;

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

If the `MultiValueMap` contains at least one non-String value, which could also be
represent regular form data (i.e. "application/x-www-form-urlencoded"), you don't have to
set the `Content-Type` to "multipart/form-data". This is always the case when using
`MultipartBodyBuilder` which ensures an `HttpEntity` wrapper.

As an alternative to `MultipartBodyBuilder`, you can also provide multipart content,
inline-style, through the built-in `BodyInserters`. For example:

[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]]
== Client Filters

You can register an `ExchangeFilterFunction` in the `WebClient.Builder` to intercept and
possibly modify requests performed through the client:

[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();
----

This can be used for cross-cutting concerns such as authentication. The example below uses
a filter for basic authentication through a static factory method:

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

// static import of ExchangeFilterFunctions.basicAuthentication

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

Filters apply globally to every request. To change how a filter's behavior for a specific
request, you can add request attributes to the `ClientRequest` that can then be accessed
by all filters in the chain:

[source,java,intent=0]
[subs="verbatim,quotes"]
----
WebClient client = WebClient.builder()
		.filter((request, next) -> {
			Optional<Object> usr = request.attribute("myAttribute");
			// ...
		})
		.build();

client.get().uri("https://example.org/")
		.attribute("myAttribute", "...")
		.retrieve()
		.bodyToMono(Void.class);

	}
----

You can also replicate an existing `WebClient`, and insert new filters or remove already
registered filters. In the example below, a basic authentication filter is inserted at
index 0:

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

// static import of ExchangeFilterFunctions.basicAuthentication

WebClient client = webClient.mutate()
		.filters(filterList -> {
			filterList.add(0, basicAuthentication("user", "password"));
		})
		.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.
