如何使用 Spring WebClient 解析来自外部 API 的特定数据?
How to use a Spring WebClient to parse specific data from an external API?
我正在尝试使用 Spring WebFlux WebClient 从 openweathermap API 检索数据。我已经使用 RestTemplate 完成了这个没问题,但想了解执行此操作的正确方法。本质上,我从 openweathermap API 接收到 JSON,如下所示:
{
"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
}
根据这些数据,我只需要字符串、天气对象或 json 对象中的名称、温度和感觉温度。我试图通过以下方式做到这一点:
@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;
}
不过,最终这会导致天气对象的所有空值。我可以用数据打印到控制台,但没有别的。我是反应式程序和 lambda 的新手,所以我肯定有一些误解。
如果您正在使用 Spring 启动,您应该 use the WebClient.Builder
component that's pre-configured for you。但真正的问题是,在 Mono
或 Flux
上调用 subscribe
会启动一个可能在未来解决的新异步任务。到您的服务 returns 值时,它尚未填充,因为异步工作仍在进行中。
如果你想把一个响应式类型变成命令式值,你可以使用block()
。您的代码片段可能如下所示:
@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();
}
}
我正在尝试使用 Spring WebFlux WebClient 从 openweathermap API 检索数据。我已经使用 RestTemplate 完成了这个没问题,但想了解执行此操作的正确方法。本质上,我从 openweathermap API 接收到 JSON,如下所示:
{
"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
}
根据这些数据,我只需要字符串、天气对象或 json 对象中的名称、温度和感觉温度。我试图通过以下方式做到这一点:
@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;
}
不过,最终这会导致天气对象的所有空值。我可以用数据打印到控制台,但没有别的。我是反应式程序和 lambda 的新手,所以我肯定有一些误解。
如果您正在使用 Spring 启动,您应该 use the WebClient.Builder
component that's pre-configured for you。但真正的问题是,在 Mono
或 Flux
上调用 subscribe
会启动一个可能在未来解决的新异步任务。到您的服务 returns 值时,它尚未填充,因为异步工作仍在进行中。
如果你想把一个响应式类型变成命令式值,你可以使用block()
。您的代码片段可能如下所示:
@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();
}
}