Home > Net >  ReactJS CORS header ‘Access-Control-Allow-Origin’ missing
ReactJS CORS header ‘Access-Control-Allow-Origin’ missing

Time:09-17

I'm using a 3rd party API https://www.metaweather.com and in my package.json i've added
"proxy": "https://www.metaweather.com",

My app.js is as follows:

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;

my 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;

my 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;

The ExtraWeather.jsx isn't relevant.

If I comment out the MainWeather and log the return from the LocationSearch it returns to object perfectly but as soon as I introduce the MainWeather back I get "CORS header ‘Access-Control-Allow-Origin’ missing" error. I've tried everything I can find from hosting the app on Netlify, changing what is the proxy to the local host address, moving things to different places, and I'm unsure if I did it correctly but I did try a reverse proxy.

Also using herokuapp and a browser extension does fix the problem but I want something more permanent.

Any help will be greatly appreciated.

CodePudding user response:

The issue is that the response is being redirected to include a / suffix, ie

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

This causes your browser to re-attempt the request to that URL which bypasses your proxy.

Try including the / suffix, eg

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

Keep in mind that the proxy setting only works for local development. If you're deploying to Netlify, see https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service


Debugging Process

Something was directing your browser to try and access the API by its full URL so I suspected a redirect.

I just ran

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

and looked at the response status and headers...

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

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

Spotting the difference was the hard part

  • Related