Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a Spring 5 WebClient response in a non-blocking way?

I'm using Spring WebFlux WebClient to retrieve data from an external API, like this:

public WeatherWebClient() {
    this.weatherWebClient = WebClient.create("http://api.openweathermap.org/data/2.5/weather");
}

public Mono<String> getWeatherByCityName(String cityName) {
    return weatherWebClient
            .get()
            .uri(uriBuilder -> uriBuilder
                                .queryParam("q", cityName)
                                .queryParam("units", "metric")
                                .queryParam("appid", API_KEY)
                                .build())
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(String.class);
}

This works fine and produces a response like this:

{
    "coord":{
        "lon":-47.06,
        "lat":-22.91
    },
    "weather":[
    {
        "id":800,
        "main":"Clear",
        "description":"clear sky",
        "icon":"01d"
    }
    ],
    "base":"stations",
    "main":{
        "temp":16,
        "pressure":1020,
        "humidity":67,
        "temp_min":16,
        "temp_max":16
    },
    "visibility":10000,
    "wind":{
        "speed":1,
        "deg":90
    },
    "clouds":{
        "all":0
    },
    "dt":1527937200,
    "sys":{
        "type":1,
        "id":4521,
        "message":0.0038,
        "country":"BR",
        "sunrise":1527932532,
        "sunset":1527971422
    },
    "id":3467865,
    "name":"Campinas",
    "cod":200
}

But I'm only interested in the "temp" property (main -> temp). How could I transform the response (using Jackson's ObjectMapper, for example) to return only "temp" value in a reactive/non-blocking way?

I understand the first thing is replacing ".retrieve()" by ".exchange()" but I can't figure out how to make it work.

PS: This is my first question here. Please let me know if I'm doing something wrong or if you need more details.

Thanks!

like image 735
Michel Nagme Avatar asked Jun 02 '18 17:06

Michel Nagme


People also ask

Is Spring WebClient non-blocking?

WebClient Non-Blocking Client. On the other side, WebClient uses an asynchronous, non-blocking solution provided by the Spring Reactive framework. While RestTemplate uses the caller thread for each event (HTTP call), WebClient will create something like a “task” for each event.

How do you get an object from mono without blocking?

A non-blocking way would be via one of the overloaded subscribe() methods. In this example, we will use the subscribe(Consumer<? super T> consumer) to get the data from Mono asynchronously. With subscribe(), the current thread will not be blocked waiting for the Publisher to emit data.

How do I get data from mono WebClient?

Mono is a reactive publisher, that can emit 0 zero or 1 elements. Thus, in order to retrieve a single JSON resource with WebClient, we should use Mono publisher. We will use WebClient to read a JSON object and parse it into POJO. Example of WebClient reading single JSON Object as a POJO with Mono.


1 Answers

You need to create a type that corresponds to the response sent by the server. A very minimal example could be like this:

@JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherResponse {
    public MainWeatherData main;
}

and the MainWeatherData class could be:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MainWeatherData {
    public String temp;
}

Finally, you could use WeatherResponse in bodyToMono:

...
   .retrieve()
   .bodyToMono(WeatherResponse.class);

The @JsonIgnoreProperties(ignoreUnknown = true)annotation instructs Jackson to not give any errors if it encounters any value in JSON string that is not present in you POJO.

You can access the WeatherResponseobject with a chained map operator:

getWeatherByCityName(cityName)
     .map(weatherResponse -> weatherResponse.main.temp)  
like image 113
MuratOzkan Avatar answered Oct 15 '22 11:10

MuratOzkan