简体   繁体   中英

How to make CORS Request

I'm making a weather app with React.js and I want to make a CORS request for fetching data from weather underground website. What I want is getting a city name, use autocomplete API for finding the city and fetch data for that city.

The problem is, everytime I give a city name (for example: tehran), the xhr.onerror event handler runs and I get this error:

XMLHttpRequest cannot load http://autocomplete.wunderground.com/aq?query=tehran. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.

This is my code for fetching data:

var axios = require('axios');

function createCORSRequest(method, url) {
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr) {
        xhr.open(method, url, true);
    }
    else if (typeof XDomainRequest != "undefined") {
        xhr = new XDomainRequest();
        xhr.open(method, url);
    }
    else {
        xhr = null;
    }
    return xhr;
}

function makeCorsRequest(url) {
    var autoCompleteText;
    var xhr = createCORSRequest('GET', url);
    if (!xhr) {
        alert('CORS not supported');
        return;
    }

    xhr.onload = function() {
        var text = xhr.responseText;
        autoCompleteText = text;
    }
    xhr.onerror = function() {
        alert('Woops, there was an error making the request.');
    }
    xhr.send();
    return autoCompleteText;
}

const WEATHER_UNDERGROUND_AUTOCOMPLETE = 'http://autocomplete.wunderground.com/aq?query=';
const WEATHER_UNDERGROUND_URL = 'http://api.wunderground.com/api/eda52d06d32d71e9/conditions/q/';

module.exports = {
    getTemp: function(city) {
        var encodedCity = encodeURIComponent(city);
        var requestAutoComplete = `${WEATHER_UNDERGROUND_AUTOCOMPLETE}${encodedCity}`;

        var autoCompleteText = makeCorsRequest(requestAutoComplete);
        var foundCity = autoCompleteText.RESULTS[0].name.split(', ');
        var requestUrl = `${WEATHER_UNDERGROUND_URL}${foundCity[1]}/${foundcity[0]}.json`;
        return axios.get(requestUrl).then(function(res) {
            return res.data.current_observation.temp_c;
        }, function(err) {
            throw new Error(res.data.error);
        });
    }
}

Screenshot of the app: localhost:3000/weather page

Here is a simple react component which calls the api with query params and get 's the desired result.

import React, { Component } from 'react'
import axios from 'axios';

export default class App extends Component {

    componentDidMount() {
        axios.get('http://autocomplete.wunderground.com/aq?query=tehran')
            .then((response) => {
                console.log(response);
            })
            .catch((error) => {
                console.log(error);
            })
    }
    render () {
        return (
            <div>React simple starter</div>
        )
    }
}

Because http://autocomplete.wunderground.com/aq?query=tehran doesn't send the Access-Control-Allow-Origin response header, you must change your frontend code to instead make the request through proxy. Do that by changing the WEATHER_UNDERGROUND_AUTOCOMPLETE value:

const WEATHER_UNDERGROUND_AUTOCOMPLETE =
  'https://cors-anywhere.herokuapp.com/http://autocomplete.wunderground.com/aq?query=';

The https://cors-anywhere.herokuapp.com/http://autocomplete.wunderground.com/… URL will cause the request to go to https://cors-anywhere.herokuapp.com , a public CORS proxy which sends the request on to the http://autocomplete.wunderground.com… URL you want.

That proxy gets the response, takes it and adds the Access-Control-Allow-Origin response header to it, and then finally passes that back to your requesting frontend code as the response.

So in the end because the browser sees a response with the Access-Control-Allow-Origin response header, the browser allows your frontend JavaScript code to access the response.

Or use the code from https://github.com/Rob--W/cors-anywhere/ or such to set up your own proxy.

You need a proxy in this case because http://autocomplete.wunderground.com/… itself doesn't send the Access-Control-Allow-Origin response header—and in that case your browser will not allow your frontend JavaScript code to access a response from that server cross-origin.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS has more details.


Incidentally, you can use curl or some other tool to verify that server isn't sending the header:

$ curl -i -H 'Origin: http://localhost:3000' \
    'http://autocomplete.wunderground.com/aq?query=tehran'

HTTP/1.1 200 OK
Content-type: application/json; charset=utf-8
Content-Length: 2232
Connection: keep-alive

{ "RESULTS": [
    {
        "name": "Tehran Dasht, Iran",
        …

Notice there's no Access-Control-Allow-Origin in the response headers there.

Are you bound to using axios? if not I would highly recommend Mozilla's Fetch. To make a cors api call with fetch, do this:

var myInit = { 
    method: 'GET',
    mode: 'cors',
    credentials: 'include'
};

fetch(YOUR_URL, myInit)
.then(function(response) {
    return response.json();
})
.then(function(json) {
    console.log(json)
});

You can learn more here: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

If you are facing issues making CORS request, then use this simple chrome extension (Allow control Allow origin) .

This will let you make CORS request without adding any extra parameter in headers/config .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM