AJAX跨域

跨域
通过XHR实现AJAX通信的一个主要限制来源于跨域安全。默认情况下,XHR对象只能访问与包含他的页面位于同一个域中的资源。这样可以预防某些恶意行为,但是实现合理的跨域请求对开发某些应用程序是至关重要的。下面来介绍一些XHR实现跨域的方式。
CORS
CORS实现跨域的思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
在向服务器发送请求时会额外附加一个Origin头部,其中包含请求页面的源信息(协议、域名和端口),服务器可以根据这个头信息来决定是否给予响应。
  Origin: http://www.baidu.com
如果服务器认为该请求可以接受,就在Access-Control-Allow-Origin头部中会发相同的源信息或者(*)
  Access-Control-Allow-Origin: http://www.baidu.com
如果没有这个头部,或者这个头部信息和源信息不匹配,浏览器就会驳回请求。
NOTE:通过CORS发送的请求和响应都不会包含cookie信息。
1、IE对CORS的实现(IE8、IE9、IE10,IE11+可以通过XHR实现跨域)
  IE8+引入了XDR(XDomainRequest)对象,该对象与XHR类似,并且能实现安全可靠的跨域通信。XDR与XHR的不同:
    cookie不能发送也不会随响应返回
    只能访问请求头和响应头的Content-Type字段(不能设置)
    不允许访问响应头部信息
    只支持GET和POST请求
    只能发送异步请求,不能发送同步请求
XDR在接受到响应后只能访问响应返回的数据,没办法确定响应的状态代码。但是只要响应有效就会触发load事件,失败就会触发error事件。
get请求的XDR示例:
1 header('Access-Control-Allow-Origin:http://localhost:63342');
2 echo json_encode($_GET);
1     var xdr = new XDomainRequest();
2     xdr.open('get', 'http://localhost/ajax/data.php?'+fnGetURLParam({name: 'hum', age: 20}));
3     xdr.send(null);
4     xdr.onload = function(){
5         console.log(xdr.responseText);
6     }
7     xdr.onerror = function(){
8         console.log('error');
9     }

post请求的XDR示例:

1 header('Access-Control-Allow-Origin:http://localhost:63342');
2 echo json_encode($_POST);
 1     var xdr = new XDomainRequest();
 2     // xdr.contentType = "application/X-www-form-urlencoded"; // contentType是只读的(IE8报错IE9不报错但是设置不成功)
 3     xdr.open('post', 'http://localhost/ajax/data.php');
 4     xdr.send(fnGetURLParam({name: 'hum', age: 20}));
 5     xdr.onload = function(){
 6         console.log(xdr.responseText); // []
 7         console.log(xdr.contentType); // text/html
 8     }
 9     xdr.onerror = function(){
10         console.log('error');
11     }

  NOTE:XDR的post请求无法设置请求头Content-Type为application/x-www-form-urnencoded,后端不能获取请求数据。为了兼容IE89不能使用POST方式。

XDR对象的其他方法、事件、属性:

  abort方法:在响应前调用来中断请求

 1     var xdr = new XDomainRequest();
 2     xdr.open('get', 'http://localhost/ajax/data.php?'+fnGetURLParam({name: 'hum', age: 20}));
 3     xdr.send(null);
 4     xdr.onload = function(){
 5         console.log(xdr.responseText); // 不会执行
 6     }
 7     xdr.onerror = function(){
 8         console.log('error');
 9     }
10     xdr.abort(); // 中断请求

  NOTE:timeout属性、ontimeout事件可能有些问题。考虑使用jquery的定时器调用abort来模拟超时。XHR对象也可。

1 header('Access-Control-Allow-Origin:http://localhost:63342');
2 sleep(10);
3 echo json_encode($_GET);
 1     var xdr = new XDomainRequest();
 2     xdr.open('get', 'http://localhost/ajax/data.php?'+fnGetURLParam({name: 'hum', age: 20}));
 3     xdr.send(null);
 4     xdr.onload = function(){
 5         console.log(xdr.responseText); //没有输出
 6     }
 7     xdr.onerror = function(){
 8         console.log('error');
 9     }
10     setTimeout(function(){
11         xdr.abort();
12     }, 1000);

2、标准浏览器对CORS的支持

  标准浏览器都对XHR对象实现了对CORS的原生支持。

  XHR的CORS跨域,XHR对象可以访问status和同步请求,但是也有一些限制:

    不能使用setRequestHeader设置自定义头部信息

    不能发送和接受cookie

兼容IE8+的CORS封装:

 1     // 兼容IE8+ 只能发送get类型的异步跨域请求
 2     function fnGetCorsRequest(url, data, fnSuccess, fnError) {
 3         var xhr = new XMLHttpRequest;
 4         if ('withCredentials' in xhr) {
 5             xhr.open('get', url + '?' + fnGetURLParam(data), true);
 6         } else {
 7             xhr = new XDomainRequest;
 8             xhr.open('get', url + '?' + fnGetURLParam(data));
 9         }
10         xhr.send(null);
11         if (fnSuccess) { // 成功
12             xhr.onload = function () {
13                 fnSuccess(xhr.responseText);
14             }
15         }
16         if (fnError) { // 失败
17             xhr.onerror = function (e) {
18                 fnError(e);
19             }
20         }
21         return xhr;
22     }
23     // 测试
24     var xhr = fnGetCorsRequest(
25             'http://localhost/ajax/data.php',
26             {name: 'hum', age: 20},
27             function (data) {
28                 console.log(data);
29             },
30             function (e) {
31                 console.log(e);
32             }
33     );
34     
35     // 获取URLParam的辅助方法
36     function fnGetURLParam(data) {
37         var urlParam = [];
38         for (var key in data) {
39             urlParam.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
40         }
41         return urlParam.join('&');
42     }

IE11+和标准浏览器的CORS跨域封装:

 1     // 兼容IE11+和标准浏览器 支持异步和同步 get和post
 2     function fnGetCorsRequest(method, url, data, bAsyn, fnSuccess, fnError) {
 3         var xhr = new XMLHttpRequest;
 4         data = fnGetURLParam(data);
 5         if (method == 'get') {
 6             url += '?' + data;
 7             data = null;
 8         }
 9         xhr.open(method, url, bAsyn);
10         if(method == 'post'){
11             xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // setRequestHeader要放在open后
12         }
13         xhr.onload = function () {
14             fnSuccess && fnSuccess(xhr.responseText);
15         }
16         xhr.onerror = function (e) {
17             fnError && fnError(e);
18         }
19         xhr.send(data); // 支持同步 send方法应该放在事件绑定后面
20 
21         return xhr;
22     }
23     // 测试
24     var xhr = fnGetCorsRequest(
25             'post',
26             'http://localhost/ajax/data.php',
27             {name: 'hum', age: 20},
28             true,
29             function (data) {
30                 console.log(data);
31             },
32             function (e) {
33                 console.log(e);
34             }
35     );
36 
37     // 获取URLParam的辅助方法
38     function fnGetURLParam(data) {
39         var urlParam = [];
40         for (var key in data) {
41             urlParam.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
42         }
43         return urlParam.join('&');
44     }

 

Image对象(img标签)
一个网页可以从任何其他网页加载图片,而不会出现跨域的问题。
1     var oImg = new Image;
2     oImg.src = 'http://blog.smdcn.net/dizxcv.png';
3     oImg.onload = function(){
4         document.body.innerHTML += '<img src="' + oImg.src + '" />';
5     }

缺点:只能get请求,无法访问响应文本......

JSONP
JSONP(JSON with padding)填充式的JSON的缩写,是被包含在函数调用中的JSON。
JSONP由两部分组成:回掉函数和JSON
  回调函数是当响应返回时在页面中需要执行的函数
  JSONP是通过script标签来使用的,在src中可以制定一个跨域的URL
优点:
  可以直接访问响应文本,支持浏览器与服务器双向访问
缺点:
  安全性低
  只能进行get请求
  不能检测请求失败
1 $data = json_encode(array(            // php代码
2     'name' => $_GET['name'],
3     'age' => $_GET['age']
4 ));
5 $callback = $_GET['callback'];
6 echo $callback.'('. $data .')';
 1     function fnUseJSONP(url, data, fnCallback) {           // js代码
 2         var oScript = document.createElement('script');
 3         oScript.type = 'text/javascript';
 4         if (data) {
 5             oScript.src = url + '?' + fnGetURLParam(data) + '&callback=!' + fnCallback;// 注意这个!号
 6         } else {
 7             oScript.src = url + '?callback=!' + fnCallback; // 注意这个!号
 8         }
 9         // 请求返回!function (data) {                console.log(data);            }({"name":"hum","age":"20"})
10         // 为了让该函数自执行可以加上!等
11         document.body.appendChild(oScript);
12     }
13 
14     fnUseJSONP(
15             'http://localhost/ajax/data.php',
16             {name: 'hum', age: 20},
17             function (data) {
18                 console.log(data);
19             }
20     );
21 
22     // 获取URLParam的辅助方法
23     function fnGetURLParam(data) {
24         var urlParam = [];
25         for (var key in data) {
26             urlParam.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
27         }
28         return urlParam.join('&');
29     }

 

其他跨域技术
comet、SSE、web sockets有兴趣的同学可以自行研究。。。
posted @ 2015-02-28 12:42  tyxloveyfq  阅读(419)  评论(0编辑  收藏  举报