Issue
I have a springboot project which uses Springboot Resttemplate. We have moved to springboot 2.0.1 from 1.5.3 and we are trying to make the rest calls from it asynchronous by using WebClient. We used to process the string received using Resttemplate as given below. But WebClient returns only data in Mono or Flux. How can I get the data as String. Already tried block() method , but it does asynchronous calls.
@Retryable(maxAttempts = 4, value = java.net.ConnectException.class,
backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
return webClient.get().uri(urlForCurrent).accept(APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class);
}
Present Data flow with RestTemplate
- Controller receives the client call
- provider gets the data in String format
- Provider processes the String
- Data is given to controller
Controller.java
@RequestMapping(value = traffic/, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public String getTraffic(@RequestParam("place") String location) throws InterruptedException, ExecutionException {
String trafficJSON = Provider.getTrafficJSON(location)
return trafficJSON;
}
Provider.java
public String getTrafficJSON(String location) {
String url = ----;
ResponseEntity<String> response = dataFetcher.getResponse(url);
/// RESPONSEBODY IS READ AS STRING AND IT NEEDS TO BE PROCESSED
if (null != response {
return parser.transformJSON(response.getBody(), params);
}
return null;
}
DataFetcher.java
@Retryable(maxAttempts = 4,
value = java.net.ConnectException.class,
backoff = @Backoff(delay = 3000, multiplier = 2))
public ResponseEntity<String> getResponse(String url) {
/* ----------------------- */
return getRestTemplate().getForEntity(urlForCurrent, String.class);
}
Solution
Due to the fact that there are lot of misconception, so here I'm going to clear up some things.
Spring has officially stated that RestTemplate
is in maintenence mode
so if you can, use WebClient
if you want to be as future proof as possible.
as stated in the RestTemplate API
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the
org.springframework.web.reactive.client.WebClient
which has a more modern API and supports sync, async, and streaming scenarios.
Non reactive application
If your application is a non-reactive application (not returning fluxes or monos to the calling clients) what you have to do is to use block()
if you need the value. You can of course use Mono
or Flux
internally in your application but in the end you must call block()
to get the concrete value that you need to return to the calling client.
Non reactive applications use for instance tomcat
, undertow
as the underlying server implementation, which follows the servlet specification so it will assign 1 thread per request so you will not gain the performance gains you get with a reactive application.
Reactive application
If you on the other hand you have a reactive application you should never under any circumstances ever call block()
in your application. Blocking is exactly what it says, it will block a thread and block that threads execution until it can move on, this is bad in a reactive world.
You should also not call subscribe
in your application unless your application is the final consumer of the response. For instance, if you are calling an api to get data and write into a database that your application is connected to. Your backend application is the final consumer. If an external client is calling your backend (for instance an react, angular app, mobile client, etc. etc.) the external client is the final consumer, and is the one subscribing. Not you.
Underlying default server implementation here is a netty
server which is a non servlet, event based server that will not assign one thread to each request, the server itself is thread agnostic and any thread available will handle anything at any time during any request.
The webflux documentation clearly states that both servlet 3.1+ supported servers tomcat and jetty can be used with webflux as well as non-serlet servers netty and undertow.
How do i know what application i have?
Spring states that if you have both spring-web
and spring-webflux
on the classpath, the application will favor spring-web
and per default start up a non-reactive application with an underlying tomcat server.
This behaviour can manually be overridden if needed as spring states.
Adding both
spring-boot-starter-web
andspring-boot-starter-webflux
modules in your application results in Spring Boot auto-configuring Spring MVC, not WebFlux. This behavior has been chosen because many Spring developers addspring-boot-starter-webflux
to their Spring MVC application to use the reactive WebClient. You can still enforce your choice by setting the chosen application type toSpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)
.
The “Spring WebFlux Framework”
So how to implement WebClient in accordance to code provided by the question?
@Retryable(maxAttempts = 4,
value = java.net.ConnectException.class,
backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
return webClient.get()
.uri(url)
.exchange()
.flatMap(response -> response.toEntity(String.class));
}
I would say this is the easiest and the most less intrusive implementation. You of course need to build a proper webclient in maybe a @Bean
and autowire it into its class.
Answered By - Toerktumlare
Answer Checked By - Mildred Charles (JavaFixing Admin)