Spring WebClient
is a non-blocking and reactive web client to perform HTTP requests. WebClient
has been added in Spring 5 (spring-webflux
module) and provides fluent functional style API.
Before Spring 5, RestTemplate has been the primary technique for client-side HTTP accesses, which is part of the Spring MVC project. Since Spring 5, WebClient
is the recommended approach.
1. Maven
To use WebClient
api, we must have spring-boot-starter-webflux module imported into the project.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2. Creating Spring WebClient
To create WebClient
bean, we can follow any one of the given approaches.
2.1. Using WebClient.create()
The create()
method is an overloaded method and can optionally accept a base URL for requests.
WebClient webClient1 = WebClient.create(); //with empty URI
WebClient webClient2 = WebClient.create("https://client-domain.com");
2.2. Using WebClient.Builder API
We can also build the client using the DefaultWebClientBuilder class, which uses builder pattern style fluent-API.
WebClient webClient2 = WebClient.builder()
.baseUrl("http://localhost:3000")
.defaultCookie("cookie-name", "cookie-value")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
3. Sending HTTP Requests
To send a request, we can use its fluent API and execute the necessary steps as per requirements. For example, sending an HTTP POST request involves the following steps.
- Create
WebClient.UriSpec
reference usingmethod(HttpMethod)
or prebuilt methods such asget()
,put()
,post()
ordelete()
. - Set the request URI if not set already.
- Set the request headers and authentication details, if any.
- Set the request body, if any.
- Call the
retrieve()
orexchange()
method. Theretrieve()
method directly performs the HTTP request and retrieves the response body. Theexchange()
method returnsClientResponse
having the response status and headers. We can get the response body fromClientResponse
instance. - Handle the response returned from the server.
In the following example, we send an HTTP POST request to URI http://localhost:3000/employees that returns an Employee object after the successful call.
WebClient webClient = WebClient.create("http://localhost:3000");
Employee createdEmployee = webClient.post()
.uri("/employees")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
4. Handling API Response
If we want to get only the response body then using methods retrieve()
and then use the bodyToFlux()
and bodyToMono()
methods.
....retrieve().bodyToMono(Employee.class);
//or
....retrieve().bodyToFlux(Employee.class);
Please note that bodyToMono()
and bodyToFlux()
methods always expect a response body of a given class type. If the response status code is 4xx (client error) or 5xx (Server error) i.e. there is no response body then these methods throw WebClientException. Use bodyToMono(Void.class)
if no response body is expected.
webClient.put()
.uri("/employees/100")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Void.class);
Else, use the method exchange()
which will return the ClientResponse
which has all the response elements such as status, headers and response body as well.
When using exchange()
, we must always use any of the bodyXXX()
or toEntity()
methods of ClientResponse to ensure resources are released and to avoid potential issues with HTTP connection pooling.
webClient.get()
.uri("/employees/1")
.exchange()
.flatMap(clientResponse -> clientResponse.bodyToMono(Employee.class))
.block();
As of Spring 5.3, the exchange() method is deprecated due to potential memory and connection leaks. Prefer exchangeToMono(), exchangeToFlux(), or retrieve() instead.
Mono<Employee> response = webClient.get()
.uri("/employees/1")
.exchangeToMono(response -> {
if (response.statusCode().equals(HttpStatus.OK)) {
return response.bodyToMono(Employee.class);
} else if (response.statusCode().is4xxClientError()) {
return Mono.just("Error response");
} else {
return response.createException().flatMap(Mono::error);
}
});
5. Spring WebClient Examples
5.1. GET API Example
Generally, we will use GET
API to fetch either collection of resources or a singular resource. Let’s see the example of both use cases using get()
method call.
HTTP GET /employees
: collection of employees asFlux
HTTP GET /employees/{id}
: single employee by id asMono
@Autowired
WebClient webClient;
public Flux<Employee> findAll()
{
return webClient.get()
.uri("/employees")
.retrieve()
.bodyToFlux(Employee.class);
}
public Mono<Employee> findById(Integer id)
{
return webClient.get()
.uri("/employees/" + id)
.retrieve()
/*.onStatus(httpStatus -> HttpStatus.NOT_FOUND.equals(httpStatus),
clientResponse -> Mono.empty())*/
.bodyToMono(Employee.class);
}
5.2. POST API Example
POST
API is commonly used for creating a resource. Let’s see an example of post()
method to create an employee.
HTTP POST /employees
: creates a new employee from request body and returns the created employee in response.
@Autowired
WebClient webClient;
public Mono<Employee> create(Employee empl)
{
return webClient.post()
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
}
5.3. PUT API Example
PUT
API is commonly used for updating a resource. Let’s see an example of put()
method to update an employee.
HTTP PUT /employees/{id}
: updates an existing employee data from the request body and returns the updated employee in response.
@Autowired
WebClient webClient;
public Mono<Employee> update(Employee e)
{
return webClient.put()
.uri("/employees/" + e.getId())
.body(Mono.just(e), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
}
5.4. DELETE API Example
DELETE
API is commonly used for deleting a resource. Let’s see an example of delete()
method to delete an employee from records.
HTTP DELETE /employees/{id}
: deletes an existing employee by its id. It does not accept any request body as well as does not return any response body as well.
@Autowired
WebClient webClient;
public Mono<Void> delete(Integer id)
{
return webClient.delete()
.uri("/employees/" +id)
.retrieve()
.bodyToMono(Void.class);
}
6. Useful Configurations
6.1. Configuring Memory Limit
Spring WebFlux configures the default memory limit for buffering data in-memory to 256KB. If this limit is exceeded in any case then we will encounter DataBufferLimitException
error.
To reset the memory limit, configure the below property in application.properties
file.
spring.codec.max-in-memory-size=1MB
6.2. Configuring Connection Timeouts
We can use HttpClient
class to set timeout periods for connection timeout, read timeout and write timeouts.
@Bean
public WebClient getWebClient()
{
HttpClient httpClient = HttpClient.create()
.tcpConfiguration(client ->
client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10))));
ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
return WebClient.builder()
.baseUrl("http://localhost:3000")
.clientConnector(connector)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
Drop me your questions related to Spring WebClient.
Happy Learning !!