当前位置: 首页 > news >正文

【项目实践】在系统接入天气api,根据当前天气提醒,做好plan

前期调研

经多方查找,发现和风天气对于个人开发人相对有好,有详细的api接口文档以及实现案例。

优点:

  • 只需要提供城市、授权ID和Key,就会进行返回 跟天气相关的多样态数据;
  • 响应数据种类多,提供各类svg图标样式,后续扩展性可以延伸;

详情查看官网

  • 和风天气开发文档
  • 和风天气图标

准备工作

注册和风天气账号

  1. 注册时需要准备邮箱,手机账号
  2. 验证码注意检查下垃圾邮件,有可能被默认规则拦截

  1. 添加项目,这里就开始注册需要使用 的api了

  1. 注册项目

  1. 创建完成后会显示这个,下面的凭据 点击;
  2. api Key 就是我们需要用的APIKey
  3. 详细设置参照官方文档进行:

开发配置

开始实行

导入依赖

  1. 返回JSON数据,所以数据格式转换是必须的,需要jaskson
  2. 和风天气 API响应的数据使用Gzip压缩,正常显示失败,需要设置 HttpComponentsClientHttpRequestFactory ,为了解决 API 响应压缩导致的解析问题,确保能够正确处理和风天气 API 返回的可能经过 gzip 压缩的 JSON 数据。
        <!-- Jackson(解析JSON) --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.4.2</version></dependency><!-- HttpClient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency>

bean注入需要扫描的包

  1. 我将所有的外置工具都放到utility中;
  2. 配置RestTemplate(用于发送HTTP请求)
    <!-- 扫描Controller和Service所在的包 --><context:component-scan base-package="com.utility"/><!-- 配置RestTemplate(用于发送HTTP请求) --><bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>

目录如下:

配置秘钥

public class WeatherConfig {// 和风天气API配置public static final String API_URL = "https://devapi.qweather.com/v7/weather/now";public static final String API_KEY = "你的密钥"; // 替换为实际密钥public static final String CITY_ID = "101010100"; // 北京
}

编写实体类

  1. 返回显示的数据,应答代码所需数据
// 顶层响应
public class WeatherResponse {private String code;private Now now;// getter + setter
}
// 当前天气详情
public class Now {private String temp;private String text;private String windDir;private String humidity;// getter + setter
}

编写逻辑层代码

  1. 调用和风天气API
  2. 处理数据,符合URL规范,参考官方文档进行
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.weather.config.WeatherConfig;@Service
public class WeatherService {@Autowiredprivate RestTemplate restTemplate;// 获取当前天气public WeatherResponse getCurrentWeather() {// 拼接请求URLString url = String.format("%s?location=%s&key=%s",WeatherConfig.API_URL,WeatherConfig.CITY_ID,WeatherConfig.API_KEY);// 调用API并返回结果return restTemplate.getForObject(url, WeatherResponse.class);}
}

控制层代码

  1. 返回视图解析器,返回页面显示详情信息
  2. 返回ajax,动态加载页面,在页面小地方加载内容
@Controller
@RequestMapping("/weather")
public class WeatherController {@Autowiredprivate WeatherService weatherService;@Autowiredprivate IpParseService ipParseService;// 响应JSON格式的天气数据@GetMapping("/current/json")@ResponseBody // 表示返回JSON而非视图public WeatherResponse getCurrentWeather(HttpServletRequest request) {String city = ipParseService.getCityByIp(IpUtils.getRealIp(request));WeatherResponse weather = weatherService.getCurrentWeather(city);weather.setLocation(city);return weather;}@GetMapping("/current")public String getCurrentWeather(HttpServletRequest request, Model mode) {String city = ipParseService.getCityByIp(IpUtils.getRealIp(request));WeatherResponse weather = weatherService.getCurrentWeather(city);weather.setLocation(city);mode.addAttribute("weather", weather);return "weather";}

View层视图显示

  1. 动态加载的 使用ajax
       $.ajax({url: "/weather/current/json",type: "GET",dataType: "json",success: function (data) {console.log("天气数据:", data);if (data && data.now !== null) {//     $("#weather_info").text(data.now.text);$("#weather-info").html("天气:" + data.now.text);}},error: function (xhr, status, error) {console.log("获取天气数据失败:", error);$("#weather-info").html("天气:获取失败");}})
  1. 视图跳转,显示视图的内容

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head><title>当前天气</title><style>.weather-container {width: 500px;margin: 50px auto;padding: 20px;border: 1px solid #ddd;border-radius: 10px;text-align: center;}.weather-info {margin: 20px 0;font-size: 18px;}.temp {font-size: 36px;color: #2c3e50;margin: 10px 0;}.text {font-size: 24px;color: #3498db;}</style>
</head>
<body>
<div class="weather-container"><h2>当前天气信息</h2><!-- 显示天气数据 --><c:if test="${not empty weather}"><div class="weather-info"><div class="text">${weather.location}</div><div class="text">${weather.now.text}</div><div class="temp">${weather.now.temp} ℃</div><div>风向:${weather.now.windDir}</div><div>湿度:${weather.now.humidity}%</div></div></c:if><!-- 错误提示 --><c:if test="${empty weather or weather.code != '200'}"><div style="color: red;">获取天气失败,请稍后重试</div></c:if>
</div>
</body>
</html>

优化

优化点1:根据登录ip判断所在地,自动使用所在地地点显示天气

  1. 工具调查,需要使用获取地址的api工具
  2. 需要用到api: [https://ip.taobao.com/service/getIpInfo.php](https://ip.taobao.com/service/getIpInfo.php),现在已经永久废止了,使用[https://ip9.com.cn/get?ip=[IP](https://ip9.com.cn/get?ip=[IP)地址]与前面一样,都是会返回JSON格式
控制器

Controller 改动原来的就可以,毕竟是为了保障以后

逻辑层
  1. 所需数据少,需要变动点少,索性放在一个里面,引入lombok,省下写getter/setter;
  2. 城市 是需要请求天气所需要的东西,如果进行变动的话需要考虑进去
  3. 请求响应失败的场合 需要返回个默认地址
package com.utility.service;import com.utility.config.WeatherConfig;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class CityCodeService {// 和风天气城市搜索API  curl "https://nj59fbrfwr.re.qweatherapi.com/geo/v2/city/lookup?location=北京&key=key***"private static final String CITY_SEARCH_URL = WeatherConfig.API_URL + "/geo/v2/city/lookup";//    @Value("${weather.api.key}")//    private String apiKey;@Autowiredprivate RestTemplate restTemplate;// 根据城市名获取城市ID(如“北京”→“101010100”)public String getCityIdByCityName(String cityName) {try {// 拼接请求URLString url = String.format("%s?location=%s&key=%s",CITY_SEARCH_URL, cityName, WeatherConfig.API_KEY);System.out.println(" 和风天气城市搜索API::" + url);// 调用API并解析结果CitySearchResponse response = restTemplate.getForObject(url, CitySearchResponse.class);if (response != null && "200".equals(response.getCode())&& response.getLocation() != null && !response.getLocation().isEmpty()) {return response.getLocation().get(0).getId(); // 返回第一个匹配的城市ID}} catch (Exception e) {e.printStackTrace();}// 失败时返回默认城市ID : 北京return "101010100";}@Data// 城市搜索API返回的实体类(简化版)public static class CitySearchResponse {private String code; // 200表示成功private java.util.List<Location> location;}@Datapublic static class Location {private String id; // 城市ID}
}
  1. taobao的废止了,使用了ip9的接口去用;IpParseService返回一个城市name,这个nameCityCodeService需要使用,我们使用这个去 查找城市代码;
  2. Test 成功,使用junit测试通过,使用的百度在北京的服务器所在的ip地址;使用单元测试的思想,可以避免我们不停的重复启停服务所花费时间,来回启动会导致 IDE工具 内存不足;
package com.utility.service;import lombok.Data;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class IpParseService {// 淘宝IP解析接口
//    private static final String IP_API_URL = "https://ip.taobao.com/service/getIpInfo.php?ip=";private static final String IP_API_URL = "https://ip9.com.cn/get?ip=";@Autowiredprivate RestTemplate restTemplate;// 根据IP获取城市名称(如“北京”)public String getCityByIp(String ip) {try {// 调用淘宝IP接口String url = IP_API_URL + ip;// 获取原始响应作为字符串
//            String responseString = restTemplate.getForObject(url, String.class);IpResponse response = restTemplate.getForObject(url, IpResponse.class);// 手动解析JSON(需要引入Jackson或其他JSON库)
//            ObjectMapper objectMapper = new ObjectMapper();
//            IpResponse response = objectMapper.readValue(responseString, IpResponse.class);// 解析返回结果(淘宝接口格式特殊,需对应实体类)if (response != null && response.getCode() == 0) {if (response.getData() != null && !response.getData().getCity().isEmpty()) {return response.getData().getCity();}}} catch (Exception e) {e.printStackTrace();}// 解析失败时返回默认城市return "山东";}@Data// 淘宝IP接口返回的实体类(简化版)public static class IpResponse {private int code; // 0表示成功private IpData data;}@Datapublic static class IpData {private String city; // 城市名称}@Testpublic void test() {RestTemplate restTemplate = new RestTemplate();String IP = "182.61.200.108";System.out.println(getCityByIp(IP));}
}
工具类
  1. 解析Request中ip地址
package com.utility.utils;import javax.servlet.http.HttpServletRequest;public class IpUtils {// 获取用户真实IP(考虑代理情况)public static String getRealIp(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}// 多代理情况下,取第一个IPif (ip != null && ip.contains(",")) {ip = ip.split(",")[0].trim();}return ip;}
}

优化点2:地址拆出来,方便以后使用,这样就可以一次请求存在Session中使用,节省api请求次数,提高效率

控制器

Controller 改动原来的就可以,毕竟是为了保障以后

逻辑层

原来功能:

public WeatherResponse getCurrentWeather(HttpServletRequest request) {String cityId =
cityCodeService
.getCityIdByCityName(ipParseService.getCityByIp(IpUtils.getRealIp(request)));}

分析可拆分点,IP转化日期单独拿出,后面再单独去调用;

public WeatherResponse getCurrentWeather(String city) {String cityId = cityCodeService.getCityIdByCityName(city);
}
实体类

需求发生变化,页面内容就要变更,取值的方式就多种多样了,改之前的Entity,返回的ResponseVo中加个字段? 再新写个实体类? 把IpParseServiceIpData提取出来?思路各色各样

人世间万事万物都是变得,唯唯物辩证法永恒。

http://www.dtcms.com/a/315347.html

相关文章:

  • C语言的控制语句
  • 16day-人工智学习-机器学习-特征工程
  • 【世纪龙科技】虚拟技术助力职教汽车自动变速器拆装虚拟实训软件
  • RFID技术在汽车倍速链中的应用:驱动智能制造的隐形引擎
  • Windows/Linux入侵排查
  • CPP学习之多态
  • Python高频元素分析技术:高效找出序列中出现次数最多的元素
  • 【Unity3D实例-功能-镜头】第三人称视觉
  • FeiQ飞秋安装教程:FeiQ.1060559168 详细安装步骤(附注意事项)​
  • 【QT】常⽤控件详解(三)常用按钮控件PushButton RadioButton CheckButton Tool Button
  • 茗鹤工业低代码可视化技术开发平台
  • 网络相关命令
  • 全国计算机二级C语言二级考试通关笔记
  • 风光储并网协同运行simulink仿真模型实现
  • [找出字符串中第一个匹配项的下标]
  • MiDSS复现
  • Codeforces Round 1010 (Div. 2, Unrated)
  • 8.4IO进程线程——进程
  • MySQL 基本操作入门指南
  • 代码随想录day55图论5
  • 通往L4之路:构建自我进化的智能驾驶决策大脑
  • Dubbo 3.x源码(32)—Dubbo Provider处理服务调用请求源码
  • CSS 安卓应用卸载碎片化动画效果
  • pyqt5-tools/pyqt6-tools 安装失败,解决办法
  • 【秋招笔试】2025.08.03虾皮秋招笔试-第三题
  • 7.2 I/O接口 (答案见原书 P305)
  • 大模型部署、nvidia-smi、token数
  • Java项目:基于SSM框架实现的商铺租赁管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告+任务书+远程部署】
  • pytorch 学习笔记3-利用框架内网络训练糖尿病数据集
  • Linux 使用 firewalld :开放端口与常用操作指南