Last updated on:
In this example, we’re going to build a web client to call the REST APIs built in the previous blog, [Tips] Spring Boot - Create REST APIs with WebFlux. We’ll call it API provider going forward.
Below are the URIs:
- http://localhost:8080/rest/findalldpts - get a collection of departments,
- http://localhost:8080/rest/findbyid/{department_id} - get the department identified by department_id.
Prerequisite
Spring Tool Suite is required for this example.
Assume Eclipse is the IDE for development. The tool suite is findable in Eclipse Marketplace.
Create a Spring Boot Project
This will add WebFlux dependency to the pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Data Class
public class DptDto {
private Long department_id;
private String department_name;
private Integer manager_id;
/*
* Constructors, Getters, Setters etc.
*/
}
Configuration
As the API provider uses the default port number, this web client will take 9090 when it is launched.
The host hosting REST APIs is defined as a property, which is extracted by the program when creating the SpringWebClient bean.
server.port=9090
rest.host=http://localhost:8080
Instead of hard-coding the API’s paths in the calling methods, we declare a few constants, which are concatenated to form the URIs.
public final class AppConstant {
public static final String REST_ROOT = "/rest";
public static final String API_DPT_FINDALL = "/findalldpts";
public static final String API_DPT_FINDBYID = "/finddptbyid/";
}
WebClient is used to access the endpoint.
We expect findAllDpts() to get us a collection of items, findById() to give us one item. In the meanwhile, we also need to take into account an exceptional case, that is, no item is found.
We use onStatus() to deal with this case. If NOT_FOUND status is received, throw an exception.
.onStatus(status -> status.equals(HttpStatus.NOT_FOUND), clientResponse ->
Mono.error(new Exception("Departments not found")))
Subsequently, at onErrorResume(), return an empty Flux or an empty Mono. As a consequence, you won’t get an error when you block for the content.
.onErrorResume(Exception.class, e -> {return Flux.empty();})
@Component
public class SpringWebClient {
private final WebClient webClient;
public SpringWebClient(
WebClient.Builder builder,
@Value("${rest.host}") String restHost) {
this.webClient = builder.baseUrl(restHost).build();
}
public Flux<DptDto> findAllDpts() {
String uri = AppConstant.REST_ROOT + AppConstant.API_DPT_FINDALL;
return this.webClient
.get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(status -> status.equals(HttpStatus.NOT_FOUND),
clientResponse -> Mono.error(
new Exception("Departments not found")))
.bodyToFlux(DptDto.class)
.onErrorResume(Exception.class,
e -> {return Flux.empty();});
}
public Mono<DptDto> findById(Long department_id) {
String uri = AppConstant.REST_ROOT + AppConstant.API_DPT_FINDBYID
+ Long.toString(department_id);
return this.webClient
.get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(status -> status.equals(HttpStatus.NOT_FOUND),
clientResponse -> Mono.error(
new Exception("Department not found")))
.bodyToMono(DptDto.class)
.onErrorResume(Exception.class,
e -> {return Mono.empty();});
}
}
Get this to work
In the main() method, we use the application context to get the SpringWebClient bean. For a Mono object or a Flux object, we call block() to get the items received from the API provider.
You might have noticed, there is a slight difference between Mono and Flux when calling block() to get the items. For Flux, because a list of elements is expected, we call collectList() before block.
@SpringBootApplication
public class SpringWebClientApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context
= SpringApplication.run(SpringWebClientApplication.class, args);
SpringWebClient webClient = context.getBean(SpringWebClient.class);
System.out.println(">> findById " + webClient.findById((long)30).block());
List<DptDto> dptList = webClient.findAllDpts().collectList().block();
dptList.forEach(dpt -> System.out.println(
">> findAllDpts = " + dpt.toString()));
}
}
Test the Apps
Launch the API provider first. When it is running, start this web client app that we just built. On the console, you'll see the output like this.
>> findById Department[department_id=30, department_name='Compliance', manager_id=300]
>> findAllDpts = Department[department_id=10, department_name='Administration',
manager_id=100]
>> findAllDpts = Department[department_id=20, department_name='Marketing', manager_id=200]
>> findAllDpts = Department[department_id=30, department_name='Compliance', manager_id=300]
>> findAllDpts = Department[department_id=40, department_name='Channel', manager_id=400]
>> findAllDpts = Department[department_id=99, department_name='Dummy', manager_id=0]
Recap
Non-blocking WebClient class is used to consume the REST APIs.
We also demonstrated the difference between handling Mono and Flux.
How to handle the case that no item is found was another point presented in the example.
No comments:
Post a Comment