|
1 | 1 | [[webflux-client]]
|
2 | 2 | = WebClient
|
3 | 3 |
|
4 |
| -The `spring-webflux` module includes a reactive, non-blocking client for HTTP requests |
5 |
| -with a functional-style API client and Reactive Streams support. `WebClient` depends on a |
6 |
| -lower level HTTP client library to execute requests and that support is pluggable. |
| 4 | +Spring WebFlux includes a reactive, non-blocking `WebClient` for performing HTTP requests |
| 5 | +using a functional-style API that exposes Reactor `Flux` and `Mono` types, see |
| 6 | +<<web-reactive.adoc#webflux-reactive-libraries>>. The client relies on the same |
| 7 | +<<web-reactive.adoc#webflux-codecs,codecs>> that WebFlux server applications use to work |
| 8 | +with request and response content. |
7 | 9 |
|
8 |
| -`WebClient` |
9 |
| -uses the same <<web-reactive.adoc#webflux-codecs,codecs>> as WebFlux server applications do, and |
10 |
| -shares a common base package, some common APIs, and infrastructure with the |
11 |
| -server <<web-reactive.adoc#webflux-fn,functional web framework>>. |
12 |
| -The API exposes Reactor `Flux` and `Mono` types, also see |
13 |
| -<<web-reactive.adoc#webflux-reactive-libraries>>. By default it uses |
14 |
| -it uses https://github.com/reactor/reactor-netty[Reactor Netty] as the HTTP client |
15 |
| -library and |
16 |
| -https://github.com/jetty-project/jetty-reactive-httpclient[Jetty ReactiveStreams HttpClient] |
17 |
| -is supported as well via `JettyClientHttpConnector`, but others can be plugged in |
18 |
| -through a custom `ClientHttpConnector`. |
| 10 | +Internally `WebClient` delegates to an HTTP client library. By default it uses |
| 11 | +https://github.com/reactor/reactor-netty[Reactor Netty], there is built-in support for |
| 12 | +the Jetty https://github.com/jetty-project/jetty-reactive-httpclient[reactive HtpClient], |
| 13 | +and others can be plugged in through a `ClientHttpConnector`. |
19 | 14 |
|
20 |
| -By comparison to the <<integration.adoc#rest-resttemplate,RestTemplate>>, the |
21 |
| -`WebClient` is: |
22 | 15 |
|
23 |
| -* non-blocking, reactive, and supports higher concurrency with less hardware resources. |
24 |
| -* provides a functional API that takes advantage of Java 8 lambdas. |
25 |
| -* supports both synchronous and asynchronous scenarios. |
26 |
| -* supports streaming up or down from a server. |
27 | 16 |
|
28 |
| -The `RestTemplate` is not a good fit for use in non-blocking applications, and therefore |
29 |
| -Spring WebFlux application should always use the `WebClient`. The `WebClient` should also |
30 |
| -be preferred in Spring MVC, in most high concurrency scenarios, and for composing a |
31 |
| -sequence of remote, inter-dependent calls. |
| 17 | + |
| 18 | +[[webflux-client-builder]] |
| 19 | +== Configuration |
| 20 | + |
| 21 | +The simplest way to create a `WebClient` is through one of the static factory methods: |
| 22 | + |
| 23 | +* `WebClient.create()` |
| 24 | +* `WebClient.create(String baseUrl)` |
| 25 | + |
| 26 | +The above uses Reactor Netty `HttpClient` from "io.projectreactor.netty:reactor-netty" |
| 27 | +with default settings and participates in global resources such for event loop threads and |
| 28 | +a connection pool, see <<webflux-client-builder-reactor, Reactor Netty configuration>>. |
| 29 | + |
| 30 | +The `WebClient.Builder` can be used for access to further options: |
| 31 | + |
| 32 | +* `uriBuilderFactory` -- customized `UriBuilderFactory` to use as a base URL. |
| 33 | +* `defaultHeader` -- headers for every request. |
| 34 | +* `defaultCookie)` -- cookies for every request. |
| 35 | +* `defaultRequest` -- `Consumer` to customize every request. |
| 36 | +* `filter` -- client filter for every request. |
| 37 | +* `exchangeStrategies` -- HTTP message reader/writer customizations. |
| 38 | +* `clientConnector` -- HTTP client library settings. |
| 39 | + |
| 40 | +For example, to configure <<web-reactive.adoc#webflux-codecs,HTTP codecs>>: |
| 41 | + |
| 42 | +[source,java,intent=0] |
| 43 | +[subs="verbatim,quotes"] |
| 44 | +---- |
| 45 | + ExchangeStrategies strategies = ExchangeStrategies.builder() |
| 46 | + .codecs(configurer -> { |
| 47 | + // ... |
| 48 | + }) |
| 49 | + .build(); |
| 50 | +
|
| 51 | + WebClient client = WebClient.builder() |
| 52 | + .exchangeStrategies(strategies) |
| 53 | + .build(); |
| 54 | +---- |
| 55 | + |
| 56 | +Once built a `WebClient` instance is immutable. However, you can clone it, and build a |
| 57 | +modified copy without affecting the original instance: |
| 58 | + |
| 59 | +[source,java,intent=0] |
| 60 | +[subs="verbatim,quotes"] |
| 61 | +---- |
| 62 | + WebClient client1 = WebClient.builder() |
| 63 | + .filter(filterA).filter(filterB).build(); |
| 64 | +
|
| 65 | + WebClient client2 = client1.mutate() |
| 66 | + .filter(filterC).filter(filterD).build(); |
| 67 | +
|
| 68 | + // client1 has filterA, filterB |
| 69 | +
|
| 70 | + // client2 has filterA, filterB, filterC, filterD |
| 71 | +---- |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +[[webflux-client-builder-reactor]] |
| 76 | +=== Reactor Netty |
| 77 | + |
| 78 | +To customize Reactor Netty settings: |
| 79 | + |
| 80 | +[source,java,intent=0] |
| 81 | +[subs="verbatim,quotes"] |
| 82 | +---- |
| 83 | + HttpClient httpClient = HttpClient.create() |
| 84 | + httpClient.secure(sslSpec -> ...); |
| 85 | + ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); |
| 86 | +
|
| 87 | + WebClient webClient = WebClient.builder().clientConnector(connector).build(); |
| 88 | +---- |
| 89 | + |
| 90 | +By default `HttpClient` participates in the global Reactor Netty resources held in |
| 91 | +`reactor.netty.http.HttpResources`, including event loop threads and a connection pool. |
| 92 | +This is the recommended mode since fixed, shared resources are preferred for event loop |
| 93 | +concurrency. In this mode global resources remain active until the process exits. |
| 94 | + |
| 95 | +If the server is timed with the process, there is typically no need for an explicit |
| 96 | +shutdown. However if the server can start or stop in-process, e.g. Spring MVC |
| 97 | +application deployed as a WAR, you can declare a Spring-managed bean of type |
| 98 | +`ReactorResourceFactory` with `globaResources=true` (the default) to ensure the Reactor |
| 99 | +Netty global resources are shut down when the Spring `ApplicationContext` is closed: |
| 100 | + |
| 101 | +[source,java,intent=0] |
| 102 | +[subs="verbatim,quotes"] |
| 103 | +---- |
| 104 | + @Bean |
| 105 | + public ReactorResourceFactory reactorResourceFactory() { |
| 106 | + return new ReactorResourceFactory(); |
| 107 | + } |
| 108 | +---- |
| 109 | + |
| 110 | +You may also choose not to participate in the global Reactor Netty resources. However keep |
| 111 | +in mind in this mode the burden is on you to ensure all Reactor Netty client and server |
| 112 | +instances use shared resources: |
| 113 | + |
| 114 | +[source,java,intent=0] |
| 115 | +[subs="verbatim,quotes"] |
| 116 | +---- |
| 117 | + @Bean |
| 118 | + public ReactorResourceFactory resourceFactory() { |
| 119 | + ReactorResourceFactory factory = new ReactorResourceFactory(); |
| 120 | + factory.setGlobalResources(false); // <1> |
| 121 | + return factory; |
| 122 | + } |
| 123 | +
|
| 124 | + @Bean |
| 125 | + public WebClient webClient() { |
| 126 | +
|
| 127 | + Function<HttpClient, HttpClient> mapper = client -> { |
| 128 | + // Further customizations... |
| 129 | + }; |
| 130 | +
|
| 131 | + ClientHttpConnector connector = |
| 132 | + new ReactorClientHttpConnector(resourceFactory(), mapper); // <2> |
| 133 | +
|
| 134 | + return WebClient.builder().clientConnector(connector).build(); // <3> |
| 135 | + } |
| 136 | +---- |
| 137 | +<1> Create resources independent of global ones. |
| 138 | +<2> Use `ReactorClientHttpConnector` constructor with resource factory. |
| 139 | +<3> Plug the connector into the `WebClient.Builder`. |
32 | 140 |
|
33 | 141 |
|
34 | 142 |
|
@@ -268,69 +376,11 @@ inline-style, through the built-in `BodyInserters`. For example:
|
268 | 376 |
|
269 | 377 |
|
270 | 378 |
|
271 |
| -[[webflux-client-builder]] |
272 |
| -== Builder options |
273 |
| - |
274 |
| -A simple way to create `WebClient` is through the static factory methods `create()` and |
275 |
| -`create(String)` with a base URL for all requests. You can also use `WebClient.builder()` |
276 |
| -for access to more options. |
277 |
| - |
278 |
| -To customize the underlying HTTP client: |
279 |
| - |
280 |
| -[source,java,intent=0] |
281 |
| -[subs="verbatim,quotes"] |
282 |
| ----- |
283 |
| - SslContext sslContext = ... |
284 |
| -
|
285 |
| - ClientHttpConnector connector = new ReactorClientHttpConnector( |
286 |
| - builder -> builder.sslContext(sslContext)); |
287 |
| -
|
288 |
| - WebClient webClient = WebClient.builder() |
289 |
| - .clientConnector(connector) |
290 |
| - .build(); |
291 |
| ----- |
292 |
| - |
293 |
| -To customize the <<web-reactive.adoc#webflux-codecs,HTTP codecs>> used for encoding and |
294 |
| -decoding HTTP messages: |
295 |
| - |
296 |
| -[source,java,intent=0] |
297 |
| -[subs="verbatim,quotes"] |
298 |
| ----- |
299 |
| - ExchangeStrategies strategies = ExchangeStrategies.builder() |
300 |
| - .codecs(configurer -> { |
301 |
| - // ... |
302 |
| - }) |
303 |
| - .build(); |
304 |
| -
|
305 |
| - WebClient webClient = WebClient.builder() |
306 |
| - .exchangeStrategies(strategies) |
307 |
| - .build(); |
308 |
| ----- |
309 |
| - |
310 |
| -The builder can be used to insert <<webflux-client-filter>>. |
311 |
| - |
312 |
| -Explore the `WebClient.Builder` in your IDE for other options related to URI building, |
313 |
| -default headers (and cookies), and more. |
314 |
| - |
315 |
| -After the `WebClient` is built, you can always obtain a new builder from it, in order to |
316 |
| -build a new `WebClient`, based on, but without affecting the current instance: |
317 |
| - |
318 |
| -[source,java,intent=0] |
319 |
| -[subs="verbatim,quotes"] |
320 |
| ----- |
321 |
| - WebClient modifiedClient = client.mutate() |
322 |
| - // user builder methods... |
323 |
| - .build(); |
324 |
| ----- |
325 |
| - |
326 |
| - |
327 |
| - |
328 |
| - |
329 | 379 | [[webflux-client-filter]]
|
330 | 380 | == Client Filters
|
331 | 381 |
|
332 |
| -You can register an `ExchangeFilterFunction` in the `WebClient.Builder` to intercept and |
333 |
| -possibly modify requests performed through the client: |
| 382 | +You can register a client filter (`ExchangeFilterFunction`) through the `WebClient.Builder` |
| 383 | +in order to intercept and/or modify requests: |
334 | 384 |
|
335 | 385 | [source,java,intent=0]
|
336 | 386 | [subs="verbatim,quotes"]
|
|
0 commit comments