zoukankan      html  css  js  c++  java
  • 《JavaScript》高级程序设计第21章:Ajax和Comet,jsonp

    Ajax的技术核心是XMLHttpRequest对象(简称XHR)

    一、创建XMLHttpRequest对象


     

     1     function createXHR(){
     2         if(typeof XMLHttpRequest != "undefined"){
     3             //IE7, FireFox, Opera, Chrome, Safari都支持原生的XHR对象,这些浏览器中可以使用XMLHttpRequest构造函数
     4             return new XMLHttpRequest();        
     5         } else if (typeof ActiveXObject != "undefined"){
     6             //使用于IE7之前的版本
     7             if(typeof arguments.callee.activeXString != 'string'){
     8                 var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
     9                                  "MSXML2.XMLHttp"],
    10                     i, len;
    11                 for(i=0, len=versions.length; i<len; i++){
    12                     try{
    13                         new ActiveXObject(versions[i]);
    14                         arguments.callee.activeXString = versions[i];
    15                         break;
    16                     } catch(ex){
    17                         //跳过
    18                     }
    19                 }
    20             }
    21             return new ActiveXObject(arguments.callee.activeXString);
    22         } else {
    23             throw new Error("No XHR object available");
    24         }
    25     }
    26 
    27     //创建XHR对象
    28     var xhr = createXHR();

    二、XHR的用法


     

      1. open()方法:

      • 三个参数:发送的请求类型;请求的url;是否异步发送
      • 注意:
        • url相对于执行代码的当前页面(也可以使用绝对路径)
        • 调用open()方法并不会真正发送请求,而只是启动一个请求以备发送
        • 只能向同一个域中使用相同端口协议的url发送请求,如果url和启动请求的页面有任何差别,都会引发安全错误!

      2. 发送请求:send()方法

      • 接收一个参数,即要作为请求主体发送的数据。
      • 如果不需要通过请求主体发送数据,则必须传入null。这个参数对某些浏览器来说是必须的。

      3. 收到响应后,响应的数据会自动填充xhr对象的属性。相关的属性有:

      • responseText
      • responseXML
      • status
      • statusText

       收到响应,首先要检查status属性,一般来说,可将http状态代码为200作为成功标志。此外,状态码304表示请求资源没有被修改,可以直接使用浏览器中缓存的版本,则响应也有效。

       对于异步请求,可以检测xhr对象的readyStatus属性,该属性表示请求/响应过程的当前活动阶段,可取值如下:

      • 0:未初始化。尚未调用open()方法
      • 1:启动。已经调用open()方法,尚未调用send()方法
      • 2:发送。已经调用send()方法,尚未接收到响应
      • 3:接收。已经接收部分响应数据。
      • 4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。【一般只需检查这个阶段】

       只要readyStatus属性改变,都会触发一次readyStatechange事件。

     1     var xhr = createXHR();
     2     //必须要在open()方法前指定onreadyStatechange事件处理程序才能确保跨浏览器兼容性
     3     xhr.onreadyStatechange = function(){
     4         //这里使用xhr对象而非this对象,原因是此事件的作用域问题
     5         //如果使用this对象,在有的浏览器中会导致函数执行失败,或者导致错误发生。
     6         if(xhr.readyState == 4){
     7             if((xhr.status >= 200 && xhr.status <300) || xhr.status == 304){
     8                 alert(xhr.responseText);
     9             } else {
    10                 alert("Request was unsuccessful: " + xhr.status);
    11             }
    12         }
    13     };
    14     xhr.open("get", "example.txt", true);
    15     xhr.send(null);

    三、XMLHttpRequest 2 级


     

    四、进度事件


     

    五、跨域资源共享


     

    1. 跨域 :

      跨域安全策略:默认情况下, XHR对象只能访问与包含它 的页面位于同一个域中的资源。

      所谓跨域,就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。

      简单来说,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名协议端口号的组合.

    URL

    说明

    是否允许通信

    http://www.a.com/a.js
    http://www.a.com/b.js
    同一域名下 允许
    http://www.a.com/lab/a.js
    http://www.a.com/script/b.js
    同一域名下不同文件夹 允许
    http://www.a.com:8000/a.js
    http://www.a.com/b.js
    同一域名,不同端口 不允许
    http://www.a.com/a.js
    https://www.a.com/b.js
    同一域名,不同协议 不允许
    http://www.a.com/a.js
    http://70.32.92.74/b.js
    域名和域名对应ip 不允许
    http://www.a.com/a.js
    http://script.a.com/b.js
    主域相同,子域不同 不允许
    http://www.a.com/a.js
    http://a.com/b.js
    同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问)
    http://www.cnblogs.com/a.js
    http://www.a.com/b.js
    不同域名 不允许

    2. 跨域资源共享(CORS,Cross-Origin Resource Sharing)

      是W3C的标准。

      基本思想:使用自定义的http头部让浏览器与服务器进行沟通,从而决定请求或相应是应该成功,还是应该失败。(这一块的东西看着烦,先不管了)

    六、其他跨域技术


     

    1. 图像Ping

      即使用<img>标签。因为网页可以从任何网页中加载图像,而不用担心是否跨域。

      请求的数据是通过查询字符串形式发送的,而响应可以是任意内容。通过图像Ping,浏览器得不到任何具体的数据,但是通过侦听load和error事件,能知道响应是什么时候接收到的。

      

    1     var img = new Image();
    2     //将onload和onerror事件处理程序指定为同一个函数。这样无论是什么响应,只要请求完成,就能得到通知。
    3     img.onload = img.onerror = function(){
    4         alert('Done');
    5     };
    6     //请求从设置src属性那一刻开始,这里在请求中发送了一个name参数
    7     img.src='http://xxxxxxxx/test?name=Nicholas';

      缺点:

      • 只能发送GET请求
      • 无法访问服务器的响应文本
      • 因此只能用于浏览器与服务器间的单向通信

    2. JSONP(JSON with padding,填充式JSON/参数式JSON)

      是被包含在函数调用中的JSON。由两部分组成:

        1)回调函数:当响应到来时应该在页面中调用的函数。一般在请求中指定

        2)数据:传入回调函数中的JSON数据

      JSONP是通过动态<script>元素来使用,使用时可以为src属性指定一个跨域URL。这里script和img元素类似,都有能力不受限地从其他域加载资源。因为JSONP是有效的JavaScript代码,所以在请求完成后,即在JSONP响应加载到页面中以后,会立即执行。

    1     function handleResponse(response){
    2         alert("You are at IP address " + response.ip + ", which is in " +
    3             response.city + ", " + response.region_name);               //试了下真的可以弹出我的地址!!!
    4     }
    5 
    6     var script = document.createElement("script");    
    7     script.src = "http://freegeoip.net/json/?callback=handleResponse";    //指定回调函数是handleResponse()
    8     document.body.insertBefore(script,document.body.firstChild);  

      与图片Ping相比,JSONP的优点在于:

        1)能够直接访问响应文本

        2)支持在浏览器与服务器之间双向通信。

      不足:

        1)JSONP是从其他域中加载代码执行。其他域的安全性难以保证

        2)要确保JSONP请求是否失败并不容易。<script>元素的onerror事件处理程序不被浏览器支持,因此必须使用计时器检测指定时间内是否收到响应。但是用户上网速度和带宽并不一定。

      JSONP的原理:

      网页可以得到从其他来源动态产生的Json资料,而这种使用模式就是所谓的Jsonp。用Jsonp抓到的资料并不是Json,而是任意的Javascript,用Javascript直译器执行而不是用Json解析器解析。比如:

      看以下的代码就理解jsonp如何进行回调函数了。

    JavaScript:

    //注意:Jsonp只能使用GET请求
    $.ajax({
      url: 'http://www.shihj.com',
      dataType: 'jsonp',
        success: function (response) {
            console.log(response);
        }
    });

    PHP:

    $response = array('Javascript', 'PHP', 'Html', 'CSS');
    $response = json_encode($response);
    
    $callback = isset($_GET['callback']) ? $_GET['callback'] : false;
    if ($callback) {
        $response = 'try{' . $callback . '(' . $response . ')}catch(e){}';
    }
    exit($response);

    3. Comet(“服务器推送”)

      Ajax是一种从页面向服务器请求数据的技术,而Comet是一种服务器向页面推送数据的技术。Comet能够让信息近乎实时地被推送到页面上

      有两种实现Comet的方式:

      1)长轮询:

        定义:页面发起一个到服务器的请求,然后服务器一直保持连接打开,知道有数据可发送。发送完数据后,浏览器关闭连接,随即又发起一个到服务器的新请求。这一过程在页面打开期间一直持续不断。

        优点:所有浏览器都支持,使用XHR和setTimeout()就能实现

      2)http流:

        定义:浏览器向服务器发送一个请求,而服务器保存连接打开,然后周期性地向浏览器发送数据。

     1     //在IE中不可用
     2     //三个参数:url, 接收到数据时调用的函数, 关闭连接时调用的函数  
     3     function createStreamingClient(url, progress, finished){
     4         var xhr = new XMLHttpRequest(),
     5             received = 0;
     6 
     7         xhr.open("get", url, true);
     8         xhr.onreadystatechange = function(){
     9             var result;
    10 
    11             if(xhr.readyState == 3){
    12 
    13                 //只取得最新数据并调整计数器
    14                 result = xhr.responseText.substring(received);
    15                 received  += result.length;
    16 
    17                 //调用progress回调函数
    18                 progress(result);
    19             } else if (xhr.readyState == 4){
    20                 finished(xhr.responseText);
    21             }
    22         };
    23         xhr.send(null);
    24         return xhr;
    25     } 
    26 
    27     var client = createStreamingClient("streaming.php", function(data){
    28                     alert("Received: " + data);
    29                 }, function(data){
    30                     alert("Done!");
    31                 });
    32     });

    4. 服务器发送事件(SSE:Server-Sent Events)

      是围绕只读Comet交互推出的API或者模式。SSE API用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。

      支持短轮询,长轮询和HTTP流,且能在断开连接时自动确定何时重新连接

      不支持ie

    5. Web Sockets

     目标:在一个单独的持久连接上提供全双工、双向通信。

     在js中创建了WebSocket后,会有一个http请求发送到浏览器以发起连接。在取得服务器响应后,建立的连接会使用http升级从http协议交换为Web Socket协议。只有支持这种协议的服务器才能正常工作。

      http://    --->    ws://

      https://    --->    wss://

     创建Web Socket:

      var socket = new WebSocket("ws://www.example.com/server.php");

      必须传入绝对url。同源策略对Web Socket不适用。因此可以连接任何站点。

     实例化WebSocket对象后,浏览器就会马上尝试创建连接。与xhr类似,WebSocket也有一个表示当前状态的readyState属性:

    WebSocket.OPENING (0) 正在建立连接
    WebSocket.OPEN (1) 已经建立连接
    WebSocket.CLOSING (2) 正在关闭连接
    WebSocket.CLOSE (3) 已经关闭连接

     要关闭Web Socket连接,可以在任何时候调用close()方法:socket.close();

    6. SSE & Web Sockets

      1) 是否有自由度建立和维护Web Sockets服务器?

      2)是否需要双向通信?

    7. 跨域方法小结:

      以上,我看Web Socket还可以实现跨域,但SSE好像没有涉及到跨域?SSE和Web Socket两节应该都是偏向于讲述两种Comet(服务器推送)的方法。而至于跨域的方法,综合上面介绍的几种:

      1)图片Ping:使用<img>标签

      2)JSONP:通过动态<script>元素调用

      3)后台代理方法:这种方式可以解决所有跨域问题,也就是将后台作为代理,每次对其它域的请求转交给本域的后台,本域的后台通过模拟http请求去访问其它域,再将返回的结果返回给前台,这样做的好处是,无论访问的是文档,还是js文件都可以实现跨域。

      4)设置document.domain:只适用于不同子域的框架间的交互。

      5) 使用window.name

      • 在应用页面(a.com/app.html)中创建一个iframe,把其src指向数据页面(b.com/data.html)。数据页面会把数据附加到这个iframe的window.name上
      • 在应用页面(a.com/app.html)中监听iframe的onload事件,在此事件中设置这个iframe的src指向本地域的代理文件(代理文件和应用页面在同一域下,所以可以相互通信)
      • 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)。

      6) 使用HTML5的新方法:window.postMessage()

        window.postMessage(message, targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源。

     调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
     需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
     上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的那种情况,因为每一个框架都有一个window对象。在讨论第种方法的时候,我们说过,不同域的框架间是可以获取到对方的window对象的,虽然没什么用,但是有一个方法是可用的-window.postMessage。下面看一个简单的示例,有两个页面:

    a.com/index.html中的代码:

    1 <iframe id="ifr" src="b.com/index.html"></iframe>
    2 <script type="text/javascript">
    3     window.onload = function(){
    4         var ifr = document.getElementById('ifr');
    5         var targetOrigin = "http://b.com";  //若写成http://b.com/c/proxy.html效果一样
    6                                             //若写成'http://c.com'就不会执行postMessage了
    7         ifr.contentWindow.postMessage('I was there! ', targetOrigin);
    8     };
    9 </script>

    b.com/index.html中的代码:

     1 <script type="text/javascript">
     2     window.addEventListener('message', function(event){
     3         //通过origin属性判断来源地址
     4         if(event.origin == 'http://a.com'){
     5             alert('event.data');    //弹出I was there
     6             alert(event.source);    //对a.com, index.html中window对象的引用
     7                                     //由于同源策略,这里event.source不可以访问window对象
     8         }
     9     },false)
    10 </script>

    七、安全


     

    参考: JSONP解决跨域问题

  • 相关阅读:
    洛谷P1514引水入城
    洛谷P2827蚯蚓
    外部排序总结
    桶排序
    基数排序
    计数排序
    搜索文件-读目录的使用
    堆排序
    甲乙用同一串数字玩游戏
    Cracking the Coding Interview 8.7
  • 原文地址:https://www.cnblogs.com/haoyijing/p/5778155.html
Copyright © 2011-2022 走看看