'How to use a Spring WebClient to parse specific data from an external API?
I'm attempting to use the Spring WebFlux WebClient to retrieve data from the openweathermap API. I've done this using a RestTemplate no problem, but want to understand the proper way to do this. Essentially, I'm receiving JSON from the openweathermap API which looks as follows:
{
"coord": {
"lon": -122.08,
"lat": 37.39
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}
],
"base": "stations",
"main": {
"temp": 282.55,
"feels_like": 281.86,
"temp_min": 280.37,
"temp_max": 284.26,
"pressure": 1023,
"humidity": 100
},
"visibility": 10000,
"wind": {
"speed": 1.5,
"deg": 350
},
"clouds": {
"all": 1
},
"dt": 1560350645,
"sys": {
"type": 1,
"id": 5122,
"message": 0.0139,
"country": "US",
"sunrise": 1560343627,
"sunset": 1560396563
},
"timezone": -25200,
"id": 420006353,
"name": "Mountain View",
"cod": 200
}
From that data, all I need is the name, temperature, and feels like temperature in either a string, weather object, or json object. I've tried to do this in the following manner:
@Service
public class WeatherService {
private final String APIKEY = "###";
private final WebClient webClient;
public WeatherService() {
this.webClient = WebClient.create("http://api.openweathermap.org/data/2.5/weather");
}
public OptumWeather getWeatherByZipCode(String zipCode){
ObjectMapper obj = new ObjectMapper();
OptumWeather weather = new OptumWeather();
webClient
.get()
.uri(uriBuilder -> uriBuilder
.queryParam("zip", zipCode)
.queryParam("units", "imperial")
.queryParam("appid", APIKEY)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class)
.subscribe(m -> {
try {
FullWeather weatherFull = obj.readValue(m, FullWeather.class);
weather.setName(weatherFull.getName());
weather.setTemp(weatherFull.getMain().getTemp());
weather.setFeelsLikeTemp(weatherFull.getMain().getFeelsLike());
} catch (IOException e) {
e.printStackTrace();
}
});
return weather;
}
Ultimately, though, this results in all null values for the weather object. I can print to the console just fine with the data but nothing else. I'm new to reactive program and lambda's so there is definitely some misunderstanding on my part.
Solution 1:[1]
If you're using Spring Boot, you should use the WebClient.Builder
component that's pre-configured for you. But the real problem is, calling subscribe
on a Mono
or Flux
kicks off a new asynchronous task that might be resolved in the future. By the time your service returns the value, it's not been filled yet as the asynchronous work is still ongoing.
If you want to turn a reactive type into an imperative value, you can use block()
. Your code snippet could look like:
@Service
public class WeatherService {
private final String APIKEY = "###";
private final WebClient webClient;
public WeatherService(WebClient.Builder builder) {
this.webClient = builder.baseUrl("http://api.openweathermap.org/data/2.5/weather").build();
}
public OptumWeather getWeatherByZipCode(String zipCode){
return webClient
.get()
.uri(uriBuilder -> uriBuilder
.queryParam("zip", zipCode)
.queryParam("units", "imperial")
.queryParam("appid", APIKEY)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(FullWeather.class)
.map(weatherFull -> {
OptumWeather weather = new OptumWeather();
weather.setName(weatherFull.getName());
weather.setTemp(weatherFull.getMain().getTemp());
weather.setFeelsLikeTemp(weatherFull.getMain().getFeelsLike());
return weather;
})
.block();
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |