如何使用 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。但真正的问题是,在 MonoFlux 上调用 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();
}

}