You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

天气应用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 11:05:22