使用AJAX加载跨域终端点

138
我正在尝试使用AJAX加载一个跨域的HTML页面,但除非dataType为"jsonp",否则我无法得到响应。但是,使用jsonp时,浏览器期望脚本mime类型,但接收到的是"text/html"。

我的请求代码如下:

$.ajax({
    type: "GET",
    url: "http://saskatchewan.univ-ubs.fr:8080/SASStoredProcess/do?_username=DARTIES3-2012&_password=P@ssw0rd&_program=%2FUtilisateurs%2FDARTIES3-2012%2FMon+dossier%2Fanalyse_dc&annee=2012&ind=V&_action=execute",
    dataType: "jsonp",
}).success( function( data ) {
    $( 'div.ajax-field' ).html( data );
});

有没有避免使用jsonp请求的方法?我已经尝试使用crossDomain参数,但它没有起作用。

如果没有,有没有以jsonp方式接收html内容的方法?目前控制台显示"jsonp回复中出现意外的<"。


1
我已经通过创建一个proxy.php并按照此处所述的步骤 https://scode7.blogspot.com/2019/11/how-to-fix-ajax-no-access-control-allow.html 解决了这个问题。 - CodeDezk
谢谢CodeDezk,我根据你提供的链接创建了自己的PHP代理来处理跨域AJAX请求。这非常容易。 - GTS Joe
9个回答

242

jQuery Ajax笔记

  • 由于浏览器安全限制,大多数Ajax请求受到同源策略的限制;请求无法成功地从不同的域、子域、端口或协议检索数据。
  • 脚本和JSONP请求不受同源策略的限制。

有一些方法可以克服跨域障碍:

有一些插件可以帮助处理跨域请求:

注意!

克服这个问题的最好方法是在后端创建自己的代理,使您的代理指向其他域中的服务,因为在后端不存在同源策略限制。但如果您无法在后端执行此操作,则请注意以下提示。


使用第三方代理不是一个安全的做法,因为他们可以追踪您的数据,因此它可以与公共信息一起使用,但绝不能与私人数据一起使用。
下面展示的代码示例使用jQuery.get()jQuery.getJSON(),它们都是jQuery.ajax()的简写方法。请注意,本文保留HTML标签。

CORS Anywhere

2021更新

公开演示服务器(cors-anywhere.herokuapp.com)将在2021年1月31日之前受到非常严格的限制

CORS Anywhere(cors-anywhere.herokuapp.com)的演示服务器旨在展示该项目。但是,由于滥用行为已经如此普遍,演示服务器所在平台(Heroku)已要求我关闭服务器,尽管我们已经努力对抗滥用行为。由于滥用和其流行度,宕机变得越来越频繁。

为了对抗这个问题,我将做出以下更改:

  1. 速率限制将从每小时200个减少到每小时50个。
  2. 在2021年1月31日之前,cors-anywhere.herokuapp.com将停止作为开放代理服务器。
  3. 从2021年2月1日开始,cors-anywhere.herokuapp.com仅在访问者完成挑战后才会提供服务:用户(开发人员)必须访问cors-anywhere.herokuapp.com上的页面,以暂时为其浏览器解锁演示。这使开发人员可以尝试功能,以帮助决定自主托管或寻找替代方案。

CORS Anywhere是一个node.js代理程序,它会在代理请求中添加CORS头信息。
要使用该API,请在URL前加上API URL。(支持https:请参阅github repository
如果您想在需要时自动启用跨域请求,请使用以下代码片段:
$.ajaxPrefilter( function (options) {
  if (options.crossDomain && jQuery.support.cors) {
    var http = (window.location.protocol === 'http:' ? 'http:' : 'https:');
    options.url = http + '//cors-anywhere.herokuapp.com/' + options.url;
    //options.url = "http://cors.corsproxy.io/url=" + options.url;
  }
});

$.get(
    'http://en.wikipedia.org/wiki/Cross-origin_resource_sharing',
    function (response) {
        console.log("> ", response);
        $("#viewer").html(response);
});

无论何源

无论何源是一种跨域jsonp访问方式。这是anyorigin.com的一个开源替代品。

要从google.com获取数据,您可以使用此片段:

// It is good specify the charset you expect.
// You can use the charset you want instead of utf-8.
// See details for scriptCharset and contentType options: 
// http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
$.ajaxSetup({
    scriptCharset: "utf-8", //or "ISO-8859-1"
    contentType: "application/json; charset=utf-8"
});

$.getJSON('http://whateverorigin.org/get?url=' + 
    encodeURIComponent('http://google.com') + '&callback=?',
    function (data) {
        console.log("> ", data);

        //If the expected response is text/plain
        $("#viewer").html(data.contents);

        //If the expected response is JSON
        //var response = $.parseJSON(data.contents);
});

CORS代理

CORS代理是一个简单的node.js代理,可用于启用任何网站的CORS请求。 它允许您网站上的JavaScript代码访问其他域上的资源,否则将由于同源策略而被阻止。

它是如何工作的? CORS代理利用了与HTML 5一起添加的跨源资源共享功能。服务器可以指定他们希望浏览器允许其他网站请求他们托管的资源。CORS代理只是一个HTTP代理,它在响应中添加一个标题,表明“任何人都可以请求此资源”。

这是另一种实现目标的方法(请参见www.corsproxy.com)。你所要做的就是从被代理的URL中删除http://www.,并在URL前加上www.corsproxy.com/
$.get(
    'http://www.corsproxy.com/' +
    'en.wikipedia.org/wiki/Cross-origin_resource_sharing',
    function (response) {
        console.log("> ", response);
        $("#viewer").html(response);
});


现在看起来 http://www.corsproxy.com/ 域名是一个不安全/可疑的网站。不建议使用。


CORS代理浏览器

最近我发现了这个工具,它涉及各种安全导向的跨源远程共享实用程序。但它是一个带有Flash后端的黑盒子。

你可以在这里看到它的运行效果:CORS代理浏览器
在GitHub上获取源代码:koto/cors-proxy-browser


4
您可以从这里部署您自己的WhateverOrigin.org版本(或移植代码以供您自己使用):https://github.com/ripper234/Whatever-Origin/ - EpicVoyage
1
图片、CSS和外部JavaScript可以从另一个来源引用,因此,在响应中,您可以遍历HTML字符串并替换外部资源的src。 - jherax
1
你好@Miru,正如标题所说:“使用jQuery AJAX加载跨域HTML页面”,我通过提供一些使用代理执行跨域请求的示例来回答标题。此外,针对问题的措辞,我提供了一些链接,以使用YQL使用JSONP进行跨域请求。我邀请您阅读这些链接,它们非常有用。 - jherax
1
最终使用了CORS Anywhere方法和$.ajaxPrefilter,效果非常好。非常感谢! - Joshua Pinter
1
不要使用CORS代理 - 现在看起来像是一个病毒网站。 - asheroto
显示剩余9条评论

25

你可以使用Ajax-cross-origin这个jQuery插件。使用该插件,你可以通过jQuery.ajax()实现跨域请求。它使用Google服务来实现:

AJAX Cross Origin插件使用Google Apps Script作为代理jSON获取器,在jSONP未被实现时使用。当你将crossOrigin选项设置为true时,该插件会将原始URL替换为Google Apps Script地址,并将其作为编码后的URL参数发送。Google Apps Script使用Google服务器资源获取远程数据,并将其作为JSONP返回给客户端。

它非常容易使用:

    $.ajax({
        crossOrigin: true,
        url: url,
        success: function(data) {
            console.log(data);
        }
    });

您可以在此处阅读更多信息:http://www.ajax-cross-origin.com/


25
就我而言,这个插件从来没有起过作用,在Chrome上它什么也做不了。 - Michael
我该如何向服务器进行身份验证? - Syed Ali
非常好!我使用的API既不支持JSONP也不支持CORS,所以这是唯一有效的方法。非常感谢! - JP Lew
jQuery的crossOrigin选项肯定不能缓解同源策略。如果可以的话,我会删除这个答案。 - Phil

13
如果外部网站不支持JSONP或CORS,您唯一的选择是使用代理。
在您的服务器上构建一个脚本来请求该内容,然后使用jQuery ajax命中您服务器上的脚本。

7

只需将以下内容放置在您的 PHP 页面头部,即可无需 API 进行工作:

header('Access-Control-Allow-Origin: *'); //allow everybody  

或者

header('Access-Control-Allow-Origin: http://codesheet.org'); //allow just one domain 

或者
$http_origin = $_SERVER['HTTP_ORIGIN'];  //allow multiple domains

$allowed_domains = array(
  'http://codesheet.org',
  'http://stackoverflow.com'
);

if (in_array($http_origin, $allowed_domains))
{  
    header("Access-Control-Allow-Origin: $http_origin");
}

我想知道 $_SERVER['HTTP_ORIGIN'] 是从哪里来的。我在 PHP 文档或其他任何地方都找不到它。 - Zsolti
嗯,看起来它只有通过AJAX请求填充。无论如何,感谢您的回答。 - Zsolti

0

如果按照jherax的建议,通过使用本地代理来获取外部站点的数据,您可以创建一个PHP页面,从相应的外部URL中获取内容,然后向该PHP页面发送一个GET请求。

var req = new XMLHttpRequest();
req.open('GET', 'http://localhost/get_url_content.php',false);
if(req.status == 200) {
   alert(req.responseText);
}

作为 PHP 代理,您可以使用 https://github.com/cowboy/php-simple-proxy

0

你的URL最近无法使用,但是你可以使用这个可行的解决方案来更新你的代码:

var url = "http://saskatchewan.univ-ubs.fr:8080/SASStoredProcess/do?_username=DARTIES3-2012&_password=P@ssw0rd&_program=%2FUtilisateurs%2FDARTIES3-2012%2FMon+dossier%2Fanalyse_dc&annee=2012&ind=V&_action=execute";

url = 'https://google.com'; // TEST URL

$.get("https://images"+~~(Math.random()*33)+"-focus-opensocial.googleusercontent.com/gadgets/proxy?container=none&url=" + encodeURI(url), function(data) {
    $('div.ajax-field').html(data);
});
<div class="ajax-field"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


0
我发表这篇文章是为了帮助那些遇到和我一样问题的人。我有一台装备了ZebraNet打印服务器的Zebra热敏打印机,它提供了基于HTML的用户界面,用于编辑多个设置、查看打印机的当前状态等。我需要获取打印机的状态,该状态显示在ZebraNet服务器提供的其中一个html页面中,并且例如,在浏览器中alert()一个消息给用户。这意味着我首先必须在Javascript中获取那个html页面。虽然打印机在用户PC的局域网内,但Same Origin Policy仍然阻碍着我的道路。我尝试过JSONP,但服务器返回的是html,我还没有找到修改其功能的方法(如果我能,我已经设置了魔术头Access-control-allow-origin:*)。所以我决定编写一个小型控制台应用程序来解决这个问题。它必须以管理员身份运行才能正常工作,否则会抛出异常:D。以下是一些代码:
// Create a listener.
        HttpListener listener = new HttpListener();
        // Add the prefixes.
        //foreach (string s in prefixes)
        //{
        //    listener.Prefixes.Add(s);
        //}
        listener.Prefixes.Add("http://*:1234/"); // accept connections from everywhere,
        //because the printer is accessible only within the LAN (no portforwarding)
        listener.Start();
        Console.WriteLine("Listening...");
        // Note: The GetContext method blocks while waiting for a request. 
        HttpListenerContext context;
        string urlForRequest = "";

        HttpWebRequest requestForPage = null;
        HttpWebResponse responseForPage = null;
        string responseForPageAsString = "";

        while (true)
        {
            context = listener.GetContext();
            HttpListenerRequest request = context.Request;
            urlForRequest = request.RawUrl.Substring(1, request.RawUrl.Length - 1); // remove the slash, which separates the portNumber from the arg sent
            Console.WriteLine(urlForRequest);

            //Request for the html page:
            requestForPage = (HttpWebRequest)WebRequest.Create(urlForRequest);
            responseForPage = (HttpWebResponse)requestForPage.GetResponse();
            responseForPageAsString = new StreamReader(responseForPage.GetResponseStream()).ReadToEnd();

            // Obtain a response object.
            HttpListenerResponse response = context.Response;
            // Send back the response.
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseForPageAsString);
            // Get a response stream and write the response to it.
            response.ContentLength64 = buffer.Length;
            response.AddHeader("Access-Control-Allow-Origin", "*"); // the magic header in action ;-D
            System.IO.Stream output = response.OutputStream;
            output.Write(buffer, 0, buffer.Length);
            // You must close the output stream.
            output.Close();
            //listener.Stop();

用户只需要以管理员身份运行控制台应用程序即可。我知道这太...令人沮丧和复杂了,但如果您无法以任何方式修改服务器,那么它是绕过域策略问题的一种解决方法。

编辑:从JS中,我进行一个简单的AJAX调用:

$.ajax({
                type: 'POST',
                url: 'http://LAN_IP:1234/http://google.com',
                success: function (data) {
                    console.log("Success: " + data);
                },
                error: function (e) {
                    alert("Error: " + e);
                    console.log("Error: " + e);
                }
            });

请求页面的 HTML 被返回并存储在 data 变量中。


-2

你需要CORS代理,将你的请求从浏览器代理到请求的服务,并附加适当的CORS headers。此类服务的列表在下面的代码片段中。您还可以运行提供的代码片段,以查看来自您位置的对这些服务的响应时间。

$('li').each(function() {
  var self = this;
  ping($(this).text()).then(function(delta) {
    console.log($(self).text(), delta, ' ms');
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/jdfreder/pingjs/c2190a3649759f2bd8569a72ae2b597b2546c871/ping.js"></script>
<ul>
  <li>https://crossorigin.me/</li>
  <li>https://cors-anywhere.herokuapp.com/</li>
  <li>http://cors.io/</li>
  <li>https://cors.5apps.com/?uri=</li>
  <li>http://whateverorigin.org/get?url=</li>
  <li>https://anyorigin.com/get?url=</li>
  <li>http://corsproxy.nodester.com/?src=</li>
  <li>https://jsonp.afeld.me/?url=</li>
  <li>http://benalman.com/code/projects/php-simple-proxy/ba-simple-proxy.php?url=</li>
</ul>


11
这并没有以任何方式回答这个问题。 - 0xc0de
@0xc0de,我终于写出了答案。 - galeksandrp

-7

已经想通了。 使用这个替代。

$('.div_class').load('http://en.wikipedia.org/wiki/Cross-origin_resource_sharing #toctitle');

你在那里使用的代码是无关紧要的。重要的是服务器端的CORS头信息。 - Quentin

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