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

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的错误信息。

次要问题:代码优化点

  1. 不必要的dangerouslySetInnerHTML:该API返回的是纯文本格式的METAR/TAF数据,不需要用dangerouslySetInnerHTML渲染,直接展示文本即可,避免潜在的XSS风险。
  2. 未处理非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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:43:13