天气应用UI更新异常求助:首次点击报错,后续操作失效
天气应用开发问题排查与修复
应用流程说明
开发一款天气应用,逻辑如下:用户从下拉菜单选择城市后,前端通过fetch发送POST请求将城市名传递至服务器;服务器使用Axios向OpenWeatherMap API发送GET请求获取天气数据,再通过GET请求返回给前端更新UI。
当前遇到的问题
- 首次点击提交按钮时,控制台报两个错误:
GET http://localhost:3000/fulldata 404 (Not Found)和Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON - 再次点击提交按钮,UI可正常更新
- 选择其他城市后点击提交,无任何反应,控制台与终端均无新错误
- 给
sendCityToServer调用添加await后,第二次点击UI也无法更新,且无报错
前端代码
// Global Variables const SERVER_URL = 'http://localhost:3000/' const COUNTRIES_NAMES_API = 'https://restcountries.com/v3.1/all' // Create a new date instance dynamically with JS let d = new Date().toDateString() // HTML Selectors const generate = document.querySelector('#generate') const date = document.querySelector('.date') const temp = document.querySelector('.temperature') const weatherCondition = document.querySelector('.weatherCondition') const country = document.querySelector('.place') const weatherIcon = document.querySelector('.wi') const countriesNames = async () => { const select = document.querySelector('#countries') const countriesList = await (await fetch(COUNTRIES_NAMES_API)).json() countriesList.splice(38, 1) countriesList.forEach(country => { const option = document.createElement('option') if (country.capital) { option.innerText = `${country.capital[0]}` select.append(option) } }) } // Getting data from API const APIdata = async () => { const citySelector = document.querySelector('#countries').value sendCityToServer(`${SERVER_URL}postcityselector`, { city: citySelector }) // API Keys const data = await (await fetch(`${SERVER_URL}fulldata`)).json() // Update UI with API data weatherIcon.src = `http://openweathermap.org/img/wn/${data.weather[0].icon}@4x.png` date.innerText = date temp.innerText = Math.floor(data.main.temp) + '°C' weatherCondition.innerText = data.weather[0].description country.innerText = data.name } // Update UI when button is clicked generate.addEventListener('click', APIdata) async function sendCityToServer(url = '', data = {}) { const response = fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) } countriesNames()
服务器代码(已省略express、body parser、cors和dotenv的初始化代码)
const apiKeys = { URL: process.env.BASE_URL, KEY: process.env.API_KEY, } weatherData = {} app.post('/postcityselector', req => { axios.get(apiKeys.URL + req.body.city + apiKeys.KEY).then(response => { app.get('/fulldata', (req, res) => { res.send(response.data) }) }) })
问题根源与修复方案
1. 后端路由注册逻辑错误
当前后端在/postcityselector的POST请求回调里动态注册/fulldata的GET路由,导致:
- 首次请求时
/fulldata路由未注册,返回404(对应首次点击的错误) - 第二次请求时路由已注册,能返回数据
- 后续更换城市时,重复注册相同路由,Express仅保留第一个路由逻辑,返回旧数据导致UI无更新
修复后服务器代码:
const apiKeys = { URL: process.env.BASE_URL, KEY: process.env.API_KEY, } // 全局存储天气数据 let weatherData = {} // 处理城市提交的POST请求 app.post('/postcityselector', async (req, res) => { try { const city = req.body.city const response = await axios.get(`${apiKeys.URL}${city}${apiKeys.KEY}`) // 更新全局天气数据 weatherData = response.data // 给前端返回成功响应 res.status(200).json({ message: '数据已更新' }) } catch (error) { res.status(500).json({ error: '获取天气数据失败' }) } }) // 独立的GET路由返回天气数据 app.get('/fulldata', (req, res) => { if (Object.keys(weatherData).length === 0) { return res.status(404).json({ error: '暂无天气数据,请先选择城市' }) } res.send(weatherData) })
2. 前端请求时序与函数完善问题
前端调用sendCityToServer后立刻发起/fulldata请求,此时后端可能未完成OpenWeatherMap API请求,导致拿到旧数据或404;且sendCityToServer未等待fetch完成,也未处理响应。
修复后前端关键代码:
// Getting data from API const APIdata = async () => { const citySelector = document.querySelector('#countries').value // 等待POST请求完成 await sendCityToServer(`${SERVER_URL}postcityselector`, { city: citySelector }) // 获取更新后的天气数据 const data = await (await fetch(`${SERVER_URL}fulldata`)).json() // Update UI with API data weatherIcon.src = `http://openweathermap.org/img/wn/${data.weather[0].icon}@4x.png` date.innerText = d // 修复原代码中将DOM元素赋值给自身的错误 temp.innerText = Math.floor(data.main.temp) + '°C' weatherCondition.innerText = data.weather[0].description country.innerText = data.name } async function sendCityToServer(url = '', data = {}) { // 等待fetch完成并返回响应 const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) // 处理请求失败情况 if (!response.ok) { throw new Error('POST请求失败') } return await response.json() }
3. 前端日期赋值错误修复
原代码中date.innerText = date是将DOM元素赋值给自身文本内容,改为使用之前定义的日期变量d。
内容的提问来源于stack exchange,提问作者Omar Magdy
相关产品推荐
相关产品推荐

