React项目:输入ICAO代码后无法展示METAR/TAF数据求助
问题:React应用无法展示从航空API获取的METAR和TAF数据
我正在开发一个React项目,需求是用户输入航空ICAO机场代码后,从指定接口获取METAR和TAF数据并展示。接口示例为https://aviationweather.gov/api/data/metar?ids=EGLL(其中EGLL为用户输入的代码),但无论如何都无法在网站上展示数据,试过ChatGPT排查但没解决问题,希望有人帮忙检查代码并指出错误。
我的代码如下:
import React, { useState } from 'react'; const Metar = () => { const [metar, setMetar] = useState(''); const [taf, setTaf] = useState(''); const [icaoCode, setIcaoCode] = useState(''); const handleSubmit = (event) => { event.preventDefault(); fetchMetar(icaoCode); fetchTaf(icaoCode); }; const fetchMetar = (code) => { const metarUrl = `https://aviationweather.gov/api/data/metar?ids=${code}`; fetch(metarUrl) .then(response => response.text()) .then(html => { setMetar(html); }) .catch(error => { console.error('Error fetching METAR data:', error); setMetar('Error fetching METAR data.'); }); }; const fetchTaf = (code) => { const tafUrl = `https://aviationweather.gov/api/data/taf?ids=${code}`; fetch(tafUrl) .then(response => response.text()) .then(html => { setTaf(html); }) .catch(error => { console.error('Error fetching TAF data:', error); setTaf('Error fetching TAF data.'); }); }; return ( <section className="bg-gray-100 py-8"> <div className="container mx-auto"> <div className="text-center mb-6"> <h1 className="text-4xl font-bold text-gray-800">METAR and TAF Weather</h1> </div> <div className="bg-white shadow-md rounded p-6"> <form onSubmit={handleSubmit} className="mb-4"> <div className="flex items-center justify-center"> <input className="border border-gray-400 rounded py-2 px-4 mr-4" type="text" value={icaoCode} onChange={(e) => setIcaoCode(e.target.value)} placeholder="ICAO CODE" maxLength="4" pattern="^[a-zA-Z0-9]+$" required /> <button className="bg-blue-500 text-white py-2 px-4 rounded" type="submit"> Click here </button> </div> </form> <div className="text-center"> {metar && <div className="text-lg whitespace-pre-wrap"><strong>METAR:</strong><br /><div dangerouslySetInnerHTML={{ __html: metar }} /></div>} {taf && <div className="text-lg whitespace-pre-wrap"><strong>TAF:</strong><br /><div dangerouslySetInnerHTML={{ __html: taf }} /></div>} </div> </div> </div> </section> ); }; export default Metar;
解决方案
核心问题:CORS跨域限制
你遇到的最主要问题是浏览器同源策略导致的CORS错误。aviationweather.gov的API没有配置允许你的前端域名跨域访问,直接在React项目中用fetch调用该接口会被浏览器拦截。打开浏览器开发者工具的Console面板,应该能看到类似No 'Access-Control-Allow-Origin' header is present on the requested resource的错误信息。
次要问题:代码优化点
- 不必要的
dangerouslySetInnerHTML:该API返回的是纯文本格式的METAR/TAF数据,不需要用dangerouslySetInnerHTML渲染,直接展示文本即可,避免潜在的XSS风险。 - 未处理非200响应:当前代码没有检查HTTP响应状态,即使接口返回404/500等错误,也会尝试解析响应文本,导致错误数据展示。
具体解决步骤
1. 解决CORS问题
推荐以下两种生产环境可用的方案:
方案一:React项目配置代理(适用于Create React App)
在项目根目录的package.json中添加proxy字段,指向API域名:
{ "name": "your-react-app", "proxy": "https://aviationweather.gov", // 其他配置... }
修改fetch的URL,去掉域名部分:
const metarUrl = `/api/data/metar?ids=${code}`; const tafUrl = `/api/data/taf?ids=${code}`;
重启React开发服务器后,请求会自动转发到目标API,绕过CORS限制。
方案二:搭建后端代理服务器
用Node.js/Express搭建简单后端,前端请求自己的后端接口,后端再去调用aviationweather.gov的API(后端不受浏览器同源策略限制)。示例后端代码:
const express = require('express'); const fetch = require('node-fetch'); const app = express(); const port = 3001; app.get('/api/metar', async (req, res) => { const { ids } = req.query; const response = await fetch(`https://aviationweather.gov/api/data/metar?ids=${ids}`); const data = await response.text(); res.send(data); }); app.get('/api/taf', async (req, res) => { const { ids } = req.query; const response = await fetch(`https://aviationweather.gov/api/data/taf?ids=${ids}`); const data = await response.text(); res.send(data); }); app.listen(port, () => { console.log(`Proxy server running on port ${port}`); });
前端修改fetch地址为自己的后端:
const metarUrl = `http://localhost:3001/api/metar?ids=${code}`; const tafUrl = `http://localhost:3001/api/taf?ids=${code}`;
2. 优化前端代码
修改后的代码包含响应状态检查、移除dangerouslySetInnerHTML:
import React, { useState } from 'react'; const Metar = () => { const [metar, setMetar] = useState(''); const [taf, setTaf] = useState(''); const [icaoCode, setIcaoCode] = useState(''); const handleSubmit = (event) => { event.preventDefault(); fetchMetar(icaoCode); fetchTaf(icaoCode); }; const fetchMetar = (code) => { // 根据选择的CORS方案修改URL const metarUrl = `/api/data/metar?ids=${code}`; fetch(metarUrl) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.text(); }) .then(text => { setMetar(text); }) .catch(error => { console.error('Error fetching METAR data:', error); setMetar('Error fetching METAR data.'); }); }; const fetchTaf = (code) => { // 根据选择的CORS方案修改URL const tafUrl = `/api/data/taf?ids=${code}`; fetch(tafUrl) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.text(); }) .then(text => { setTaf(text); }) .catch(error => { console.error('Error fetching TAF data:', error); setTaf('Error fetching TAF data.'); }); }; return ( <section className="bg-gray-100 py-8"> <div className="container mx-auto"> <div className="text-center mb-6"> <h1 className="text-4xl font-bold text-gray-800">METAR and TAF Weather</h1> </div> <div className="bg-white shadow-md rounded p-6"> <form onSubmit={handleSubmit} className="mb-4"> <div className="flex items-center justify-center"> <input className="border border-gray-400 rounded py-2 px-4 mr-4" type="text" value={icaoCode} onChange={(e) => setIcaoCode(e.target.value)} placeholder="ICAO CODE" maxLength="4" pattern="^[a-zA-Z0-9]+$" required /> <button className="bg-blue-500 text-white py-2 px-4 rounded" type="submit"> Get Weather </button> </div> </form> <div className="text-center"> {metar && ( <div className="text-lg whitespace-pre-wrap"> <strong>METAR:</strong><br />{metar} </div> )} {taf && ( <div className="text-lg whitespace-pre-wrap mt-4"> <strong>TAF:</strong><br />{taf} </div> )} </div> </div> </div> </section> ); }; export default Metar;
内容的提问来源于stack exchange,提问作者OrangeTiger
相关产品推荐
相关产品推荐

