基于 Spring Boot 的天气预报服务

2017 年 10 月 25 日 ImportNew

(点击上方公众号,可快速关注)


来源:Way Lau,

waylau.com/spring-boot-weather-report/

如有好文章投稿,请点击 → 这里了解详情


本文,我们将基于 Spring Boot 技术来实现一个微服务天气预报服务接口——micro-weather-basic。micro-weather-basic 的作用是实现简单的天气预报功能,可以根据不同的城市,查询该城市的实时天气情况。


开发环境


  • Gradle 4.0

  • Spring Boot 1.5.6

  • Apache HttpClient 1.5.3


数据来源


理论上,天气的数据是天气预报的实现基础。本应用与实际的天气数据无关,理论上,可以兼容多种数据来源。但为求简单,我们在网上找了一个免费、可用的天气数据接口。


天气数据来源为中华万年历。例如:


  • 通过城市名字获得天气数据 :http://wthrcdn.etouch.cn/weather_mini?city=深圳

  • 通过城市id获得天气数据:http://wthrcdn.etouch.cn/weather_mini?citykey=101280601


城市ID列表。每个城市都有一个唯一的ID作为标识。见 http://cj.weather.com.cn/support/Detail.aspx?id=51837fba1b35fe0f8411b6df 或者 http://mobile.weather.com.cn/js/citylist.xml。


调用天气服务接口示例,我们以“深圳”城市为例,可用看到如下天气数据返回。


{

    "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)。其原理,我也整理到我的博客中了:


  • https://waylau.com/change-gradle-wrapper-distribution-url-to-local-file/

  • https://waylau.com/use-maven-mirrors/


项目配置


添加 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 :


  • http://localhost:8080/weather/cityId/101280601

  • http://localhost:8080/weather/cityName/惠州


能看到如下的数据返回



源码


本章节的源码,见 https://github.com/waylau/spring-cloud-tutorial/ samples目录下的micro-weather-basic。


看完本文有收获?请转发分享给更多人

关注「ImportNew」,看技术干货

登录查看更多
0

相关内容

DATE:Design, Automation & Test in Europe。 Explanation:欧洲的设计、自动化和测试。 Publisher:IEEE/ACM。 SIT: http://dblp.uni-trier.de/db/conf/date/
专知会员服务
128+阅读 · 2020年7月10日
商业数据分析,39页ppt
专知会员服务
157+阅读 · 2020年6月2日
【ICMR2020】持续健康状态接口事件检索
专知会员服务
17+阅读 · 2020年4月18日
【SIGMOD2020-腾讯】Web规模本体可扩展构建
专知会员服务
29+阅读 · 2020年4月12日
《代码整洁之道》:5大基本要点
专知会员服务
49+阅读 · 2020年3月3日
携程用ClickHouse轻松玩转每天十亿级数据更新
DBAplus社群
11+阅读 · 2019年8月6日
交通评价指标概略
智能交通技术
15+阅读 · 2019年7月21日
搭建自己的searx搜索引擎
黑白之道
13+阅读 · 2019年5月31日
出行即服务(MAAS)框架
智能交通技术
53+阅读 · 2019年5月22日
【专题】Facebook遭德国反垄断调查及其影响分析
蚂蚁金服评论
17+阅读 · 2019年4月1日
网络舆情分析
计算机与网络安全
18+阅读 · 2018年10月18日
python帅到没朋友的填充软件
运维帮
4+阅读 · 2018年5月30日
爬了自己的微信,原来好友都是这样的!
七月在线实验室
4+阅读 · 2018年1月18日
如何用Python做舆情时间序列可视化?
CocoaChina
11+阅读 · 2017年7月21日
Arxiv
24+阅读 · 2020年3月11日
A Modern Introduction to Online Learning
Arxiv
19+阅读 · 2019年12月31日
Arxiv
5+阅读 · 2018年5月22日
VIP会员
相关VIP内容
专知会员服务
128+阅读 · 2020年7月10日
商业数据分析,39页ppt
专知会员服务
157+阅读 · 2020年6月2日
【ICMR2020】持续健康状态接口事件检索
专知会员服务
17+阅读 · 2020年4月18日
【SIGMOD2020-腾讯】Web规模本体可扩展构建
专知会员服务
29+阅读 · 2020年4月12日
《代码整洁之道》:5大基本要点
专知会员服务
49+阅读 · 2020年3月3日
相关资讯
携程用ClickHouse轻松玩转每天十亿级数据更新
DBAplus社群
11+阅读 · 2019年8月6日
交通评价指标概略
智能交通技术
15+阅读 · 2019年7月21日
搭建自己的searx搜索引擎
黑白之道
13+阅读 · 2019年5月31日
出行即服务(MAAS)框架
智能交通技术
53+阅读 · 2019年5月22日
【专题】Facebook遭德国反垄断调查及其影响分析
蚂蚁金服评论
17+阅读 · 2019年4月1日
网络舆情分析
计算机与网络安全
18+阅读 · 2018年10月18日
python帅到没朋友的填充软件
运维帮
4+阅读 · 2018年5月30日
爬了自己的微信,原来好友都是这样的!
七月在线实验室
4+阅读 · 2018年1月18日
如何用Python做舆情时间序列可视化?
CocoaChina
11+阅读 · 2017年7月21日
Top
微信扫码咨询专知VIP会员