当前位置: 首页 > 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.lryc.cn/news/609868.html

相关文章:

  • Linux(centos)安全狗
  • 【芯片设计专用执行单元:PWM如何重塑能源与智能控制】
  • sqli-labs靶场less29~less35
  • 2025.08.04 移除元素
  • 【测试工程思考】测试自动化基础能力建设
  • 使用mybatis生成器生成实体类mapper和查询参数文件,实现简单增删改查。使用log4j输出日志到控制台。使用配置文件注册Bean,配置视图解析器
  • 每天学一个Linux命令(38):vi/vim
  • Excel商业智能分析报表 【销售管理分析仪】
  • 免费MCP: JSON 转 Excel MCP
  • Vim 高效使用指南
  • Flutter 事件总线 Event Bus
  • HarmonyOS 多屏适配最佳实践:基于 ArkUI 的响应式 UI 方案
  • 计算机网络:理解路由的下一跳
  • 第四十六章:AI的“瞬时记忆”与“高效聚焦”:llama.cpp的KV Cache与Attention机制
  • C++ 中 initializer_list 类型推导
  • 2.1 vue组件
  • 【AMD】编译llama.cpp实践版
  • 【异常案例分析】使用空指针调用函数(非虚函数)时,没有崩溃在函数调用处,而是崩在被调用函数内部
  • 关于大数据的基础知识(三)——数据安全与合规
  • C++模板知识点3『std::initializer_list初始化时逗号表达式的执行顺序』
  • 1.kotlin数组、集合常用方法
  • 【iOS】渲染原理离屏渲染
  • 网络安全 | 从 0 到 1 了解 WAF:Web 应用防火墙到底是什么?
  • 国内首个开源SCA社区——OpenSCA开源社区
  • C++临时对象:来源与性能优化之道
  • 前后端流式交互的几种方式
  • Petalinux快捷下载
  • 【笔记】ROS1|2 Turtlebot3汉堡Burger连接和远程控制【旧文转载】
  • 【SpringAI】SpringAI的介绍与简单使用
  • 算力板卡:驱动智能时代的核心引擎