1. 程式人生 > >基於Spring Boot的天氣預報服務

基於Spring Boot的天氣預報服務

本文,我們將基於 Spring Boot 技術來實現一個微服務天氣預報服務介面——micro-weather-basic。micro-weather-basic 的作用是實現簡單的天氣預報功能,可以根據不同的城市,查詢該城市的實時天氣情況。

開發環境

  • Gradle 4.0
  • Spring Boot 1.5.6
  • Apache HttpClient 1.5.3

資料來源

理論上,天氣的資料是天氣預報的實現基礎。本應用與實際的天氣資料無關,理論上,可以相容多種資料來源。但為求簡單,我們在網上找了一個免費、可用的天氣資料介面。

呼叫天氣服務介面示例,我們以“深圳”城市為例,可用看到如下天氣資料返回。

{
    "data
": { "yesterday": { "date": "1日星期五", "high": "高溫 33℃", "fx": "無持續風向", "low": "低溫 26℃", "fl": "<![CDATA[<3級]]>", "type": "多雲" }, "city": "深圳", "aqi": "72", "forecast": [ { "date
": "2日星期六", "high": "高溫 32℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 26℃", "fengxiang": "無持續風向", "type": "陣雨" }, { "date": "3日星期天", "high": "高溫 29℃", "fengli": "<![CDATA[5-6級]]>", "low": "低溫 26℃", "fengxiang": "無持續風向", "type": "大雨" }, { "date": "4日星期一", "high": "高溫 29℃", "fengli": "<![CDATA[3-4級]]>", "low": "低溫 26℃", "fengxiang": "西南風", "type": "暴雨" }, { "date": "5日星期二", "high": "高溫 31℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 27℃", "fengxiang": "無持續風向", "type": "陣雨" }, { "date": "6日星期三", "high": "高溫 32℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 27℃", "fengxiang": "無持續風向", "type": "陣雨" } ]
, "ganmao": "風較大,陰冷潮溼,較易發生感冒,體質較弱的朋友請注意適當防護。", "wendu": "29" }
, "status": 1000, "desc": "OK" }

我們通過觀察資料,來了解每個返回欄位的含義。

  • “city”: 城市名稱
  • “aqi”: 空氣指數,
  • “wendu”: 實時溫度
  • “date”: 日期,包含未來5天
  • “high”:最高溫度
  • “low”: 最低溫度
  • “fengli”: 風力
  • “fengxiang”: 風向
  • “type”: 天氣型別

以上資料,是我們需要的天氣資料的核心資料,但是,同時也要關注下面兩個欄位:

  • “status”: 介面呼叫的返回狀態,返回值“1000”,意味著資料是介面正常
  • “desc”: 介面狀態的描述,“OK”代表介面正常

重點關注返回值不是“1000”的情況,說明,這個介面呼叫異常了。

初始化一個 Spring Boot 專案

初始化一個 Spring Boot 專案 micro-weather-basic,該專案可以直接在我們之前章節課程中的 basic-gradle 專案基礎進行修改。同時,為了優化專案的構建速度,我們對Maven中央倉庫地址和 Gradle Wrapper 地址做了調整。其中細節暫且不表,讀者可以自行參閱原始碼,或者學習筆者所著的《Spring Boot 教程》(https://github.com/waylau/spring-boot-tutorial)。其原理,我也整理到我的部落格中了:

專案配置

新增 Apache HttpClient 的依賴,來作為我們Web請求的客戶端。

// 依賴關係
dependencies {
    //...

    // 新增  Apache HttpClient 依賴
    compile('org.apache.httpcomponents:httpclient:4.5.3')

    //...
}

建立天氣資訊相關的值物件

建立com.waylau.spring.cloud.vo包,用於相關值物件。建立天氣資訊類 Weather

public class Weather implements Serializable {

    private static final long serialVersionUID = 1L;

    private String city;
    private String aqi;
    private String wendu;
    private String ganmao;
    private Yesterday yesterday;
    private List<Forecast> forecast;

    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getAqi() {
        return aqi;
    }
    public void setAqi(String aqi) {
        this.aqi = aqi;
    }
    public String getWendu() {
        return wendu;
    }
    public void setWendu(String wendu) {
        this.wendu = wendu;
    }
    public String getGanmao() {
        return ganmao;
    }
    public void setGanmao(String ganmao) {
        this.ganmao = ganmao;
    }
    public Yesterday getYesterday() {
        return yesterday;
    }
    public void setYesterday(Yesterday yesterday) {
        this.yesterday = yesterday;
    }
    public List<Forecast> getForecast() {
        return forecast;
    }
    public void setForecast(List<Forecast> forecast) {
        this.forecast = forecast;
    }
}

昨日天氣資訊:

public class Yesterday implements Serializable {

    private static final long serialVersionUID = 1L;

    private String date;
    private String high;
    private String fx;
    private String low;
    private String fl;
    private String type;

    public Yesterday() {

    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getHigh() {
        return high;
    }

    public void setHigh(String high) {
        this.high = high;
    }

    public String getFx() {
        return fx;
    }

    public void setFx(String fx) {
        this.fx = fx;
    }

    public String getLow() {
        return low;
    }

    public void setLow(String low) {
        this.low = low;
    }

    public String getFl() {
        return fl;
    }

    public void setFl(String fl) {
        this.fl = fl;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

}

未來天氣資訊:

public class Forecast implements Serializable {

    private static final long serialVersionUID = 1L;

    private String date;
    private String high;
    private String fengxiang;
    private String low;
    private String fengli;
    private String type;

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getHigh() {
        return high;
    }

    public void setHigh(String high) {
        this.high = high;
    }

    public String getFengxiang() {
        return fengxiang;
    }

    public void setFengxiang(String fengxiang) {
        this.fengxiang = fengxiang;
    }

    public String getLow() {
        return low;
    }

    public void setLow(String low) {
        this.low = low;
    }

    public String getFengli() {
        return fengli;
    }

    public void setFengli(String fengli) {
        this.fengli = fengli;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Forecast() {

    }

}

WeatherResponse 作為整個訊息的返回物件

public class WeatherResponse implements Serializable {

    private static final long serialVersionUID = 1L;

    private Weather data; // 訊息資料
    private String status; // 訊息狀態
    private String desc; // 訊息描述

    public Weather getData() {
        return data;
    }

    public void setData(Weather data) {
        this.data = data;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

}

服務介面及實現

定義了獲取服務的兩個介面方法

public interface WeatherDataService {

    /**
     * 根據城市ID查詢天氣資料
     * @param cityId
     * @return
     */
    WeatherResponse getDataByCityId(String cityId);

    /**
     * 根據城市名稱查詢天氣資料
     * @param cityId
     * @return
     */
    WeatherResponse getDataByCityName(String cityName);
}

其實現為:

@Service
public class WeatherDataServiceImpl implements WeatherDataService {

    @Autowired
    private RestTemplate restTemplate;

    private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";

    @Override
    public WeatherResponse getDataByCityId(String cityId) {
        String uri = WEATHER_API + "?citykey=" + cityId;
        return this.doGetWeatherData(uri);
    }

    @Override
    public WeatherResponse getDataByCityName(String cityName) {
        String uri = WEATHER_API + "?city=" + cityName;
        return this.doGetWeatherData(uri);
    }

    private WeatherResponse doGetWeatherData(String uri) {
        ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
        String strBody = null;

        if (response.getStatusCodeValue() == 200) {
            strBody = response.getBody();
        }

        ObjectMapper mapper = new ObjectMapper();
        WeatherResponse weather = null;

        try {
            weather = mapper.readValue(strBody, WeatherResponse.class);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return weather;
    }

}

返回的天氣資訊採用了 Jackson 來進行反序列化成為 WeatherResponse 物件。

控制器層

控制器層暴露了RESTful API 地址。

@RestController
@RequestMapping("/weather") 
public class WeatherController {

    @Autowired
    private WeatherDataService weatherDataService;

    @GetMapping("/cityId/{cityId}")
    public WeatherResponse getReportByCityId(@PathVariable("cityId") String cityId) {
        return weatherDataService.getDataByCityId(cityId);
    }

    @GetMapping("/cityName/{cityName}")
    public WeatherResponse getReportByCityName(@PathVariable("cityName") String cityName) {
        return weatherDataService.getDataByCityName(cityName);
    }

}

@RestController自動會將返回的資料,序列化成 JSON資料格式。

配置類

RestConfiguration 是 RestTemplate 的配置類。

@Configuration
public class RestConfiguration {

    @Autowired  
    private RestTemplateBuilder builder;  

    @Bean  
    public RestTemplate restTemplate() {  
        return builder.build();  
    }

}

訪問API

執行專案之後,訪問專案的 API :

能看到如下的資料返回

weather-data

原始碼

本章節的原始碼,見 https://github.com/waylau/spring-cloud-tutorial/ samples目錄下的micro-weather-basic

參考引用