31

I'm trying to access Wikipedia using JavaScript and CORS.

As far as I know, Wikipedia should support CORS: http://www.mediawiki.org/wiki/API:Cross-site_requests

I tried the following script: create a XMLHttpRequest+credential/XDomainRequest, add some HTTP headers ("Access-Control-Allow-Credentials", etc.) and send the query.

http://jsfiddle.net/lindenb/Vr7RS/

var WikipediaCORS=
    {
        setMessage:function(msg)
        {
            var span=document.getElementById("id1");
            span.appendChild(document.createTextNode(msg));
        },
        // Create the XHR object.
        createCORSRequest:function(url)
        {
            var xhr = new XMLHttpRequest();

            if ("withCredentials" in xhr)
            {
                xhr.open("GET", url, true);
            }
            else if (typeof XDomainRequest != "undefined")
            {
                xhr = new XDomainRequest();
                xhr.open(method, url);
            }
            else
            {
                return null;
            }
            xhr.setRequestHeader("Access-Control-Allow-Credentials", "true");
            xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
            return xhr;
        },
        init:function()
        {
            var _this = this;
            var url = 'http://en.wikipedia.org/w/api.php?action=opensearch&search=Javascript&format=json';
            var xhr = this.createCORSRequest(url);
            if (!xhr)
            {
                this.setMessage('CORS not supported');
                return;
            }

            xhr.onload = function()
            {
                _this.setMessage(xhr.responseText);
            };
            xhr.onerror = function()
            {
                _this.setMessage('Woops, there was an error making the request.');
            };
            xhr.send();
        }
    };

But my script fails ('xhr.onerror' is called). How can I fix it?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pierre
  • 34,472
  • 31
  • 113
  • 192
  • 1
    Fails how? What message do you get? Errors in the console? – JJJ May 30 '14 at 10:09
  • the AJAX.onerror function is called "Woops, there was an error making the request." – Pierre May 30 '14 at 10:11
  • 2
    Wikipedia has to send the CORS header, not you. – Daniel W. May 30 '14 at 10:19
  • @DanFromGermany if I remove the calls to "setRequestHeader", it still fails. – Pierre May 30 '14 at 10:21
  • 2
    You should have added the real errors, use the console log. `Refused to set unsafe header "Origin" (index):1 XMLHttpRequest cannot load http://en.wikipedia.org/w/api.php?action=opensearch&search=Javascript&format=json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://fiddle.jshell.net' is therefore not allowed access. ` – Ronni Skansing May 30 '14 at 10:57
  • The accepted answer should be the one of Ashton Morgan – drodsou Feb 19 '17 at 12:46

5 Answers5

123

I had the same problem while working on a freeCodeCamp project and the solution was so simple it made me laugh, since I had spent hours searching for it. In your jQuery URL include the parameter below.

&origin=*

Working CodePen

$.getJSON(
  'https://en.wikipedia.org/w/api.php?action=query&format=json&gsrlimit=15&generator=search' +
  '&origin=*' + // <-- this is the magic ingredient!
  '&gsrsearch='q, function(data){ /* ... */ }
);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
luckyguy73
  • 1,850
  • 2
  • 11
  • 21
  • 4
    Yo, thanks! I'm working the the same project and was in a pickle in regards to CORS with the Wiki API. For some reason when I read this: "The MediaWiki API also requires that the origin be supplied as a request parameter" I didn't realize I just had to do just as you pointed out. It seems kind of weird though since isn't it the site hosting the requested files that usually sets the allow header? Such as: Access-Control-Allow-Origin: | * – Edson Sep 26 '16 at 19:40
  • Also, could you post a link to where you found this solution? – Edson Sep 26 '16 at 19:43
  • 4
    THANK YOU! I was pulling my hair out. I was able to use the API yesterday and today I kept getting the cross origin issue. Adding this allowed my request to work again. – Bullyen Nov 30 '16 at 17:49
  • XMLHttpRequest cannot load https://en.wikipedia.org/w/api.php?action=opensearch&origin=*&format=json&search=xxx. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:2222' is therefore not allowed access. – Lin Du Jul 19 '17 at 02:09
  • Thanks, in fact I'm also doing the fcc challenge – Eleazar Resendez Aug 20 '17 at 13:27
  • Not working - try this: `https://wikipedia.org/w/api.php?action=query&format=json&origin=*&prop=extracts&list=&continue=&titles=&generator=search&callback=&formatversion=1&exsentences=3&exintro=1&explaintext=1&exsectionformat=plain&gsrsearch=javascript` – Selrond Apr 10 '18 at 07:16
21

As of August 2016, Wikipedia supports CORS requests by using the normal Ajax requests (no need of JSONP/callback manipulation). This can be done by setting the origin in the API call.

For authenticated requests, this must match the one of the one of the "origins" in the Origin header exactly (you need to set this using the beforeSend property while making an Ajax call).

For unauthenticated requests, you can simply set it as an asterisk (*), and it works when using a simple $.getJSON from your domain.

Example API call:
https://en.wikipedia.org//w/api.php?action=opensearch&format=json&origin=*&search=stack&limit=10

More is at the MediaWiki API sandbox: MediaWiki API sandbox

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Adeel Siddiqui
  • 676
  • 7
  • 16
13

CORS headers are sent to allow a requesting script to access the contents.

Wikipedia is sending the CORS, not you.

According to the comments:

Wikipedia is an exception to general rule, by requiring you to append an origin parameter to the URL you are requesting.

I think the reason behind this is related to caching. I don't know what kind of mechanism they are using, but it probably makes it easier and better for them to store a cache object and build variations that way.


More on CORS from MediaWiki API documentation:

The MediaWiki API also requires that the origin be supplied as a request parameter, appropriately named "origin", which is matched against the Origin header required by the CORS protocol. Note that this header must be included in any pre-flight request, and so should be included in the query string portion of the request URI even for POST requests.

If the CORS origin check passes, MediaWiki will include the Access-Control-Allow-Credentials: true header in the response, so authentication cookies may be sent.

This means you have to send an Origin header to tell Wikipedia where you are coming from. Wikipedia is managing the access, not you.

Send this origin header:

xhr.setRequestHeader("Origin", "http://www.yourpage.com");

Access-Control-Allow-* headers are response headers, not request headers.

Wikipedia additionally requires content type json:

xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Daniel W.
  • 31,164
  • 13
  • 93
  • 151
  • Thank you for your answer. I updated my script http://jsfiddle.net/lindenb/Vr7RS/2/ and set the two headers: that still doesn't work. – Pierre May 30 '14 at 10:43
  • @Pierre did you get it to work? I tried but didn't :S – Daniel W. May 30 '14 at 11:19
  • no, it didn't , but as far as I understand Ronni Skansing 's comment, en.wikipedia.org doesn't seem to allow CORS. – Pierre May 30 '14 at 12:39
  • You could try with `JSONP` instead, or a serverside solution. – Daniel W. May 30 '14 at 12:43
  • Yes, thanks, I used that solution before: http://lindenb.github.io/pages/pubmedwp/index.html – Pierre May 30 '14 at 13:09
  • 6
    Note in the above: "should be included in the query string portion of the request URI". So you cannot set the header, you must add '&origin=* to the actual url. – Finnjon Oct 04 '16 at 09:02
  • 1
    Just spent a couple of days messing up with Apache's CORS config without realising this. Thank you so very much! – Sergio Nov 11 '16 at 11:05
  • Setting the origin header doesn't work now (maybe things are different in 2019), Chrome is not happy - https://stackoverflow.com/questions/11182712/refused-to-set-unsafe-header-origin-when-using-xmlhttprequest-of-google-chrome. Answer by @ashton-morgan works, however (adding &origin=*). – David Airapetyan May 17 '19 at 01:16
4

You can use jQuery JSONP:

$.ajax( {
    url: "https://en.wikipedia.org/w/api.php",
    jsonp: "callback", 
    dataType: 'jsonp', 
    data: { 
        action: "query", 
        list: "search", 
        srsearch: "javascript", 
        format: "json" 
    },
    xhrFields: { withCredentials: true },
    success: function(response) { ... }
}
Aleksandr
  • 97
  • 1
  • 7
1

Adding origin=* alone with the URL was not working for me so added withCredentials=false. It will work.

Those whoever are facing issue again after adding origin=*: Try the below one with withCredentials = false

var xhttp = new XMLHttpRequest();
var url = "https://en.wikipedia.org/w/api.php?action=opensearch&limit=5&origin=*&search=simple";
xhttp.onreadystatechange = function () {
    if (this.readyState == 4 && this.status == 200) {
        console.log(this.responseText)
    }
};
xhttp.open("GET", url, true);
xhttp.withCredentials = false;
xhttp.setRequestHeader("Content-type", "application/json; charset=utf-8");
xhttp.send();

Credits to @muraliv

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kva
  • 486
  • 4
  • 15