Skip to content

Commit 67910ee

Browse files
committed
WebFlux and Integration share webflux-client.adoc
Extract WebClient content into a separate file that is now included both in the WebFlux and in the Integration sections. This allows having RestTemplate and WebClient documented in one place under Integration while also keeping the same included in the WebFlux section too.
1 parent 71ccf3c commit 67910ee

File tree

3 files changed

+274
-296
lines changed

3 files changed

+274
-296
lines changed

src/docs/asciidoc/integration.adoc

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -946,50 +946,24 @@ plugging in third-party or custom solutions here.
946946

947947

948948
[[rest-client-access]]
949-
=== Accessing RESTful services on the Client
950-
The `RestTemplate` is the core class for client-side access to RESTful services. It is
951-
conceptually similar to other template classes in Spring, such as `JdbcTemplate` and
952-
`JmsTemplate` and other template classes found in other Spring portfolio projects.
953-
`RestTemplate`'s behavior is customized by providing callback methods and configuring
954-
the `HttpMessageConverter` used to marshal objects into the HTTP request body and to
955-
unmarshal any response back into an object. As it is common to use XML as a message
956-
format, Spring provides a `MarshallingHttpMessageConverter` that uses the Object-to-XML
957-
framework that is part of the `org.springframework.oxm` package. This gives you a wide
958-
range of choices of XML to Object mapping technologies to choose from.
949+
=== Accessing REST endpoints
959950

960-
This section describes how to use the `RestTemplate` and its associated
961-
`HttpMessageConverters`.
951+
The Spring Framework offers two choices for client-side access to REST endpoints:
962952

953+
* <<rest-resttemplate>> -- the original Spring REST client with an API similar to other
954+
template classes in Spring, such as `JdbcTemplate`, `JmsTemplate` and others. The
955+
`RestTemplate` is built for synchronous use with the blocking I/O.
956+
* <<webflux-client,WebClient>> -- reactive client with a functional,
957+
fluent API. It is built on a non-blocking foundation for async and sync scenarios and
958+
supports Reactive Streams back pressure.
963959

964960

965961
[[rest-resttemplate]]
966962
==== RestTemplate
967-
Invoking RESTful services in Java is typically done using a helper class such as Apache
968-
HttpComponents `HttpClient`. For common REST operations this approach is too low level as
969-
shown below.
970963

971-
[source,java,indent=0]
972-
[subs="verbatim,quotes"]
973-
----
974-
String uri = "http://example.com/hotels/1/bookings";
975-
976-
PostMethod post = new PostMethod(uri);
977-
String request = // create booking request content
978-
post.setRequestEntity(new StringRequestEntity(request));
979-
980-
httpClient.executeMethod(post);
981-
982-
if (HttpStatus.SC_CREATED == post.getStatusCode()) {
983-
Header location = post.getRequestHeader("Location");
984-
if (location != null) {
985-
System.out.println("Created new booking at :" + location.getValue());
986-
}
987-
}
988-
----
989-
990-
RestTemplate provides higher level methods that correspond to each of the six main HTTP
991-
methods that make invoking many RESTful services a one-liner and enforce REST best
992-
practices.
964+
The `RestTemplate` provides a higher level API over HTTP client libraries with methods
965+
that correspond to each of the six main HTTP methods that make invoking many RESTful
966+
services a one-liner and enforce REST best practices.
993967

994968

995969
[NOTE]
@@ -1373,6 +1347,8 @@ The `AsyncRestTemplate` is deprecated.
13731347
Please use the <<web-reactive.adoc#webflux-client,WebClient>> instead.
13741348

13751349

1350+
include::web/webflux-client.adoc[leveloffset=+3]
1351+
13761352

13771353

13781354
[[ejb]]
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
[[webflux-client]]
2+
= WebClient
3+
4+
The `spring-webflux` module includes a non-blocking, reactive client for HTTP requests
5+
with Reactive Streams back pressure. It shares
6+
<<web-reactive.adoc#webflux-codecs,HTTP codecs>> and other infrastructure with the
7+
server <<web-reactive.adoc#webflux-fn,functional web framework>>.
8+
9+
`WebClient` provides a higher level API over HTTP client libraries. By default
10+
it uses https://github.com/reactor/reactor-netty[Reactor Netty] but that is pluggable
11+
with a different `ClientHttpConnector`. The `WebClient` API returns Reactor `Flux` or
12+
`Mono` for output and accepts Reactive Streams `Publisher` as input (see
13+
<<web-reactive.adoc#webflux-reactive-libraries>>).
14+
15+
[TIP]
16+
====
17+
By comparison to the
18+
<<integration.adoc#rest-resttemplate,RestTemplate>>, the `WebClient` offers a more
19+
functional and fluent API that taking full advantage of Java 8 lambdas. It supports both
20+
sync and async scenarios, including streaming, and brings the efficiency of
21+
non-blocking I/O.
22+
====
23+
24+
25+
[[webflux-client-retrieve]]
26+
== Retrieve
27+
28+
The `retrieve()` method is the easiest way to get a response body and decode it:
29+
30+
[source,java,intent=0]
31+
[subs="verbatim,quotes"]
32+
----
33+
WebClient client = WebClient.create("http://example.org");
34+
35+
Mono<Person> result = client.get()
36+
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
37+
.retrieve()
38+
.bodyToMono(Person.class);
39+
----
40+
41+
You can also get a stream of objects decoded from the response:
42+
43+
[source,java,intent=0]
44+
[subs="verbatim,quotes"]
45+
----
46+
Flux<Quote> result = client.get()
47+
.uri("/quotes").accept(TEXT_EVENT_STREAM)
48+
.retrieve()
49+
.bodyToFlux(Quote.class);
50+
----
51+
52+
By default, responses with 4xx or 5xx status codes result in an error of type
53+
`WebClientResponseException` but you can customize that:
54+
55+
[source,java,intent=0]
56+
[subs="verbatim,quotes"]
57+
----
58+
Mono<Person> result = client.get()
59+
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
60+
.retrieve()
61+
.onStatus(HttpStatus::is4xxServerError, response -> ...)
62+
.onStatus(HttpStatus::is5xxServerError, response -> ...)
63+
.bodyToFlux(Person.class);
64+
----
65+
66+
67+
68+
[[webflux-client-exchange]]
69+
== Exchange
70+
71+
The `exchange()` method provides more control. The below example is equivalent
72+
to `retrieve()` but also provides access to the `ClientResponse`:
73+
74+
[source,java,intent=0]
75+
[subs="verbatim,quotes"]
76+
----
77+
Mono<Person> result = client.get()
78+
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
79+
.exchange()
80+
.flatMap(response -> response.bodyToMono(Person.class));
81+
----
82+
83+
At this level you can also create a full `ResponseEntity`:
84+
85+
[source,java,intent=0]
86+
[subs="verbatim,quotes"]
87+
----
88+
Mono<ResponseEntity<Person>> result = client.get()
89+
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
90+
.exchange()
91+
.flatMap(response -> response.bodyToEntity(Person.class));
92+
----
93+
94+
Note that unlike `retrieve()`, with `exchange()` there are no automatic error signals for
95+
4xx and 5xx responses. You have to check the status code and decide how to proceed.
96+
97+
[CAUTION]
98+
====
99+
When you use `exchange()`, you must call `response.close()` if you do not intend to read
100+
the response body in order to close the underlying HTTP connection. Not doing so can
101+
result in connection pool inconsistencies or memory leaks.
102+
103+
You do not have to call `response.close()` if you consume the body because forcing a
104+
connection to be closed negates the benefits of persistent connections and connection
105+
pooling.
106+
====
107+
108+
109+
[[webflux-client-body]]
110+
== Request body
111+
112+
The request body can be encoded from an Object:
113+
114+
[source,java,intent=0]
115+
[subs="verbatim,quotes"]
116+
----
117+
Mono<Person> personMono = ... ;
118+
119+
Mono<Void> result = client.post()
120+
.uri("/persons/{id}", id)
121+
.contentType(MediaType.APPLICATION_JSON)
122+
.body(personMono, Person.class)
123+
.retrieve()
124+
.bodyToMono(Void.class);
125+
----
126+
127+
You can also have a stream of objects encoded:
128+
129+
[source,java,intent=0]
130+
[subs="verbatim,quotes"]
131+
----
132+
Flux<Person> personFlux = ... ;
133+
134+
Mono<Void> result = client.post()
135+
.uri("/persons/{id}", id)
136+
.contentType(MediaType.APPLICATION_STREAM_JSON)
137+
.body(personFlux, Person.class)
138+
.retrieve()
139+
.bodyToMono(Void.class);
140+
----
141+
142+
Or if you have the actual value, use the `syncBody` shortcut method:
143+
144+
[source,java,intent=0]
145+
[subs="verbatim,quotes"]
146+
----
147+
Person person = ... ;
148+
149+
Mono<Void> result = client.post()
150+
.uri("/persons/{id}", id)
151+
.contentType(MediaType.APPLICATION_JSON)
152+
.syncBody(person)
153+
.retrieve()
154+
.bodyToMono(Void.class);
155+
----
156+
157+
158+
[[webflux-client-builder]]
159+
== Builder options
160+
161+
A simple way to create `WebClient` is through the static factory methods `create()` and
162+
`create(String)` with a base URL for all requests. You can also use `WebClient.builder()`
163+
for access to more options.
164+
165+
To customize the underlying HTTP client:
166+
167+
[source,java,intent=0]
168+
[subs="verbatim,quotes"]
169+
----
170+
SslContext sslContext = ...
171+
172+
ClientHttpConnector connector = new ReactorClientHttpConnector(
173+
builder -> builder.sslContext(sslContext));
174+
175+
WebClient webClient = WebClient.builder()
176+
.clientConnector(connector)
177+
.build();
178+
----
179+
180+
To customize the <<web-reactive.adoc#webflux-codecs,HTTP codecs>> used for encoding and
181+
decoding HTTP messages:
182+
183+
[source,java,intent=0]
184+
[subs="verbatim,quotes"]
185+
----
186+
ExchangeStrategies strategies = ExchangeStrategies.builder()
187+
.codecs(configurer -> {
188+
// ...
189+
})
190+
.build();
191+
192+
WebClient webClient = WebClient.builder()
193+
.exchangeStrategies(strategies)
194+
.build();
195+
196+
----
197+
198+
The builder can be used to insert <<webflux-client-filter>>.
199+
200+
Explore the `WebClient.Builder` in your IDE for other options related to URI building,
201+
default headers (and cookies), and more.
202+
203+
After the `WebClient` is built, you can always obtain a new builder from it, in order to
204+
build a new `WebClient`, based on, but without affecting the current instance:
205+
206+
[source,java,intent=0]
207+
[subs="verbatim,quotes"]
208+
----
209+
WebClient modifiedClient = client.mutate()
210+
// user builder methods...
211+
.build();
212+
----
213+
214+
215+
216+
217+
218+
[[webflux-client-filter]]
219+
== Filters
220+
221+
`WebClient` supports interception style request filtering:
222+
223+
[source,java,intent=0]
224+
[subs="verbatim,quotes"]
225+
----
226+
WebClient client = WebClient.builder()
227+
.filter((request, next) -> {
228+
229+
ClientRequest filtered = ClientRequest.from(request)
230+
.header("foo", "bar")
231+
.build();
232+
233+
return next.exchange(filtered);
234+
})
235+
.build();
236+
----
237+
238+
`ExchangeFilterFunctions` provides a filter for basic authentication:
239+
240+
[source,java,intent=0]
241+
[subs="verbatim,quotes"]
242+
----
243+
244+
// static import of ExchangeFilterFunctions.basicAuthentication
245+
246+
WebClient client = WebClient.builder()
247+
.filter(basicAuthentication("user", "pwd"))
248+
.build();
249+
----
250+
251+
You can also mutate an existing `WebClient` instance without affecting the original:
252+
253+
[source,java,intent=0]
254+
[subs="verbatim,quotes"]
255+
----
256+
WebClient filteredClient = client.mutate()
257+
.filter(basicAuthentication("user", "pwd")
258+
.build();
259+
----
260+

0 commit comments

Comments
 (0)