Wikipedia API + 跨域请求

31

我正在尝试使用JavaScript和CORS访问维基百科。

据我所知,维基百科应该支持CORS:http://www.mediawiki.org/wiki/API:Cross-site_requests

我尝试了以下脚本:创建一个XMLHttpRequest +凭据/XDomainRequest,添加一些HTTP头(“Access-Control-Allow-Credentials”等),并发送查询。

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();
        }
    };

但是我的脚本失败了('xhr.onerror'被调用)。我该如何修复它?


1
失败的原因是什么?你收到了什么信息?控制台中有错误吗? - JJJ
2
维基百科必须发送CORS标头,而不是您。 - Daniel W.
2
你应该添加真正的错误信息,使用控制台日志。 拒绝设置不安全的标头“Origin”(index):1 XMLHttpRequest无法加载http://en.wikipedia.org/w/api.php?action=opensearch&search=Javascript&format=json。请求的资源上没有“Access-Control-Allow-Origin”标头。因此,源“http://fiddle.jshell.net”无法访问。 - Ronni Skansing
@RonniSkansing 我明白了,谢谢。 - Pierre
被采纳的答案应该是 Ashton Morgan 的答案。 - drodsou
显示剩余2条评论
5个回答

126

我在做一个freeCodeCamp的项目时遇到了同样的问题,解决方法却是如此简单以至于我笑了,因为我已经花费数小时寻找答案。在您的jQuery URL中包含以下参数。

&origin=*

工作的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){ /* ... */ }
);

4
谢谢!我正在做同一个项目,关于Wiki API的CORS问题一度困扰我。当我看到这句话时:“MediaWiki API还要求将来源作为请求参数提供”我没意识到我只需要像你指出的那样操作。不过这似乎有点奇怪,因为通常是提供所请求文件的站点设置允许头,比如:Access-Control-Allow-Origin: <origin> | * - Edson
另外,您能否发布您找到此解决方案的链接? - Edson
4
谢谢!我都快被搞疯了。昨天我还能使用API,今天却一直出现跨域问题。加上这个后,我的请求又可以正常工作了。 - Bullyen
谢谢,事实上我也在做fcc挑战。 - Eleazar Resendez
无法工作 - 请尝试使用以下链接: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

21
自2016年8月起,维基百科通过使用普通的Ajax请求(无需JSONP/回调操作)来支持CORS请求。这可以通过在API调用中设置来源来实现。 对于已认证的请求,必须与“Origin”标头中的一个“origins”完全匹配(需要在进行Ajax调用时使用beforeSend属性进行设置)。 对于未经身份验证的请求,您可以将其简单地设置为星号(*),并且在使用来自您域的简单$.getJSON时会起作用。 示例API调用: https://en.wikipedia.org//w/api.php?action=opensearch&format=json&origin=*&search=stack&limit=10 有关MediaWiki API沙箱的更多信息,请参见: MediaWiki API沙箱

2
如果您在浏览器中执行此示例,则无需 origin,但如果使用 AJAX 执行,则不起作用。 - BishopZ

13

CORS头部是发送给请求脚本的,以允许其访问内容。

维基百科发送CORS,而不是

根据注释:

维基百科是一个例外,要求你在请求的URL中附加一个origin参数。

我认为这背后的原因与缓存有关。我不知道他们使用的是什么机制,但这可能使得他们更容易、更好地存储缓存对象并建立变化。


MediaWiki API文档中有更多关于CORS的内容:

MediaWiki API还要求提供Origin header required所匹配的“origin”请求参数。请注意,此头必须包含在任何预检请求中,因此即使对于POST请求,也应该包含在请求URI的查询字符串部分中。

如果CORS来源检查通过,MediaWiki将在响应中包含Access-Control-Allow-Credentials:true头,因此可以发送身份验证cookie。

这意味着您必须发送一个Origin头来告诉维基百科您来自哪里。维基百科正在管理访问,而不是你。

发送这个来源头:

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

Access-Control-Allow-*头部是响应头部,而不是请求头部。

此外,维基百科还需要内容类型为json

xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");

谢谢你的回答。我更新了我的脚本http://jsfiddle.net/lindenb/Vr7RS/2/并设置了两个头部:但仍然不起作用。 - Pierre
@Pierre,你搞定了吗?我试了一下,但是没成功 :S - Daniel W.
是的,谢谢,我之前使用过这个解决方案:http://lindenb.github.io/pages/pubmedwp/index.html - Pierre
6
注意上述内容中提到的:“应该包含在请求URI的查询字符串部分中”。因此,您不能设置头信息,必须将“&origin=*”添加到实际的URL中。 - Finnjon
1
刚刚花了几天时间搞Apache的CORS配置,却没有意识到这一点。非常感谢你! - Sergio
显示剩余3条评论

4
您可以使用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) { ... }
}

1

在URL中仅添加origin=*并不能解决我的问题,所以我加上了withCredentials=false。这样就可以解决问题了。

如果在添加origin=*后仍然遇到问题的人,请尝试使用下面的方法: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();

感谢 @muraliv


可以运行,但是你反复忘记了请求属性的 this。已编辑。 - Mitya
"muraliv" 是谁?它指的是什么?是一个答案?还是一条评论? - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接