ReactJS CORS header 'Access-Control-Allow-Origin' 缺失

ReactJS CORS header ‘Access-Control-Allow-Origin’ missing

我正在使用第三方 API https://www.metaweather.com 并在我的 package.json 中添加了
"proxy": "https://www.metaweather.com",

我的app.js如下:

import { createContext, useState } from "react";
import LocationSearch from "./components/locationSearch";
import MainWeather from "./components/mainWeather";
import ExtraWeather from "./components/ExtraWeather";

export const DisplayContext = createContext({
  display: false,
  setDisplay: () => {},
});

function App() {
  const [woeid, setWoeid] = useState(null);
  const [display, setDisplay] = useState(false);

  return (
    <DisplayContext.Provider value={{ display, setDisplay }}>
      <LocationSearch setWoeid={setWoeid} />
      <MainWeather woeid={woeid} />
      <ExtraWeather />
    </DisplayContext.Provider>
  );
}

export default App;

我的LocationSearch.jsx:

import React, { useContext, useState } from "react";
import axios from "axios";
import { DisplayContext } from "../App";

const LocationSearch = ({ setWoeid }) => {
  const [data, setData] = useState({
    location: "",
  });
  const { setDisplay } = useContext(DisplayContext);

  function submit(e) {
    e.preventDefault();
    axios
      .get(
        // "https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/search/?query=" +
          "/api/location/search/?query=" +
          data.location,
        {
          location: data.location,
        }
      )
      .then((res) => {
        console.log(res.data[0].woeid);
        setWoeid(res.data[0].woeid);
        setTimeout(() => setDisplay(true), 5000);
      })
      .catch((err) => {
        console.log(err);
      });
  }

  function handle(e) {
    const newdata = { ...data };
    newdata[e.target.id] = e.target.value;
    setData(newdata);
    console.log(newdata);
  }

  return (
    <div className="flex w-96 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
      <form className="flex w-96 mx-auto p-3 rounded-xl bg-white">
        <div>
          <input
            className="text-gray-700"
            onChange={(e) => handle(e)}
            id="location"
            value={data.location}
            placeholder="Search for location"
            type="text"
          />
          <button
            className="bg-blue-900 text-gray-300 py-3 px-5 ml-12 rounded-xl"
            type="submit"
            onClick={(e) => submit(e)}
          >
            Search
          </button>
        </div>
      </form>
    </div>
  );
};

export default LocationSearch;

我的MainWeather.jsx:

import React, { useContext, useEffect, useState } from "react";
import axios from "axios";
import { DisplayContext } from "../App";
import Loader from "react-loader-spinner";

const MainWeather = ({ woeid }) => {
  const [temp, setTemp] = useState([]);
  const [icon, setIcon] = useState("");
  const { display } = useContext(DisplayContext);
  const [load, setLoad] = useState(false);

  useEffect(() => {
    axios
      .get(
        // "https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/" +
          "/api/location/" +
          woeid
      )
      .then((res) => {
        setLoad(true);
        console.log(res.data[0]);
        setIcon(res.data.consolidated_weather[0].weather_state_abbr);
        setTemp((prev) => {
          return [
            ...prev,
            res.data.consolidated_weather[0].the_temp,
            res.data.consolidated_weather[0].min_temp,
            res.data.consolidated_weather[0].max_temp,
            res.data.consolidated_weather[0].weather_state_name,
          ];
        });
      })
      .catch((err) => {
        console.log(err);
      });
  }, [woeid]);

  return (
    <>
      {display && (
        <div className="w-96 flex flex-col mx-auto p-3 mt-2 rounded-xl bg-blue-300">
          <img
            src={"/static/img/weather/" + icon + ".svg"}
            alt="Current weather icon"
            className="w-40 mx-auto pb-4"
          />
          <p className="mx-auto text-5xl pb-3">{Math.round(temp[0])}°C</p>
          <p className="mx-auto pb-1">
            {Math.round(temp[1])} / {Math.round(temp[2])}
          </p>
          <p className="mx-auto pb-2">{temp[3]}</p>
        </div>
      )}

      {!display && (
        <div>
          {load && (
            <div className="flex w-96 h-80 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
              <Loader
                className="m-auto"
                type="Puff"
                color="#00BFFF"
                height={100}
                width={100}
                timeout={5000}
              />
            </div>
          )}

          {!load && (
            <div className="flex w-96 h-80 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
              <h1 className="m-auto">Please enter a location</h1>
            </div>
          )}
        </div>
      )}
    </>
  );
};

export default MainWeather;

ExtraWeather.jsx 不相关。

如果我注释掉 MainWeather 并从 LocationSearch 记录 return 它 returns 到 object 完美但一旦我介绍MainWeather 返回“CORS header 'Access-Control-Allow-Origin' 丢失”错误。我已经尝试了所有我能找到的在 Netlify 上托管应用程序、将代理更改为本地主机地址、将内容移动到不同位置的所有方法,我不确定我是否正确地进行了操作,但我确实尝试了反向代理。

也使用 herokuapp 和浏览器扩展确实解决了这个问题,但我想要更永久的东西。

任何帮助将不胜感激。

问题是响应被重定向以包含 / 后缀,即

HTTP/2 301
location: https://www.metaweather.com/api/location/44418/

这会导致您的浏览器 re-attempt 绕过您的代理 URL 的请求。

尝试包含 / 后缀,例如

axios.get(`/api/location/${woeid}/`)

请记住,proxy 设置仅适用于本地开发。如果您要部署到 Netlify,请参阅 https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service


调试过程

有些东西正在引导您的浏览器尝试通过完整 URL 访问 API,所以我怀疑是重定向。

我只是运行

curl -v "https://www.metaweather.com/api/location/44418" -o /dev/null

并查看响应状态和 headers...

> GET /api/location/44418 HTTP/2
> Host: www.metaweather.com

< HTTP/2 301
< location: https://www.metaweather.com/api/location/44418/

发现差异是困难的部分

您可能已经在浏览器 dev-tools 网络 面板中看到了类似的内容;首先向 /api/location/44418 发送 301 响应请求和 location header,然后向 https://www.metaweather.com/api/location/44418/ 发送未通过 CORS 检查的请求