Spring boot WebClient
In this article, we will take a deep dive into Spring boot WebClient
and how to send HTTP requests and get response using it with examples.
We will also learn how to set request headers and configure timeouts.
Below are the topics covered
2. WebClient installation
3. Sending request
4. Adding body to POST request
5. Setting headers
6. Handling 404 errors
7. Specifying timeout
What is WebClient
WebClient
is a client or an object for performing HTTP requests.
It a is reactive, non-blocking client. The terms
Reactive means, that it reacts to events such as server event when data is available.
Non-Blocking means, that it does not block the executing thread and executes request asynchronously.
WebClient
is introduced in Spring 5. It supports both synchronous and asynchronous(non-blocking) API calls but it should not be used synchronously, which shall defeat its purpose.
WebClient
is built on top of reactive libraries and it uses Reactor Netty by default.
WebClient
is an interface that resides in org.springframework.web.reactive.function.client
package. Notice the term reactive in package name.
WebClient
has a single implementation class DefaultWebClient
in the same package.
WebClient Gradle dependency
WebClient
is a part of Spring WebFlux module. To use WebClient
, we need to have following gradle dependency of Spring boot WebFlux
implementation 'org.springframework.boot:spring-boot-starter-webflux:2.7.0'
WebClient Maven dependency
Below is the maven dependency for Spring WebFlux module
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>2.7.0</version> </dependency>
Spring WebClient – Send HTTP Request
Sending HTTP request with Spring WebClient
involves following steps:
2. Defining request URL or endpoint
3. Defining request type
4. Sending request and Handling response
Creating WebClient
There are following ways to create an instance of Spring WebClient
.
A. create()
WebClient
contains a static interface method create()
, which returns its instance.
Actual type of instance is DefaultWebClient
. Example,
WebClient webClient = WebClient.create();
B. create(String)
WebClient
contains an overloaded create()
method which takes a string argument representing the URL of the endpoint where request will be sent.
WebClient webClient = WebClient.create( "https://jsonplaceholder.typicode.com");
C. Using builder
WebClient
has a static builder()
method. Calling build()
on return value returns an instance of WebClient
. Example,
WebClient webClient = WebClient.builder().build();
Defining Request URL
There are multiple ways to configure the URL at which HTTP request will be sent with WebClient
.
A. In create() method
WebClient
has a create() method which takes a string URL as argument. We have already seen this above.
B. In baseUrl() method
If you create WebClient
using builder()
method, then there is an option to specify URL using its baseURL()
method as shown below
WebClient webClient = WebClient. builder(). baseUrl("request-URL"). build();
Remember that when URL is specified using a builder, then it applies to all the requests sent using this WebClient
.
C. uri() method
Request URL can be specified after uri()
method. There are multiple overloaded versions of uri()
method.
(I). As a string
webClient. get(). uri("request-URL")
(II) As a URI
webClient. get(). uri(URI.create("request-URL"))
Note that uri()
method is available in an object of type RequestBodyUriSpec
, which is obtained when we call request methods on WebClient
such as get()
or post()
.
Thus, above lines of code can be expanded to
RequestBodyUriSpec uriSpec = w.post(); uriSpec.uri("request-URL");
Till this point, we have learnt how to create a
WebClient
object and define the request URL.Now, we are ready to send HTTP request to an endpoint URL.
To send an HTTP request, call the corresponding method on WebClient
instance. Such as get()
for HTTP GET request, post()
for POST request and so on.
Another method is to call method()
on WebClient
, supplying it the request type as argument.
Both the methods are shown below
WebClient w = WebClient.create("request-URL"); // GET request w.get(); // POST request w.post() // PUT request w.put() // GET request: other way w.method(HttpMethod.GET);
Actual request is not sent merely by calling any of the above methods.
Sending request and Handling response
WebClient
provides two methods to send the request and get response back. These are
1. retrieve()
This method sends the request and extracts the response.
Spring WebClient
returns the response in the form of Mono and Flux objects that belong to project reactor on which WebFlux is created.
Mono represents a stream which emits at most 1 element.
Flux represents a stream which can emit 0 to N elements.
To get response with retrieve()
method, invoke toEntity()
method, if you want a ResponseEntity
object.
A ResponseEntity
contains the actual response and other information such as response status and status code.
Or invoke bodyToMono()
, if you are only interested in the body of response.
Both toEntity()
and bodyToMono()
accept the class type of response expected. Example,
WebClient webClient = WebClient. create( "https://jsonplaceholder.typicode.com/posts/1"); // get response body Mono<Post> mono = webClient. get(). retrieve(). bodyToMono(Post.class); // get response entity Mono<ResponseEntity<Post>> mono = webClient. get(). retrieve(). toEntity(Post.class);
Here, Post
is a simple java class having fields that match the keys of below JSON response and their getter/setter methods.
{ id: 101, title: 'foo', body: 'bar', userId: 1 }
2. exchange()
exchange()
method provides access to response status and headers apart from response body. Example,
Mono<Post> res = webClient. get(). exchange().flatMap(r -> { // get status code HttpStatus statusCode = r.statusCode(); // get response return r.bodyToMono(Post.class) });
has been deprecated as of Spring 5.3. Use exchange()
exchangeToMono()
and exchangeToFlux()
instead as shown below.
Mono<ResponseEntity<Post>> res = webClient. get(). exchangeToMono(r -> r.toEntity(Post.class));
Body is required when sending a POST request using
WebClient
.To insert request body, use
body()
method.There are two ways to add a body with
WebClient
.1. Directly supplying the object to be sent in request body.
Example,
// create a mono Mono<Post> postMono = Mono.just(post); // send POST request Mono<ResponseEntity<Post>> response = webClient. post(). body(postMono, Post.class). retrieve(). toEntity(Post.class);
where post
is an object which needs to be sent in request body.
First argument of body()
is of type Publisher
, which is an interface. Both Mono
and Flux
implement it, so we can pass either of these.
Second argument is the class type of the Publisher
supplied as first argument.
2. Using BodyInserters class to insert body.
Example,
Mono<Post> postMono = Mono.just(post); Mono<Post> exchangeToMono = webClient. post(). body(BodyInserters.fromPublisher(postMono, Post.class)). exchangeToMono(response -> response.bodyToMono(Post.class));
BodyInserters
has a static
method fromPublisher()
which accepts two arguments, a Publisher
and the class type of Publisher
.
To get the response, we can use exchange()
or retrieve()
methods, as explained in the previous sections.
Spring WebClient headers
There are two ways to set request headers in Spring WebClient
.
1. Setting default headers
This is done while building WebClient
with WebClient.builder()
as shown below
WebClient w = WebClient. builder(). defaultHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML.toString());
This header will be sent with every request sent using this WebClient
instance.
2. Set request headers
Chaining header()
method after calling the respective request method as shown below
WebClient w = WebClient.create(); w.get().header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML.toString());
This header will be sent only with this request.
Spring WebClient handle 404 error
With exchangeToMono()
or exchangeToFlux()
methods, you can gain access to response status and status code.
To handle 404 error, use statusCode()
method from response object as shown below
Mono<Post> mono = webClient. get(). exchangeToMono(res -> { if(res.statusCode() == HttpStatus.NOT_FOUND) { return res.createException().flatMap(Mono::error); } else { return res.bodyToMono(Post.class); } });
This method checks for a specific 404 error. To check for general 4xx errors, use is4xxClientError()
method as shown below
Mono<Post> mono = webClient. get(). exchangeToMono(res -> { if(res.statusCode().is4xxClientError()) { return res.createException().flatMap(Mono::error); } else { return res.bodyToMono(Post.class); } });
There is no direct way to provide timeout in
WebClient
.You have to define a connector using
clientConnector()
method. It accepts an object of type ReactorClientHttpConnector
, which in turn, requires an HttpClient
.It is on this client object that we can configure timeout.
To create an object of HttpClient
, use its static create()
method followed by option()
method to specify timeouts.
In below example, we have configured three different timeouts
1. Connection timeout, which is the time it will wait for making a connection.
2. Read timeout, which is the time it will wait to read response.
3. Write timeout, which is the time it will wait to write response.
Note that read and write timeout only make sense when a connection is made. Therefore, they are added inside doOnConnected()
method.
HttpClient client = HttpClient. create(). option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000). doOnConnected(c -> c.addHandler(new ReadTimeoutHandler(10)). addHandler(new WriteTimeoutHandler(5))). responseTimeout(Duration.ofSeconds(5)); WebClient w = WebClient. builder(). clientConnector(new ReactorClientHttpConnector(client)). build();
In some articles, you will find timeout added using tcpConfiguration()
method as shown below
HttpClient. create(). tcpConfiguration(c -> c.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000));
method has been deprecated and will be removed in HttpClient v1.1.0.tcpConfiguration()
Summary
Below is the summary of using Spring WebClient
to send HTTP requests.
1. Spring WebClient
can be created using any of the below methods
WebClient w = WebClient.create(); WebClient w1 = WebClient.builder().baseUrl("request-URL").build(); WebClient w3 = WebClient.create("request-URL");
2. To send get()
request, call get()
method after WebClient
or method()
with HttpMethod.GET
as
// 1st method webClient.get(); // 2nd method webClient.method(HttpMethod.GET);
Similar ways can be used to other HTTP request types.
3. To get response, use retrieve()
or exchangeToMono()
or exchangeToFlux()
methods.
With retrieve()
, you can only access response body. With exchange methods, response body, status and status codes can also be accessed.
4. If you want response as ResponseEntity
, use toEntity()
method.
To get response wrapped with Mono
or Flux
objects, use bodyToMono()
and bodyToFlux()
methods.
5. To add body to a POST request, use body()
method.
6. body()
method directly accepts the object to be sent in request and its class type as arguments or using BodyInserters
as shown below
// 1st method webClient. post(). body(postMono, Post.class); // 2nd method webClient. post(). body(BodyInserters.fromPublisher(postMono, Post.class));
7. There are two ways to add headers to HTTP request sent with WebClient
.
// set default headers WebClient. builder(). defaultHeader(headerName, headerValue); // set request specific headers webClient. get(). header(headerName, headerValue);
That is all on Spring WebClient
. Feel free to refer to WebClient documentation for further clarity.
Hope the article was useful.