在Express.js天气应用中,如何将https请求获取的lat/lon值代入wURL?
解决方案
核心问题拆解
- 请求时序错误:你同时发起了地理编码和天气请求,但天气请求完全依赖前者返回的经纬度,并行执行会导致天气请求拿到的参数是空值。
- 变量作用域问题:
lat和lon是在地理请求的回调内部定义的局部变量,外部提前定义的wURL根本访问不到这些值。 - 未处理异常场景:如果输入的城市不存在,直接访问
city[0].lat会触发报错,导致服务器崩溃。
修改后的完整代码
const express = require('express'); const https = require('node:https'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.get('/', function(req, res) { res.sendFile(__dirname + "/index.html"); }); app.post('/', function(req, res) { const query = req.body.cityName; const apiKey = "904f8e391a422fdf63b802aef88950c8"; const units = "metric"; const gURL = `https://api.openweathermap.org/geo/1.0/direct?q=${query}&limit=2&appid=${apiKey}`; // 先发起地理编码请求,拿到经纬度后再触发天气请求 https.get(gURL, function(response) { let geoData = ''; // 处理分块返回的数据,避免JSON解析失败 response.on('data', function(chunk) { geoData += chunk; }); response.on('end', function() { try { const city = JSON.parse(geoData); // 处理城市不存在的情况 if (!city || city.length === 0) { return res.send('<h1>未找到指定城市,请重新输入</h1>'); } const lat = city[0].lat; const lon = city[0].lon; // 拿到经纬度后再构建天气请求URL const wURL = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}&units=${units}`; // 发起天气请求 https.get(wURL, function(weatherResponse) { let weatherData = ''; weatherResponse.on('data', function(chunk) { weatherData += chunk; }); weatherResponse.on('end', function() { const climate = JSON.parse(weatherData); const temp = climate.main.temp; const location = climate.name; const description = climate.weather[0].description; const icon = climate.weather[0].icon; const imgURL = `http://openweathermap.org/img/wn/${icon}@2x.png`; res.write(`<h1>The temperature in ${location} is ${temp} degC.</h1>`); res.write(`<p>The weather is currently ${description}</p>`); res.write(`<img src="${imgURL}">`); res.send(); }); }).on('error', function(err) { res.send(`<h1>获取天气数据失败:${err.message}</h1>`); }); } catch (err) { res.send(`<h1>解析地理数据失败:${err.message}</h1>`); } }); }).on('error', function(err) { res.send(`<h1>获取地理数据失败:${err.message}</h1>`); }); }); app.listen(3000, function() { console.log("Server is running on port:3000"); })
关键修改说明
- 调整请求顺序:将天气请求嵌套在地理请求的
end回调中,确保只有拿到有效经纬度后才发起天气请求。 - 处理分块数据:原生
https.get的data事件可能分多次返回数据,拼接后再解析JSON,避免因部分数据导致解析错误。 - 添加异常处理:覆盖了城市不存在、请求失败、JSON解析失败等场景,防止服务器崩溃。
- 优化URL写法:用模板字符串替代字符串拼接,提升代码可读性和可维护性。
- 修正作用域:
lat和lon在地理请求完成后定义,此时可直接用于构建天气请求URL。
进阶优化(可选)
如果觉得嵌套回调不够清晰,可以用Promise封装请求,再用async/await简化代码:
// 封装https.get为Promise function fetchData(url) { return new Promise((resolve, reject) => { https.get(url, (response) => { let data = ''; response.on('data', (chunk) => data += chunk); response.on('end', () => resolve(data)); }).on('error', reject); }); } // 修改post处理函数为async app.post('/', async function(req, res) { try { const query = req.body.cityName; const apiKey = "904f8e391a422fdf63b802aef88950c8"; const units = "metric"; const gURL = `https://api.openweathermap.org/geo/1.0/direct?q=${query}&limit=2&appid=${apiKey}`; const geoData = await fetchData(gURL); const city = JSON.parse(geoData); if (!city || city.length === 0) { return res.send('<h1>未找到指定城市,请重新输入</h1>'); } const lat = city[0].lat; const lon = city[0].lon; const wURL = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}&units=${units}`; const weatherData = await fetchData(wURL); const climate = JSON.parse(weatherData); const temp = climate.main.temp; const location = climate.name; const description = climate.weather[0].description; const icon = climate.weather[0].icon; const imgURL = `http://openweathermap.org/img/wn/${icon}@2x.png`; res.write(`<h1>The temperature in ${location} is ${temp} degC.</h1>`); res.write(`<p>The weather is currently ${description}</p>`); res.write(`<img src="${imgURL}">`); res.send(); } catch (err) { res.send(`<h1>请求失败:${err.message}</h1>`); } });
内容的提问来源于stack exchange,提问作者Kirthi Bagrecha
相关产品推荐
相关产品推荐

