• 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有兴趣的同学可以自行研究。。。
  • 相关阅读:
    TCP 和 UDP 的区别
    python--各种锁机制归纳整理
    七牛的配置
    全文检索的配置
    记一则css3计算
    C# 生成序号不足补0
    JavaScript Post提交数据并跳转到页面(模拟Form表单提交)
    Swift 修改UITextField.Placeholder颜色
    Swift 延迟运行代码
    Swift 动画片段
  • 原文地址:https://www.cnblogs.com/tyxloveyfq/p/4305179.html
Copyright © 2020-2023  润新知