jQuery AJAX跨域

时间:2010-08-17 19:31:01

标签: javascript jquery ajax json cross-domain

这是两个页面,test.php和testserver.php。

test.php的

<script src="scripts/jq.js" type="text/javascript"></script>
<script>
    $(function() {
        $.ajax({url:"testserver.php",
            success:function() {
                alert("Success");
            },
            error:function() {
                alert("Error");
            },
            dataType:"json",
            type:"get"
        }
    )})
</script>

testserver.php

<?php
$arr = array("element1",
             "element2",
             array("element31","element32"));
$arr['name'] = "response";
echo json_encode($arr);
?>

现在我的问题:当这两个文件都在同一台服务器(localhost或web服务器)上时,它可以工作并调用alert("Success");如果它位于不同的服务器上,意味着Web服务器上的testserver.php和localhost上的test.php,它不起作用,并且alert("Error")正在执行。即使ajax中的URL更改为http://domain.com/path/to/file/testserver.php

14 个答案:

答案 0 :(得分:406)

使用JSONP

jQuery的:

$.ajax({
     url:"testserver.php",
     dataType: 'jsonp', // Notice! JSONP <-- P (lowercase)
     success:function(json){
         // do stuff with json (in this case an array)
         alert("Success");
     },
     error:function(){
         alert("Error");
     }      
});

PHP:

<?php
$arr = array("element1","element2",array("element31","element32"));
$arr['name'] = "response";
echo $_GET['callback']."(".json_encode($arr).");";
?>

回声可能是错误的,因为我使用过php已经有一段时间了。无论如何,您需要输出callbackName('jsonString')来注意引号。 jQuery将传递它自己的回调名称,因此你需要从GET参数中获取它。

正如Stefan Kendall所说,$.getJSON()是一种速记方法,但是你需要将'callback=?'作为GET参数附加到url(是的,值是?,jQuery用它自己生成的替换它回调方法)。

答案 1 :(得分:199)

JSONP是一个不错的选择,但有一种更简单的方法。您只需在服务器上设置Access-Control-Allow-Origin标头即可。将其设置为*将接受来自任何域的跨域AJAX请求。 (https://developer.mozilla.org/en/http_access_control

当然,执行此操作的方法因语言而异。这是在Rails中:

class HelloController < ApplicationController
  def say_hello
    headers['Access-Control-Allow-Origin'] = "*"
    render text: "hello!"
  end
end

在此示例中,say_hello操作将接受来自任何域的AJAX请求,并返回“hello!”的响应。

以下是可能返回的标头示例:

HTTP/1.1 200 OK 
Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: text/html; charset=utf-8
X-Ua-Compatible: IE=Edge
Etag: "c4ca4238a0b923820dcc509a6f75849b"
X-Runtime: 0.913606
Content-Length: 6
Server: WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
Date: Thu, 01 Mar 2012 20:44:28 GMT
Connection: Keep-Alive

很简单,它确实有一些浏览器限制。请参阅http://caniuse.com/#feat=cors

答案 2 :(得分:29)

您可以通过添加 Access-Control-Allow-Origin 来通过HTTP标头控制此操作。将其设置为*将接受来自任何域的跨域AJAX请求。

使用 PHP 非常简单,只需将以下行添加到您希望从您的域外访问的脚本中:

header("Access-Control-Allow-Origin: *");

不要忘记在httpd.conf中启用mod_headers模块。

答案 3 :(得分:19)

您需要查看Same Origin Policy

  

在计算中,相同的原始政策   是一个重要的安全概念   浏览器端编程的数量   语言,例如JavaScript。该   策略允许运行脚本   来自同一网站的网页   访问彼此的方法和   属性没有具体   限制,但阻止访问   大多数方法和属性   不同网站上的网页。

为了能够获取数据,必须是:

相同的协议和主机

您需要实施JSONP来解决问题。

答案 4 :(得分:16)

我必须从本地磁盘“file:/// C:/test/htmlpage.html”加载网页,调用“http://localhost/getxml.php”url,并在IE8 +和Firefox12 +浏览器中执行此操作,使用jQuery v1.7.2 lib来最小化样板代码。看了几十篇文章后终于搞清楚了。这是我的总结。

  • 服务器脚本(.php,.jsp,...)必须返回http响应标头Access-Control-Allow-Origin:*
  • 在使用jQuery之前ajax在javascript中设置了这个标志:jQuery.support.cors = true;
  • 您可以在使用jQuery ajax函数之前或每次设置标记
  • 现在我可以在IE和Firefox中阅读.xml文档了。我没有测试的其他浏览器。
  • 响应文档可以是plain / text,xml,json或其他任何内容

这是一个带有一些调试系统的jQuery ajax调用示例。

jQuery.support.cors = true;
$.ajax({
    url: "http://localhost/getxml.php",
    data: { "id":"doc1", "rows":"100" },
    type: "GET",
    timeout: 30000,
    dataType: "text", // "xml", "json"
    success: function(data) {
        // show text reply as-is (debug)
        alert(data);

        // show xml field values (debug)
        //alert( $(data).find("title").text() );

        // loop JSON array (debug)
        //var str="";
        //$.each(data.items, function(i,item) {
        //  str += item.title + "\n";
        //});
        //alert(str);
    },
    error: function(jqXHR, textStatus, ex) {
        alert(textStatus + "," + ex + "," + jqXHR.responseText);
    }
});

答案 5 :(得分:10)

同源策略确实可以防止JavaScript跨域发出请求,但CORS规范只允许您正在寻找的那种API访问,并且受到当前批量主流浏览器的支持。

了解如何为客户端和服务器启用跨源资源共享:

http://enable-cors.org/

“跨域资源共享(CORS)是一种规范,可实现跨域边界的真正开放访问。如果您提供公共内容,请考虑使用CORS打开它以进行通用JavaScript /浏览器访问。”

答案 6 :(得分:9)

这是可能的,但您需要使用JSONP,而不是JSON。斯特凡的链接指向了正确的方向。 jQuery AJAX page提供了有关JSONP的更多信息。

雷米夏普有detailed example using PHP

答案 7 :(得分:9)

我使用Apache服务器,所以我使用了mod_proxy模块。启用模块:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

然后添加:

ProxyPass /your-proxy-url/ http://service-url:serviceport/

最后,将proxy-url传递给您的脚本。

答案 8 :(得分:8)

浏览器安全性阻止从托管在一个域上的页面到托管在不同域上的页面进行ajax调用;这被称为“same-origin policy”。

答案 9 :(得分:5)

使用JSONP的例子很少,包括错误处理。

但请注意,使用JSONP时不会触发错误事件!请参阅:http://api.jquery.com/jQuery.ajax/jQuery ajax request using jsonp error

答案 10 :(得分:4)

来自Jquery文档(link):

  • 由于浏览器安全限制,大多数“Ajax”请求都遵循相同的原始策略;请求无法成功从其他域,子域或协议中检索数据。

  • 脚本和JSONP请求不受相同的原始政策限制。

所以我认为你需要使用jsonp来处理请求。但是我自己没试过。

答案 11 :(得分:1)

我知道解决问题的3种方法:

  1. 首先,如果您有权访问这两个域,则可以使用以下命令访问所有其他域:

    header("Access-Control-Allow-Origin: *");

    通过在.htaccess文件中添加代码来

    或只是一个域:

    <FilesMatch "\.(ttf|otf|eot|woff)$"> <IfModule mod_headers.c> SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.net|dev02.otherdomain.net)$" AccessControlAllowOrigin=$0 Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin </IfModule> </FilesMatch>

  2. 您可以对服务器中的php文件发出ajax请求,并使用此php文件处理对其他域的请求。

  3. 你可以使用jsonp,因为它不需要权限。为此你可以阅读我们的朋友@BGerrissen的答案。

答案 12 :(得分:0)

对于Microsoft Azure,它略有不同。

Azure具有需要设置的特殊CORS设置。它在幕后基本上是一样的,但只是设置标题joshuarh提及将无法正常工作。可以在此处找到用于启用跨域的Azure文档:

https://docs.microsoft.com/en-us/azure/app-service-api/app-service-api-cors-consume-javascript

在我意识到我的托管平台有这个特殊设置之前,我花了几个小时来解决这个问题。

答案 13 :(得分:0)

它的工作原理,你所需要的只是:

PHP:

header('Access-Control-Allow-Origin: http://www.example.com');
header("Access-Control-Allow-Credentials: true");
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');

JS(jQuery ajax):

var getWBody = $.ajax({ cache: false,
        url: URL,
        dataType : 'json',
        type: 'GET',
        xhrFields: { withCredentials: true }
});