来看看一个url地址:
http://www.baidu.com:8080/scripts/jquery.js
它包含如下几个部分:
当协议、子域名、主域名和端口号中任何一个不相同时都是不同域,不同域间相互请求资源称作跨域。
XHR对象通常只能访问与包含它的页面处于同一域中的资源。
W3C中的CORS的思想是:通过用户自定义请求头部来与服务器交互,服务器根据自定义的请求头部信息来决定是否要成功响应该请求,并在响应头部中进行相应的设置。即新增了一些请求头部信息,并按照策略返回相应的响应头以及所请求的资源。被请求的资源根据它认为合适的数据(用户代理、来源页面等)决定是否设置Access-Control-Allow-Origin头部。
具体来说,首先是请求头部中设置一个头部信息:Origin:url(请求页面的源信息);服务器收到请求后,查看是否接受这个请求,如果接受,则在响应头部中设置:Access-Control-Allow-Origin:相同的源信息(如果是公共资源,则发送*,表示服务器接受来自任何站点的跨站请求)。即通过Origin和
如果无该头部或源信息不匹配则浏览器会驳回该请求。
IE8对W3C中的跨域请求进行了部分实现。是通过XDR(XDomainRequest)对象实现跨域访问的。它的用法如下:
var xdr = new XDmainRequest();
xdr.onload=function(){
alert(xdr.responseText);
};
xdr.onerror=function(){//
};
xdr.open("get","http://www.someothersite.com/page/");
xdr.send(null);
可看出与XHR用法上的区别是open方法只包含两个参数:方式和url,它只支持异步;请求返回后会触发load事件(XHR也可以用load事件代替readyStateChange事件检测是否响应成功)。
没有办法确定响应的状态码,如果响应未成功则会触发error事件,应该为xdr指定error事件处理程序,否则即使失败了也没有任何提示。
此外还有如下几个区别:
可使用原生的XHR,url使用绝对路径即可。可支持同步请求。获取状态码。但也有以下区别:
由于跨源和同源请求都使用了相同的接口,因此为消除歧义和对访问头部和cookie的限制,建议同源的就使用相对路径。
以下内容针对XHR对象。
preflight即预请求,是相对于前面所述的简单请求,即通过使用 Origin 和 Access-Control-Allow-Origin 就可以完成最简单的跨站请求。简单请求是指:
只使用 GET, HEAD 或者 POST 请求方法。如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种。
不会使用自定义请求头
服务器如果认为接收该源页面的请求,则直接在响应中设置头部信息 Access-Control-Allow-Origin并发送响应数据。
而预请求允许用户自定义头部、使用GET、POST之外的方法。它要求必须先发送一个 OPTIONS 请求(OPTIONS 是 HTTP/1.1 里的方法,用来获取更多服务器端的信息,类似GET等是一种请求方式)给目的站点,来查明这个跨站请求对于目的站点是不是安全可接受的。这样做,是因为跨站请求可能会对目的站点的数据造成破坏(其他请求方式)。
具备以下条件就被会当做预请求来处理:
通过这种方式,先发送一个预请求,服务器接收到后判断是否接受请求,并会在响应头部中告知允许的请求方式、自定义头部等信息。之后浏览器根据预请求响应再发送一个真正的请求,之后服务器再返回响应数据。
OPTIONS请求头部中包含如下信息:
Origin:来源页面
Access-Control-Request-Method:提醒服务器实际的跨站请求将使用的方法
Access-Control-Request-Headers:请求将携带的自定义头部信息(具体的内容将在真正发送请求时携带)
服务器接收到该预请求后会解析,判断是否接受该请求。
服务器对OPTIONS请求的响应头部包括如下内容:
Access-Control-Allow-Origin:
Access-Control-Allow-Methods:允许的请求方法
Access-Control-Allow-Headers:允许的自定义头部
Access-Control-Max-Age:告诉浏览器,本次“预请求”的响应结果有效时间是多久。在这段时间内,浏览器在处理针对该服务器的跨站请求,都可以无需再发送“预请求”,只需根据本次结果进行判断处理。
默认情况下,跨域请求浏览器是不会发送凭证信息的(如cookie、HTTP认证、客户端SSL证明等)。通过设置xhr对象的属性withCredentials = true来发送带凭据的请求,这样cookie等就可以随请求一起发送了。
这个也是会根据xhr请求有无自定义头部等方式来判断需不需要发送预请求。
如果在响应头部中没有字段Access-Control-Allow-Credentials: true,那么浏览器将不会把响应结果传递给发出请求的脚步程序,responseText中将会是空字符串,以保证信息的安全。并且会触发error事件。
特别注意: 给一个带有withCredentials的请求发送响应的时候,服务器端必须指定允许请求的域名,不能使用’*’.如果响应头是这样的:Access-Control-Allow-Origin: * ,则响应会失败. 在这个例子里,因为Access-Control-Allow-Origin的值是http://foo.example这个指定的请求域名,所以客户端把带有凭证信息的内容被返回给了客户端. 另外在响应中更多的cookie信息也被创建了.
注意:IE10及之前的版本不支持预请求和带凭据的请求。
慕课网中的跨域讲解:
JSONP是当前非常流行的跨域方式,是一种非正式的传输协议,它是利用了<\script>标签没有跨域限制的“漏洞”(历史遗迹)来达到与第三方通讯的目的。客户端可以跨域请求一个JS文件,在客户端创建一个回调函数,参数应该就是期待服务端返回的JSON数据,在客户端动态地生成请求脚本(主要是想设置script的src属性值,其中带参数回调函数名)。在远程服务器上设法把客户所需要的数据装进js格式的文件里(实际上所有包含src属性的标签都没有跨域限制)。客户端成功调用请求到的JS文件也就获取到了所需要的JSON数据。
该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
实际上就是一种扩展的支持在用户定义函数中包含返回数据的能力。这种方法依赖于必须接受一个回调函数的名字作为参数。
具体做法是:
$("#search").click(function(){
$.ajax({
type: "GET",
url: "http://127.0.0.1:8000/ajaxdemo/serverjsonp.php?number=" + $("#keyword").val(),
dataType: "jsonp",//
jsonp: "callback", //
success: function(data) {
if (data.success) {
$("#searchResult").html(data.msg);
} else {
$("#searchResult").html("出现错误:" + data.msg);
}
},
error: function(jqXHR){
alert("发生错误:" + jqXHR.status);
},
});
});
后端GET这个值为callback的$jsonp对象,通过这个名字拼接json字符串。调用数据服务器的返回值需要用$jsonp.’()’的方式连接起来, ‘.’号在php中用于连接字符串。
$jsonp = $_GET["callback"];
$result = $jsonp . '({"success":true,"msg":"找到员工:员工编号:' . $value["number"] .',员工姓名:' . $value["name"] .',员工性别:' . $value["sex"] . ',员工职位:' . $value["job"] . '"})';
echo $result; //返回的数据及前端的data
上段参考知乎回答:http://www.zhihu.com/question/19966531
说说JSON和JSONP,也许你会豁然开朗,含jQuery用例
只需在被请求的服务器端中进行下列头部设置:
<?php header("Control-Allow-Origin: *");//允许的跨域源地址,*表示允许所有 header("Control-Allow-Origin-Methods: POST,GET"); ?>
而无需修改客户端。
此外还有一些别的方法,比如document.domain来实现框架间的交互,这篇文档中有详细的解释:
js中几种实用的跨域方法原理详解
参考资料:Mozilla技术文档中对跨域请求的讲解