javascript--21ajax与comet

XMLHttpRequest对象
在IE5中,XHR对象是通过MSXML库中的ActiveX对象实现的。在IE中可能会遇到三种不同版本的XHR对象,即MSXML2.XMLHttp、MSXML2.XMLHttp.3.0和MXSML.XMLHttp.6.0。IE7之后的版本和其他浏览器都会使用下面的函数来创建:
var xhr=new XMLHttpRequest();
兼容性创建XHR的代码如下:
function createXHR(){
   if(typeof XMLHttpRequest!="undefined"){
    return new XMLHttpRequest();   
   }else if(typeof ActiveXObject!="undefined"){
    if(typeof arguments.callee.activeXString!="string"){
   var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
   for(i=0,len=versions.length;i<len;i++){
    try{
  new ActiveXObject(versions[i]);
  arguments.callee.activeXString=versions[i];
  break;
}catch(ex){
  //skip;
}   
   }
    }
return new ActiveXObject(arguments.callee.activeXString);
   }else{
     throw new Error("No XHR object available.");
   }


XHR的用法
在使用XHR对象时,要调用的第一方法时open(),它接收三个参数:要发送请求的类型,请求的URL和表示是否异步发送请求的布尔值。
xhr.open("get","example.php",false);
要发送特定的请求,要向下面一样调用send()方法:
xhr.open("get","example.php",false);
xhr.send(null);
XHR从服务器返回后,发生变化的属性,即保存服务器相应数据的属性为:
responseText:作为响应主体被返回的文本
responseXML:如果响应类型是"text/xml"和"application/xml",则保存着响应数据的XML DOM文档
status:响应的HTTP状态
statusText:HTTP状态的说明
在使用XHR之前必须指定onreadystatechange事件处理程序才能保证跨浏览器兼容性。
var xhr=createXHR();
xhr.onreadystatechange=function(){
   if(xhr.readyState=="4"){
   if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
   alert(xhr.responseText);
   }else{
   alert("Request was unsuccessful:"+xhr.status);   
   }   
   
   }
};
xhr.open("get","example.text",true);
xhr.send(null);
另外在接收到响应之前还可以调用abort()方法来取消异步请求。如下所示:
xhr.abort();
在终止请求之后,还应该对XHR对象进行解引用操作。由于内存原因,不建议重用XHR对象。

HTTP头部信息
XHR对象提供了操作请求头部和响应头部信息的方法。在默认情况下,在发送XHR请求同时,还会发送下列头部信息:
Accept:浏览器能够处理的内容类型
Accept-Charset:浏览器能够显示的字符集
Accept-Encoding:浏览器能够处理的压缩编码
Accept-Language:浏览器当前设置的语言
Connection:浏览器与服务器之间连接的类型
Cookie:当前页面设置的任何Cookie
Host:发送请求的页面所在的域
Referer:发送请求的页面的URI
User-Agent:浏览器的用户代理字符串
使用setRequestHeader()方法可以设置自定义的请求头部信息。这个方法接收两个参数:头部字段名和头部字段值。要成功发送请求头部信息,必须在调用open()方法之后且send()方法之前调用它。建议使用自定义的头部名称,而且浏览器有的支持有的不支持。

POST请求
使用post提交,要设置头部属性Content-type。如果不设置,会出现数据无法解码和获取等问题。

XMLHttpRequest 2级
并非所有的浏览器都完整的实现了XMLHttpRequest 2级的规范,但是所有的浏览器都实现了它部分的规范。
FormData
它是为序列化表单以及创建于表单格式相同的数据提供了遍历。使用方式参考下面的两个例子:
var data=new FormData();
data.append("name","seacean2000");

var data=new FormData(document.forms[0]);
它的方便之处在于不用明确的在XHR对象上设置请求头部。

超时设定
IE8+唯一支持的超时设定事件,XHR对象的ontimeout事件。XHR对象的timeout设定超时时间,单位是毫秒数。这些设定要方法open之后,send之前。
//open
xhr.timeout=1000;
xhr.ontimeout=function(){   };
//send

overrideMimeType()方法
用于重写XHR响应的MIME类型。它能强迫服务器返回的数据类型给些为本方法提供的类型。使用方法:
在open之后,send之前。

进度事件
有以下6个进度事件:
loadstart:在接收到响应数据的第一个字节时触发。
progress:在接收响应数据期间持续的触发
error:在请求发生错误时触发
abort:在因调用abort()方法而终止连接时触发
load:在接收到完整的响应数据时触发
loadend:在通信完成或者触发error,abort,load事件后触发。
现在所有的主流浏览器都支持load事件,前五个除了IE其他的都支持

load事件
并非所有的浏览器都为了这个事件实现了适当的事件对象。下面是使用示例:
xhr.onload=function(){
   if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
   alert(xhr.responseText);
   }else{
   alert("Request was unsuccessful:"+xhr.status);   
   } 
}
放到open方法之前。

progress事件
Mozilla为XHR新添加的事件,这个事件会在浏览器接收数据期间周期性的触发。而onprogress事件处理程序会接收一个event对象,其target属性是XHR对象,但包含着三个额外属性:lengthComputable,position,tatalSize.其中lengthComputable表示进度信息是否可用的布尔值,position表示已经接收的字节数,totalSize表示根据Content-Length响应头部确定的预期字节数。有了这些我们就可以为用户创建一个进度指示器。
xhr.onprogress=function(event){
var divStatus=document.getElementById("status");
if(event.lengthComputable){
 divStatus.innerHTML="Received "+event.position+" of "+event.totalSize+" bytes";
}
};
要放到open之前才可以。

跨源资源共享
通过XHR实现Ajax通信的一个主要限制,来源于跨域安全策略。在默认情况下,Ajax只能访问与包含它的页面位于同一个域中的资源。但是有时也需要一些跨域的请求。为了解决这个问题,现在的浏览器采用CORS(Cross-Origin Resource Sharing,跨域资源共享)策略来实现。CORS是W3C的一个工作草案,定义了必须访问跨源资源时浏览器与服务器之间如何进行沟通。这个策略的基本思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应的成功和失败。注意请求和响应都不包含cookie信息。

IE对CORS的实现
IE8中引入了XDR(XDomainRequest)类型,这个对象与XHR类似,但是能实现安全可靠的跨域通信。XDR对象的安全机制部分实现了W3C的CORS规范。以下是XDR与XHR的不同之处:
cookie不会随请求发送,也不会随响应返回
只能设置请求头部信息中的Content-Type字段
不能访问响应头部信息
只支持GET和POST请求
XDR对象使用方法:创建一个实例,调用open方法,调用send方法。open方法只接受两个参数:请求的类型和URL。所有XDR的请求都是异步的。请求返回后会触发load事件,响应数据保存在responseText属性中。
var xdr=new XDomainRequest();
xdr.onload=function(){
  alert(xdr.responseText);
};
xdr.open("get","http://www.somewhere-else.com/page/");
xdr.send(null);
在跨域请求上唯一可以获得响应的信息就是错误本身,因此确定响应失败的方式就是使用onerror事件。要放到open之前使用。
xdr.onerror=function(){
   alert("an error occurred!");
}
在请求返回之前,可以调用取消命令abort()方法:
xdr.abort();//终止请求
与XHR一样,XDR对象也支持timeout属性以及ontimeout事件处理程序。
xdr.timeout=1000;
xdr.ontimeout=function(){
alert("late!")
}
将这些处理程序添加到open之前才可以哦。
为了支持post请求,XDR对象提供了contentType属性,用来表示发送数据的格式:在open之后,send之前设置
xdr.contentType="application/x-www-form-urlencoded";

其他浏览器对CORS的实现
其他的浏览器都通过XMLHttpRequest对象实现了对CORS的原生支持。但是在跨域请求的时候有以下的限制:
不能使用setRequestHeader()设置自定义头部。
不能接受和发送cookie
调用getAllResponseHeaders()方法总会返回空字符串。
无论是同源请求还是跨域请求,对于本地资源最好使用相对URL,在访问远程资源时再使用绝对URL。

Preflighted Requests
透明服务器验证机制,支持开发人员使用自定义的头部,get和post之外的方法,以及不同类型的主题内容。这种请求使用OPTIONS方法,发送下列头部:
Origin:与简单的请求相同
Access-Control-Request-Method:请求自身使用的方法
Access-Control-Request-Headers:(可选)自定义头部信息,多喝头部逗号分隔。
发送请求之后,服务器决定是否允许这种类型的请求。服务器通过响应发送如下的头部与浏览器进行沟通:
Access-Control-Allow-Origin:与简单的请求相同
Access-Control-Allow-Methods:允许的方法,多个方法以逗号分隔
Access-Control-Allow-Headers:允许的头部,多个头部以逗号分隔
Access-Control-Max-Age:应该将这个Preflight请求缓存多长时间(以秒表示)

带凭据的请求
通过将withCredentials属性设置为true,可以指定特定的请求应该发送凭据。如果服务器接收带凭据的请求,会用下面的HTTP头部响应
Access-Control-Allow-Credentials:true

跨浏览器的CORS
function createCORSRequest(method,url){
   var xhr=new XMLHttpRequest();
   if("withCredentials" in xhr){
   xhr.open(method,url,true);   
    }else if(typeof XDomainRequest!="undefined"){
    xhr=new XDomainRequest();
xhr.open(method,url);
    }else{
   xhr=null;
    }
return xhr;
}
var request=createCORSRequest("get","http://somewhere-else.com/page/");
if(request){
   request.onload=function(){
      //request.responseText
   };
   request.send();
   
}

其他的跨域技术
图像Ping技术
根据一个网页可以从任何网页中加载图像而不用担心使用跨域的原理,我们可以动态的创建图像,使用他们的onload和onerror事件处理程序来确定是否收到响应。
动态的创建图像经常用于图像Ping。图像Ping是与服务器进行简单的单向的跨域通信的一种方式。请求的数据通过查询字符串形式发送的,而响动可以是任意内容,但通常是像素图或者204响应。通过图像Ping,浏览器得不到任何具体的数据,但是通过侦听load和error事件,它能知道是什么时候接受的。
var img=new Image();
img.onload=img.onerror=function(){
  alert("Done");
};
img.src="http://ww.example.com/test?name=seacean";
图像Ping最常用于跟踪用户点击页面或动态广告曝光次数。它有两个主要的缺点:只能发送get请求,无法访问服务器的响应文本。因此,图像Ping只能用于浏览器与服务器间的单向通信。

JSONP
JSONP是JSON with padding的简写,是应用json的一种新方法,在后来的Web服务中非常流行。JSONP由两部分组成:回调函数和数据。回调函数式当响应到来时应该在页面中调用的函数,回调函数的名字一般是在请求中指定的。数据就是传入回调函数的json数据。下面是以个JSONP的例子:
http://freegeoip.net/json/?callback=handleResponse
JSONP是通过动态的<script>元素来使用的,使用时可以为src属性指定一个跨域URL。这里的<script>元素有能力不受限制的同其他域加载资源。因为JSONP是有效地javascript代码,所以在请求完成后,即在JSONP响应加载到页面后会立即执行。
function handlerResponse(response){
  alert("You're at IP address "+response.ip+", where is in "+response.city+", "+response.region_name);
}
var script=document.createElement("script");
script.src="http://freegeoip.net/json/?callback=handlerResponse";
document.body.insertBefore(script,document.body.firstChild);
JSONP的优势在于简单易用,直接访问响应文本,支持浏览器与服务器之间的双向通信。JSONP的不足之处:不能确定加载的域是否安全,要确定请求失败并不容易。为了避免这些,开发人员的现行方案是用指定时间内是否接收到了响应来判断。

Comet
这是一种服务器向页面推送数据的技术。Comet能够让信息近乎实时的被推送到页面上,非常适合处理体育比赛和股票报价。有两种实现Comet的方式:长轮询和流。长轮询是传统轮询的一个翻版,即浏览器定时向服务器发送请求,看看有没有数据更新。长轮询把传统轮询颠倒了一下,页面发送一个到服务器的请求,然后服务器一直保持连接打开,知道有数据可发送。发送完数据后,浏览器关闭连接,随即又发起一个到服务器的新请求。这个过程在页面打开期间一直不断持续。第二种流行的Comet方式是HTTP流。流在页面的整个生命周期中只使用一个HTTP连接。具体来说就是浏览器向服务器发送一个请求,然后服务器保持连接打开,然后周期性的向浏览器发送数据。下面这段php脚本就是采用流实现的服务器中的常见方式:
$i=0;
while(true){
echo "Number is $i";//输出数据然后刷新缓存
flush();
sleep(10);//停止几秒
$i++;
}
这段代码是实现HTTP流的关键。
下面这段代码是XHR对象实现HTTP流的典型代码:
function createStreamingClient(url,progress,finished){
var xhr=new XMLHttpRequest(),received=0;
xhr.open("get",url,true);
xhr.onreadystatechange=function(){
  var result;
  if(xhr.readyState==3){
  result=xhr.responseText.substring(received);
  received+=result.length;
  progress(result);
  }else if(xhr.readyState==4){
  finished(xhr.responseText);
  }
};
xhr.send(null);
return xhr;
}
var client=createStreamingCilent("streaming.php",function(data){
  alert("Received:"+data);
  },function(data){
  alert("Done!");
  });
这个createStreamingCilent函数接收三个参数:要连接的URL,在接收到数据时调用的函数以及关闭连接时调用的函数。

服务器发送事件
SSE(Server-Sent Events,服务器发送事件)是围绕只读Comet交互推出的API或者模式。SSE API用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。服务器响应的MIME类型必须是text/event-stream,而且是浏览器中的Javascript API能解析的格式输出。SSE支持短轮询,长轮询和HTTP流,而且能够在断开连接时自动确定何时重新连接。
SSE API
SSE是为javascript api与其他传递消息的javascript api很相似。要预定新的事件流,要创建新的EventSource对象,并传入一个入口点:
var source=new EventSource("myevents.php");
注意:要传入的URL必须与创建对象的页面同源。EventSource的实例有一个readyState属性,值为0表示正连接到服务器,值为1表示打开了连接,值为2表示关闭连接。另外还有三个事件:
open:在建立连接时触发
message:在从服务器接收到新事件时触发
error:在无法建立连接时触发
服务器返回的数据以字符串的格式保存在event.data中。如果想强制立即断开并且不再重新连接,可以调用close()方法。
事件流
多段数据发送时,要以换行符进行分隔不同段的数据。而且还可以在每段数据的前面或者后面附加ID,以便多段数据的拼接。在设置了ID之后,EventSource对象会跟踪上一次触发的事件。如果连接断开,会向服务器发送一个包含名为Last-Event-ID的特殊HTTP头部请求,以便服务器知道下次触发那个事件。在多次连接的事件流中,这种机制保证了浏览器能够以正确的顺序接收到连接的数据段。

Web Sockets
在js中web sockets使用了自定义的协议。未加密的协议是ws://;加密的协议是wss://.目前支持它的的浏览器有FireFox6+,safari5+,Chrome,IOS4+版safari。
创建web sockets的例子:
var sockets=new WebSockets("ws://www.example.com/server.php");
注意,必须给WebSockets构造函数传入绝对的URL。同源策略对它不适用。能否与特定的域通信完全取决于服务器。
sockets.close();   //关闭
sockets.send("Hello word");//可以发送字符串,json格式的字符串
sockets的事件有onmessage:服务器向客户端发送消息,sockets会触发;onopen:成功建立连接时触发;onerror:在发生错误时触发,连接时不能持续;onclose:在连接关闭时触发。在close事件中的event对象有三个额外的属性:wasClean,code,reason.第一个参数表示连接是否明确的关闭,布尔值。第二个是服务器返回的数值状态码,而reason是一个字符串,包含服务器返回的信息。

Web Sockets协议比同于HTTP,现有的服务器不能用Web Sockets通信,HTTP可以满足要求。如果只需要向服务器请求数据,那么SSE比较容易,要是双向的通信Web Sockets更好一些。在不能使用Web Sockets的情况下,组合XHR+SSE也能实现双向通信。

在AJAX安全方面,下列措施是得力的:
要求以SSL连接来访问可以通过XHR请求的资源
要求每一次请求都要附带经过相应算法计算得到的验证码

Ajax通信与数据格式无关,从服务器获取的数据不一定是XML数据。

 Ajax的核心:XMLHttpRequest对象(简称XHR)

       在XHR对象之前,Ajax通信通常使用hack手段,如使用隐藏的或内嵌的框架。

       XHR对象为向服务器发送信息和解析服务器响应提供了流畅的接口。

1.XMLHttpRequest对象

       IE5是第一款引进XHR对象的浏览器,通过MSXML库中的ActiveX对象实现(有3个版本)。

  兼容所有浏览器,创建XHR对象:



function createXHR(){
    if (typeof XMLHttpRequest != "undefined"){
        return new XMLHttpRequest();
    } else if (typeof ActiveXObject != "undefined"){
        if (typeof arguments.callee.activeXString != "string"){
            var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
                            "MSXML2.XMLHttp"],
                i, len;
   
            for (i=0,len=versions.length; i < len; i++){
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex){
                    //skip
                }
            }
        }
   
        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error("No XHR object available.");
    }
}

   之后就能在所有浏览器创建XHR对象:var xhr = createrXHR();

2.原生XHR对象 (支持的浏览器: IE7+、FF、Chrome、Opera、Safari)

       通过XMLHttpRequest构建函数,创建XHR对象:

var xhr = new XMLHttpRequest();
3.XHR用法

3-1.open()

       open() 3个参数: 发送的类型、请求的URL、表是否异步的布尔值

xhr.open("get","example.php", false);
       ①URl为相对于执行代码的当前页,或绝对地址;

       ②false为同步,JavaScript代码会在服务器响应后再继续执行;

       ③调用open()只是启动一个请求以备发送,还没真正发送;

       ④只能在同个域中使用相同端口和协议的URL发送请求。

3-2.send()

       send() 1个参数: 请求主体发送的数据,不需要通过请求主体发送数据则传入null。

       调用send()后,请求被分派到服务器。

xhr.open("get","example.php", false) ;
xhr.send(null);
3-3. 收到响应后,响应数据会自动填充XHR对象的属性:

       responseText:作为响应的主体被返回的文本;

       responseXML:若响应的内容类型”text/xml”或”application/xml”,此属性保存响应数据XML DOM文档

       status:响应的HTTP状态;

       statusText:HTTP状态的说明。

☆:无论什么内容类型,响应主体的内容都会保存在responseText属性中。对于非XML数据,responseXML属性值为null。
3-4.status属性确认响应是否成功返回

HTTP状态代:

       200:响应有效,responseText属性已就绪,内容类型正确下的responseXML也可访问。

       304:响应有效,只是请求的资源并为修改,可直接使用浏览器中缓存的版本。

       正确检查上述2种状态代码:

status判断


if ((xhr.status >= 200 && xhr.status <=300) || xhr.status == 304) {
     alert(xhr.responseText);
} else {
     alert("Request was unsuccessful:" + xhr.status);
};
3-5.readystate属性

       该属性存储 请求/响应过程的 当前活动状态。

              0 : 未初始化,未调用open();
              1 : 启动,调用了open();
              2 : 发送,调用了send(),未接受响应;
              3 : 接受,已接受部分响应;
              4 : 完成,已接受全部响应,且可在客户端使用。

3-6.readystatechange事件

       该事件,在readystate属性值改变时触发。



var xhr = createXHR();
xhr.onreadystatechange = function(event){
    if (xhr.readyState == 4){
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            alert(xhr.responseText);
        } else {
            alert("Request was unsuccessful: " + xhr.status);
        }
    }
};
xhr.open("get", "example.txt", true);
xhr.send(null);

       ①必须在调用open()之前知道readystatechange事件的事件处理程序,确保兼容。

       ②该事件处理程序中没有传递event对象,必须通过XHR对象本地来确定下一步怎么做;

       ③使用xhr对象而不使用this对象,是因为onreadystatechange事件处理程序的作用域问题。使用this对象在一些浏览器会导致函数执行失败或发生错误。

3-7.abort()

       调用此方法可取消异步请求:xhr.abort();

       调用后,xhr对象停止触发事件,不允许访问如何与响应相关的属性;

       终止请求后,应对XHR对象进行解引用操作,不建议重用XHR对象。

4、HTTP头部信息

       发送请求时的头部信息:

              Accept:浏览器能够处理的内容类型

              Accept-Charset:浏览器能够显示的字符集

              Accept-Encoding:浏览器能够处理的压缩编码

              Axxept-Language:浏览器当前设置的语言

              Connection:浏览器与服务器之间连接的类型

              Cookie:当前页面设置的如何Cookie

              Host:发送请求耳洞页面所在域

              Referer:发出请求的页面的URI

              User-Agent:浏览器的用户代理字符串

       setRequestHeader()

              设置自定义头部信息。

              2个参数:头部字段名称、头部信息值。

              需在open()方法之后调用send()之前调用setRequestHeader(),才能成功发送请求头部信息。



var xhr = createXHR();      
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            alert(xhr.responseText);
        } else {
            alert("Request was unsuccessful: " + xhr.status);
        }
    }
};
xhr.open("get", "example.php", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);

        getRequestHeader()

              获取指定的相应头部信息

xhr.getRequestHeader(“MyHeader”);
       getAllRequestHeader()

              获取一个包含所有头部信息的长字符串

xhr.getAllRequestHeader();
5、GET请求

  对于XHR对象,位于opne()的URL末尾的查询字符串 需经过编码,使用encodeURIComponent()编码。

  名-值对需用和号(&)分隔。

  自定义函数,添加URL查询字符串参数:


function addURLParam(url,name,value){
    url += (url.indexOf('?') == -1?'?':'&');
    url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
    return url;
}
6、POST请求

  长用于想服务器发送要保存的数据。

  由于XHR其初的设计是为了处理XML,故在send(0中可传入XHR DOM文档。

6-1.服务端读取POST数据

  ①默认情况下,服务器对POST请求和提交Web表单不会一视同仁,故服务端需要程序来读取发送的原始数据,并解析出有用部分。

  ②XHR模拟表单提交:

    1.将Content-Type头部信息设置为application/x-www-form-urlencoded (即表单提交时的内容问题);

    2.以适当格式创建一个字符串。(通过serialize()函数创建该字符串,序列化表单数据)



function submitData(){
    var xhr = createXHR();       
    xhr.onreadystatechange = function(event){
        if (xhr.readyState == 4){
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                alert(xhr.responseText);
            } else {
                alert("Request was unsuccessful: " + xhr.status);
            }
        }
    };

   

    xhr.open("post", "postexample.php", true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    var form = document.getElementById("user-info");           
    xhr.send(serialize(form));
}

 7、CORS 跨源资源共享(IE8+、FF、Chrome....)

   跨域安全策略限制了Ajax的异步通信,CORS则是定义了跨域时,客户端和服务器的沟通。

  CORS思想:使用自定义HTTP头部让浏览器与服务器进行沟通,从而决定请求/响应的成功与否。

7-1.给一个请求附加Origin头部,包含请求页面的源信息(协议、域名 和 端口)

Origin: http://www.domain.com
  服务器根据Origin判断是否接收请求,接收则在Access-Control-Allow-Origin头部会发相同信息。

    (若是公共资源,可以回发"*")

Access-Control-Allow-Origin: http://www.domain.com
  若无此头部或头部信息不匹配,浏览器将驳回请求。

☆请求和响应不会包含cookie信息。

 7-2.IE8+对CORS的实现

  IE8引入的XDR(XDomainRequest)类型,类型XHR,可实现安全可靠的跨域通信。

7-2-1.XDR与XHR的不同之处:

  ①cookie不会随请求发送,也不会随响应返回;

  ②只能设置请求头部信息中的Content-Type字段;

  ③不能访问响应头部信息;

  ④只支持GET和POST请求

XDR缓解了CSRF(跨站请求伪造)和XSS(跨站点脚本)问题

被请求的资源可判断用户代理、来源页面等如何数据 来决定是否设置Access-Control-Allow-Origin头部

 7-2-2. XDR使用方法类似XHR,创建一个XDomainRequest实例,调用open(),再调用send()。

  XDR只能执行异步请求,所以open()方法只有两个参数,请求的类型和URL。

  在收到响应后,只能访问响应的原始文本,无法确定响应的状态代码。

  只要响应有效就会触发load事件,响应的数据会保存在responseText属性中。

  如果失败(如,响应中缺少Access-Control-Allow-Origin头部)就会触发error事件,但该事件无有用信息,需要自定义一个onerror事件句柄。

obload事件-onerror事件
  在请求返回前调用abort()可终止请求。

7-2-3.XDR也支持timeout属性及ontiomout事件处理程序,在运行超过timeout设定的秒数后,调用ontimeout事件句柄。

  为支持POST请求,XDR提供了contentType属性,用于表示发送数据的格式。

    contentType属性是XDR对象影响头部信息的唯一方式。

xdr.contentType = "application/x-www-form-urlencoded";
7-3其他浏览器对CORS的实现

  FF等浏览器都通过XMLHttpRequest对象实现了对CORS的原生支持。要请求另一个域中的资源时,使用标准XHR对象并在open()中传入绝对URL即可。

标准XHR的跨域
   与IE不同,通过跨域XHR对象可以访问status属性和statusText属性,也可同步请求。

7-3-1.跨域XHR的限制:

  ①不能使用setRequestHeader()设置自定义头部;

  ②不能发送和接收cookie;

  ③调用getAllResponseHeader()方法总会返回空字符串。

 7-3-2.无论同源请求还是跨域请求都是使用相同的接口,故对于本地资源,最好用相对URL,对远程资源再用绝对URL。

  这样能消除歧义,避免出现限制访问头部或本地cookie信息等问题。

 7-4、跨浏览器的CORS(IE8+、FF等)

   检测XHR是否支持CORS的方法:检查是否存在withCredentials属性,在结合检测XDomainRequest对象是否存在。



function createCORSRequest(method, url){
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr){
        xhr.open(method, url, true);
    } else if (typeof XDomainRequest != "undefined"){
        xhr = new XDomainRequest();
        xhr.open(method, url);
    } else {
        xhr = null;
    }
    return xhr;
}

var request = createCORSRequest("get", "http://www.somewhere-else.com/xdr.php");
if (request){
    request.onload = function(){
        //do something with request.responseText
    };
    request.send();
}

  上述createCORSRequest()函数返回的对象的属性(XHR和XDR的共同属性):

  ①abort():停止正在进行的请求;

  ②onerror:用于替代onreadystatechange检测错误;

  ③onload:用于代替onreadystatechange检测成功;

  ④responseText:用于取得响应内容;

  ⑤send():用于发送请求。

8、其他跨域技术

  在CORS出现前,常利用DOM中能够执行跨域请求的功能,在不依赖XHR对象时,也能发送某种请求。

  与COSR不同的是,不用修改服务器代码。

8-1.图像Ping

  使用<img>标签,由于可以从任何网页加载图像,故常是在线广告跟踪浏览量的只要方式。

  可动态创建图像,使用它们的onload和onerror事件句柄,确定是否接受到了响应。

var img = new Image();
img.onload = img.onerror = function(){
    alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";
  图像Ping是与服务器进行简单、单向的跨域通信的一种方式。请求的数据通过查询字符串形式发送,响应可以是任何内容,通常是像素图或204响应。虽然通过图像Ping,浏览器得不到任何具体数据,但通过侦听load和error事件,能找到响应收到的时间。

  图像Ping常用于跟踪用户点击页面 或动态广告曝光次数。

  缺点:①只能发送GET请求;②无法访问服务器响应文本。

8-2.JSONP

  JSONP(JSON width Padding)填充式JSON或参数式JSON,类似JSON,是包含在函数调用中的JSON:

callback( {"name" : "value"} );
8-2-1.JSONP有两个部分:回调函数 和 数据

  回调函数:当响应到来时应该在页面中调用的函数。回调函数的名称在请求中指定。

  数据:传入回调函数的JSON数据。

8-2-2.JSONP通过动态<script>元素,为其src属性指定一个跨域的URL。类似<img>元素,即都能不受限制地跨域加载资源。

  JSONP为有效的JavaScript代码,在请求完成即JSONP响应 加载到页面后就会立即执行。



function handleResponse(response){
    alert("You're at IP address " + response.ip + ", which is in " + response.city + ", " + response.region_name);
}

var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

  headleResponse()为回调函数,将在响应到来后执行。

8-2-3.JSONP之所以流行,是因为 :

    ①能够直接访问响应文本;

    ②支持浏览器与服务器之间的双向通信。

  不足:

    ①JSONP从其他域加载代码执行,因此该域必须安全可靠;

    ②很难确保JSONP请求是否失败。

8-3Comet ”服务器推送" 【不兼容IE】

   Ajax从页面想服务器请求数据,Comet则是服务器向页面推送数据,Comet能近乎实时地向页面推送信息。

 8-3-1.实现Comet的2种方式:长轮询 和 流

  ①长轮询:与短轮询相反,页面发送一个请求,服务器一直保持连接打开,直到有数据可发送时就向页面发送数据。接收完数据后浏览器关闭连接,随机又发送一个新请求,在页面打开期间如此循环...

  【短轮询是服务器立即发送数据,即使数据无效,长轮询是等待发送响应。】

  轮询的优点是,所有浏览器都支持。通过XHR对象和setTimeout()实现。

  ②HTTP流:它在网页的整个生命周期内只使用一个HTTP连接。浏览器发送一个请求,服务器保持连接打开,再周期性向浏览器发送数据.



<?php
  $i = 0;
  while (true) {
    //输出一些数据,然后刷新输出缓存
    echo "Number is $1";
    flush();

    //等几秒
    sleep(10);

    $++;
  }
...

  实现HTTP流的关键:所有服务器端语言都支持打印到输出缓存然后刷新的功能。(将输出缓存中的内容一次性全部发送给客户端)



var xhr = new XMLHttpRequest(),
    received = 0;
   
xhr.open("get", url, true);
xhr.onreadystatechange = function(){
    var result;
   
    if (xhr.readyState == 3){
   
        //get only the new data and adjust counter
        result = xhr.responseText.substring(received);
        received += result.length;
       
        //call the progress callback
        progress(result);
       
    } else if (xhr.readyState == 4){
        finished(xhr.responseText);
    }
};
xhr.send(null);
return xhr;
}

var client = createStreamingClient("streaming.php", function(data){
            alert("Received: " + data);
         }, function(data){
            alert("Done!");
         });

通过XHR实现Ajax通信的一个主要限制,来源于跨域安全策略。在默认情况下,Ajax只能访问与包含它的页面位于同一个域中的资源。但是有时也需要一些跨域的请求。为了解决这个问题,现在的浏览器采用CORS(Cross-Origin Resource Sharing,跨域资源共享)策略来实现。CORS是W3C的一个工作草案,定义了必须访问跨源资源时浏览器与服务器之间如何进行沟通。这个策略的基本思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应的成功和失败。注意请求和响应都不包含cookie信息。

IE对CORS的实现
IE8中引入了XDR( XDomainRequest) 类型, 这个对象与XHR类似, 但是能实现安全可靠的跨域通信。 XDR对象的安全机制部分实现了W3C的CORS规范。 以下是XDR与XHR的不同之处:
cookie不会随请求发送, 也不会随响应返回

只能设置请求头部信息中的Content - Type字段

不能访问响应头部信息

只支持GET和POST请求

XDR对象使用方法: 创建一个实例, 调用open方法, 调用send方法。 open方法只接受两个参数: 请求的类型和URL。 所有XDR的请求都是异步的。 请求返回后会触发load事件, 响应数据保存在responseText属性中。

var xdr = new XDomainRequest();
xdr.onload = function() {
    alert(xdr.responseText);
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);
在跨域请求上唯一可以获得响应的信息就是错误本身, 因此确定响应失败的方式就是使用onerror事件。 要放到open之前使用。

xdr.onerror = function() {
    alert("an error occurred!");
}
在请求返回之前, 可以调用取消命令abort() 方法:

xdr.abort(); //终止请求
与XHR一样, XDR对象也支持timeout属性以及ontimeout事件处理程序。

xdr.timeout = 1000;
xdr.ontimeout = function() {
    alert("late!")
}
将这些处理程序添加到open之前才可以哦。
为了支持post请求, XDR对象提供了contentType属性, 用来表示发送数据的格式: 在open之后, send之前设置

xdr.contentType = "application/x-www-form-urlencoded";
其他浏览器对CORS的实现
使用标准的XHR对象并在open()方法中传入绝对URL即可:

var xhr = new XMLHttpRequest();
xhr.onload = function () {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        document.getElementById("content").innerHTML = xhr.responseText;
    } else {
        console.log("error");
    }
};
xhr.open("get", "http://www.somewhere-else.com/page/", true);
xhr.send();
其他的浏览器都通过XMLHttpRequest对象实现了对CORS的原生支持。但是在跨域请求的时候有以下的限制:

不能使用setRequestHeader()设置自定义头部。
不能接受和发送cookie
调用getAllResponseHeaders()方法总会返回空字符串。
无论是同源请求还是跨域请求,对于本地资源最好使用相对URL,在访问远程资源时再使用绝对URL。

Preflighted Requests
透明服务器验证机制,支持开发人员使用自定义的头部,get和post之外的方法,以及不同类型的主题内容。这种请求使用OPTIONS方法,发送下列头部:

Origin:与简单的请求相同
Access-Control-Request-Method:请求自身使用的方法
Access-Control-Request-Headers:(可选)自定义头部信息,多喝头部逗号分隔。
发送请求之后,服务器决定是否允许这种类型的请求。服务器通过响应发送如下的头部与浏览器进行沟通:

Access-Control-Allow-Origin:与简单的请求相同
Access-Control-Allow-Methods:允许的方法,多个方法以逗号分隔
Access-Control-Allow-Headers:允许的头部,多个头部以逗号分隔
Access-Control-Max-Age:应该将这个Preflight请求缓存多长时间(以秒表示)
带凭据的请求
通过将withCredentials属性设置为true,可以指定特定的请求应该发送凭据。如果服务器接收带凭据的请求,会用下面的HTTP头部响应

Access-Control-Allow-Credentials:true
跨浏览器的CORS
function createCORSRequest(method, url) {
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr) {
        xhr.open(method, url, true);
    } else if (typeof XDomainRequest != "undefined") {
        xhr = new XDomainRequest();
        xhr.open(method, url);
    } else {
        xhr = null;
    }
    return xhr;
}
var request = createCORSRequest("get", "http://somewhere-else.com/page/");
if (request) {
    request.onload = function() {
        //request.responseText
    };
    request.send();

}

有以下6个进度事件:

loadstart: 在接收到响应数据的第一个字节时触发。
progress: 在接收响应数据期间持续的触发
error: 在请求发生错误时触发
abort: 在因调用abort() 方法而终止连接时触发
load: 在接收到完整的响应数据时触发
loadend: 在通信完成或者触发error, abort, load事件后触发。
现在所有的主流浏览器都支持load事件, 前五个除了IE其他的都支持

load事件
下面是使用示例:

xhr.onload = function() {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        alert(xhr.responseText);
    } else {
        alert("Request was unsuccessful:" + xhr.status);
    }
}
放到open方法之前。

如:

var xhr = new XMLHttpRequest();
// xhr.onreadystatechange = function () {
//     if (xhr.readyState == 4) {
//         if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
//             console.log(xhr.responseText);
//         } else {
//             console.log("error");
//         }   
//     }
// };
xhr.onload = function () {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        console.log(xhr.responseText);
    } else {
        console.log("error");
    }
};
xhr.open("get", "getexample.php", true);
xhr.send(null);
响应接收完毕后将触发load事件,因此也就没有必要去检查readyState属性了。

progress事件
onprogress事件处理程序会接收一个event对象,其target属性是XHR对象,但包含着三个额外属性:

lengthComputable
position
totalSize
其中lengthComputable表示进度信息是否可用的布尔值,position表示已经接收的字节数,totalSize表示根据Content-Length响应头部确定的预期字节数。

根据这些信息,就可以创建一个进度指示器:

xhr.onprogress = function () {
    if (event.lengthComputable) {
        document.getElementById("status").innerHTML = "Received " + event.position + " of " + event.totalSize + " bytes.";
    }
};
要注意的是position已改为loaded;totalSize已改为total。

并非所有的浏览器都完整的实现了XMLHttpRequest 2 级的规范, 但是所有的浏览器都实现了它部分的规范。

FormData
FormData类型
append()向其添加数据,包含两个参数:键和值;
如:

var data = new FormData();
data.append("name", "oliver");
也可以用表单元素的数据预先想其中填入键值对:

var data = new FormData(document.forms[0]);
它是为序列化表单以及创建于表单格式相同的数据提供了遍历:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        } else {
            console.log("error");
        }
    }
};
xhr.open("post", "postexample.php", true);
var form = document.getElementById("form1");
xhr.send(new FormData(form));
它的方便之处在于不用明确的在XHR对象上设置请求头部。

超时设定
IE8+唯一支持的超时设定事件,XHR对象的ontimeout事件。XHR对象的timeout设定超时时间,单位是毫秒数。这些设定要方法open之后,send之前。

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        } else {
            console.log("error");
        }
    }
};
xhr.open("get", "getexample.php", true);
xhr.timeout = 1000;
xhr.ontimeout = function () {
    alert("Request did not return in a second.");
};
xhr.send(null);
overrideMimeType()方法
用于重写XHR响应的MIME类型。它能强迫服务器返回的数据类型给些为本方法提供的类型。使用方法:
在open之后,send之前。

xhr.open("get", "getexample.php", true);
xhr.overrideMimeType("text/xml");
xhr.send(null);

Ajax:Asynchronous JavaScript + XML的简写。
Ajax技术的核心是XMLHttpRequest对象(XHR),XHR为向服务器发送请求和解析服务器响应提供了流畅的接口。能够以异步方式从服务器取得信息,再通过DOM将新数据插入到页面中。
XMLHttpRequest对象

IE7+ Firefox Opera Chrome Safari原生支持XHR对象。
var xhr = new XMLHttpRequest();
XHR的用法

在使用XHR对象时,要调用的第一个方法是open(),它接受3个参数:要发送的请求类型(get,post)、请求的URL、表示是否异步发送请求的布尔值。
这时就启动了一个请求以备发送。
xhr.open("get", "example.php", false);
只能向同一个域中使用相同端口和协议的URL发送请求,如果URL与启动请求的页面有任何差别,都会引发安全错误。
要发送请求就要调用send()方法:
xhr.send(null);
send方法接收的参数是要作为请求主体发送的数据,如果不需要数据就传null,因为有的浏览器必须要传这个数据。
如果请求是同步的,那么JS会在等到服务器响应之后再继续执行。
等到响应后,响应的数据会自动填充XHR对象的属性:
responseText:响应主体的文本 responseXML:这个属性中保存着包含响应数据的XML DOM文档 status:响应的HTTP状态 statusText:HTTP状态的说明
var xhr = new XMLHttpRequest(); xhr.open("get", "16_.html", false); xhr.send(null); if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); }
在发送异步请求时JS不会等待响应,此时的XHR会有readyState属性,表示请求响应过程的当前活动阶段:
0:未初始化,尚未调用open方法 1:启动,已经调用open未调用send 2:发送,已经调用send,未收到响应 3:接收,已经接收到部分响应数据 4:完成,已经接收到全部响应数据,且数据可用
readyState属性发生改变时会触发一次readystatechange事件,为确保浏览器兼容,需要在调用open前指定readystatechange事件处理程序。
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ alert(xhr.readyState); if (xhr.readyState == 4){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } }; xhr.open("get", "16_.html", true); xhr.send(null);
在接收到响应之前可以通过abort方法取消异步请求
xhr.abort();
在终止请求,应该解引用XHR对象,由于内存原因不建议重用XHR对象。
HTTP头部信息

每个HTTP请求和响应都会带有相应的头部信息,XHR对象也提供了操作这两种头部信息的方法。
默认情况下,在发送XHR请求的同时还会发送下列头部:
Accept Accept-Charset Accept-Encoding Accept-Language Connection Cookie Host Referer User-Agent
使用setRequestHeader()方法可以设置自定义的请求头部。参数为头部字段的名称和头部字段的值。这个要在open和send之间调用才有效。
xhr.setRequestHeader("MyHeader", "MyValue");
在使用这个方法时,建议使用自定义的头部名,以免与浏览器发生冲突,有的浏览器可能不允许开发人员重写默认头部。
想要获得头部的值可以使用下面这两种方法。
var myHeader = xhr.getResponseHeader("MyHeader"); var allHeaders = xhr.getAllResponseHeaders();
GET请求

用于向服务器查询某些信息,将参数放到后面。使用addURLParam就是保证URI被正确编码,格式良好。
var xhr = new XMLHttpRequest(); function addURLParam(url, name, value) { url += (url.indexOf("?") == -1 ? "?" : "&"); url += encodeURIComponent(name) + "=" + encodeURIComponent(value); return url; } var url = "example.php"; url = addURLParam(url, "name", "Nicholas"); url = addURLParam(url, "book", "Professional JavaScript");  xhr.open("get", url, true); xhr.send(null);
POST请求

用于向服务器发送应该保存的数据,POST请求应该会发送很多的数据到服务器。这时send的参数就是发送的数据了。一般是数据序列化后的字符串。
var xhr = new XMLHttpRequest(); xhr.open("post", "postexample.php", true); //模仿表单提交 xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); var form = document.getElementById("user-info"); xhr.send(serialize(form));
GET请求消耗的资源少,同等数据量是POST的两倍。
XMLHttpRequest 2级

FormData

Web应用中频繁使用的一项功能就是表单数据的序列化。为此,2级定义了FormData对象。
append方法可以向其添加数据,键值对形式
var data = new FormData(); data.append("name", "Nicholas");
也可以直接使用表单初始化FormData:
var data = new FormData(document.forms[0]); xhr.send(data);
创建好的FormData的实例后直接传给XHR的send方法,这时XHR会自动帮你照顾好头部信息。
超时设定

2级中又规定了一个timeout属性,表示请求在等待响应多少毫秒之后就终止。设置之后,如果超时没有接收到响应,就回触发timeout事件,调用事件处理程序,这时xhr.readyState可能已经为4了,但是此时请求已经中止了,不能再访问xhr.status,所以如果使用timeout,onreadystatechange事件处理也要小心。
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ try { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } catch (ex){  } } }; xhr.open("get", "timeout.php", true); xhr.timeout = 1000; // IE8+ xhr.ontimeout = function(){ alert("Request did not return in a second."); }; xhr.send(null);
overrideMimeType()方法

重写XHR响应的MIME类型,因为响应的MIME类型决定了XHR对象如何处理它。如果服务器返回的是XML文件,MIME却是text/plain,那XHR对象就不能正确的设置responseXML。
要在send之前调用这个方法。
var xhr = createXHR(); xhr.open("get", "text.php", true); xhr.overrideMimeType("text/xml"); xhr.send(null);
进度事件

Progress Events定义了与客户端服务器通信有关的事件,这些事件最早其实只针对XHR,但目前也被其它API借鉴,有6个进度事件:
loadstars:在接收到响应数据的第一个字节时触发 progress:在接收响应期间不断触发 error:在请求发生错误时触发 abort:在因为调用abort()方法而终止连接时触发 load:在接收到完整响应数据时触发 loaded:在通信完成(触发error、abort、load事件后)时触发
支持前5个事件的有Firefox 3.5+、Safari 4+、Chrome、iOS 版Safari、Android 版 WebKit。
Opera 11+、IE8+只支持load事件。
load事件

只要浏览器接收到服务器的响应,不管其状态如何都会触发load事件,这就意味着必须检查XHR对象的status属性。对于这个事件有的浏览器实现了event对象,其target指向XHR对象,直接就可以使用所有对象和方法,可是有的浏览器并没有实现。这就是说还得使用原来的全局XHR变量对象。
var xhr = new XMLHttpRequest(); xhr.onload = function(){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } }; xhr.open("get", "altevents.php", true); xhr.send(null);
progress事件

这个事件会在浏览器接收数据的过程中持续的触发,其target属性为XHR对象再加额外3个属性:
lengthComputable:表示进度信息是否可用 position:表示已经接收到的字节数 totalSize:根据Content-Length响应头部确定的预期字节数
这个可以用来作为进度指示器
var xhr = new XMLHttpRequest(); xhr.onprogress = function(event){ var divStatus = document.getElementById("highDiv"); if (event.lengthComputable) { divStatus.innerHTML = "Received " + event.position + " of " + event.totalSize + " bytes"; } }; xhr.open("get", "img/paypal2.png", true); xhr.send(null);
跨源资源共享

通过XHR实现Ajax通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意行为,但实现合理的跨域请求也是一个很重要的需求。
CORS(Cross-Orign Resource Sharing)就是为了这样的需求而生的。
其背后的思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应应该成功还是失败。
比如一个简单的使用GET或POST发送的请求,添加一个自定义头部Origin来包含请求页面的源信息(协议,域名和端口),以便服务器根据这个头部信息来决定是否响应。如果服务器认为这个请求可以接受,那么就在Access-Control-Allow-Origin头部中发回相同的源或*,浏览器会检查这个头部,如果不符合则驳回请求。
这里的跨域请求和响应都不会包含cookie。
IE对CORS的实现

IE8中引入了XDR(XDomainRequest)类型,它与XHR相似,是用来实现安全可靠的跨域通信的。在安全机制方面,XDR实现了部分CORS规范:
cookie不会随请求发送,也不会随响应返回 只能设置请求头部中的Content-Type字段 不能访问响应头部 只支持GET和POST
这些变化使得CSRF跨站点请求伪造和跨站点脚本的问题得到缓解,被请求的资源可以根据它认为合适的请求来决定是否设置Access-Control- Allow-Origin。作为请求的一部分Origin会自动设置为当前域。XDR只能发送异步请求。请求返回后会触发load事件。
var xdr = new XDomainRequest(); xdr.onload = function(){ alert(xdr.responseText); }; xdr.onerror = function(){ alert("An error occurred."); }; xdr.open("post", "http://www.somewhere-else.com/page/"); xdr.contentType = "application/x-www-form-urlencoded"; xdr.send("name1=value1&name2=value2");
因为无法读取响应头部,也就无法获取状态码。只要响应有效就回触发load事件,失败就触发error事件。
abort方法可以终止请求
XDR也有timeout属性和ontimeout事件处理程序。
其它浏览器对CORS的实现

其它大多数浏览器并没有使用新类型的对象来实现CORS,而是直接使用XHR原生支持。
如果要访问其他域下的资源在XHR的open方法中传入绝对URL即可。
这里不仅可以访问到status和statusText属性,还可以发起同步请求。
不过限制还是有的:
不能发送接受cookie 不能使用setRequestHeader()设置自定义头部 调用getAllResponseHeaders()总会返回空字符串
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } }; xhr.open("get", "http://www.somewhere-else.com/page/", true); xhr.send(null);
Preflighted Reqeusts

CORS使用Preflighted Reqeusts的透明服务器验证机制支持开发人员使用自定义头部和GET或POST之外的方法以及不同类型的主体内容。
在使用下列高级选项来发送请求时,就会向服务器发送一个Preflight请求。这种请求使用OPTIONS方法
Origin:与简单请求相同 Access-Control-Request-Method:请求自身使用的方法 Access-Control-Request-Headers:自定义的头部
Origin: http://www.nczonline.net Access-Control-Request-Method: POST Access-Control-Request-Headers: NCZ
发送这个请求后,服务器可以决定是否允许这种类型的请求并发送响应头部与浏览器沟通:
Access-Control-Allow-Origin Access-Control-Allow-Methods Access-Control-Allow-Headers Access-Control-Max-Age:这个Preflighted请求应该被缓存多久
Access-Control-Allow-Origin: http://www.nczonline.net Access-Control-Allow-Methods: POST, GET Access-Control-Allow-Headers: NCZ Access-Control-Max-Age: 1728000
IE10及之前的版本不支持
带凭据的请求

默认情况下,跨源请求不带凭据(cookie,HTTP认证,SSL证明等)通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。如果服务器接受带凭据的请求,会响应:Access-Control-Allow-Credentials: true。如果发送的是带凭据的请求,而服务器中的响应没有包含这个头部,那么JS获取不到这个响应中的信息。
跨浏览器的CORS

function createCORSRequest(method, url){ var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr){ xhr.open(method, url, true); } else if (typeof XDomainRequest != "undefined"){ xhr = new XDomainRequest(); xhr.open(method, url); } else { xhr = null; } return xhr; } var request = createCORSRequest("get", "http://www.baidu.com/"); if (request){ request.onload = function(){ alert(request.responseText); }; request.send(); }
其它跨域技术

在CORS出现之前,开发人员通过各种hack方式来实现跨域Ajax。
图像Ping

这个利用的是一个网页可以从任何地方加载图像,而不用担心跨域的问题。通过向一个URL请求图片的方式,可以向服务器单向使用GET传送参数,由于img元素的本身限制,并不能获取服务器发来的除图像以外的数据(响应文本)。只能通过监听load和error事件知道响应何时收到。
var img = new Image(); img.onload = img.onerror = function(){ alert("Done!"); }; img.src = "http://www.example.com/test?name=Nicholas";
响应从图片对象的src被设置时就开始了。
这个最大的用处就是跟踪用户点击或广告曝光次数,网页被打开了就使用img向纪录服务器发送个规定的参数,简单可靠。
不过只能使用GET方法和无法获得响应文本是其局限。
JSONP

JSON with padding参数式JSON
JSONP利用动态script元素从其他域中加载资源,将服务器传回的数据以JSON格式传入一个回调函数中,形式就像这样:
callback({ "name": "Nicholas" });
在实际使用时先创建一个用于处理数据的回调函数,创建一个script对象,将回调函数的函数名作为其src的URL的一个参数传递给服务器端,服务器端根据这个URL中的参数们取得响应的数据,并作为函数参数塞到回调函数中,返回一个像上面形式的对回调函数的调用。这是一个可执行的JS,被放在了一个动态创建的script元素中,当你把这个元素插入文档,就相当于执行了这个有服务器数据作为参数的回调函数。
function handleResponse(response){ alert("You re at IP address " + response.ip + ", which is in " + response.city + ", " + response.region_name); } var script = document.createElement("script"); script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);
这个就可以实现浏览器和服务器间的双向通信了,不过有两点问题:
必须确保你请求的Web服务是可靠的,否则就是你亲手给恶意代码提供了执行的机会 对于确定何时接到请求或请求何时失败并不好判断,script元素在HTML5规范中是有error事件的,不过没人实现,现在一个比较笨的解决办法是使用计时器手动检测请求超时
Comet

Comet是一种服务器像浏览器推送数据的技术。可以让信息近乎实时的被推送到页面上。有两种实现Comet的方式,长轮询和流。
长轮询
页面发起一个到服务器的请求,服务器连接一直打开,知道有数据可发送,浏览器接收完数据后关闭该连接,发起一个新的请求。

HTTP流的实现方式则不同,在页面的整个生命周期内只会有一个HTTP连接。浏览器向服务器发送一个请求,服务器保持连接打开,周期性的向浏览器发送数据。一般来说,都是把新的数据加到之前输出缓存的末尾再重新都发一遍。
通过监听readystatechange事件,当readyState为3时就代表数据都接收到了。随着不断从服务器端接收到数据,readyState会周期性的变为3。这时就可以读取responseText中的数据,并与上次接收到的做比对,获取新的数据。
function createStreamingClient(url, progress, finished){ var xhr = new XMLHttpRequest(), received = 0; xhr.open("get", url, true); xhr.onreadystatechange = function(){ var result; if (xhr.readyState == 3){ // 数 数 result = xhr.responseText.substring(received); received += result.length; // progress 数 progress(result); } else if (xhr.readyState == 4){ finished(xhr.responseText); } }; xhr.send(null); return xhr; } var client = createStreamingClient("streaming.php", function(data){ alert("Received: " + data); }, function(data){ alert("Done!"+ data); });
Comet连接比较容易出错,不过大家都看好这项技术,为了简化这项技术的使用成本,提高可靠性,又有两个新的接口出现了。服务器发送事件(Server-Sent Events,SSE)和Web Socket。
服务器发送事件

Server-Sent Events,SSE,这是围绕只读Comet推出的API,用于创建到服务器的单向连接。服务器通过这个连接可以发送任意数量的数据。服务器响应的MIME类型必须是text/event-stream,并且是浏览器中的JS能解析的格式输出。SSE支持短轮询,长轮询,HTTP流。并且在断开连接时自动确定何时重连。
支持SSE的浏览器有:Firefox 6+ Safari 5+ Opera 11+ Chrome iOS 4+版 Safari。
SSE API
首先新建一个EventSource对象,参数为入口点URL,这个URL要与创建对象的页面同源(相同URL模式,域,及端口)。EventSource对象的readyState属性:
0:正在连接服务器 1:打开了连接 2:关闭了连接。
三个事件:
open建立连接时 message收到新服务器事件时 error连接错误时
在连接断开时会自己重新连接。这就非常适合长轮询,HTTP流。
想要手动断开,有close方法。
var source = new EventSource("myevents.php"); source.onmessage = function(event){  var data = event.data;  }; source.close();
事件流
服务器事件会通过一个持久的HTTP响应发送,这个响应的MIME类型为text/event-stream,格式为纯文本。最简单的情况是每个数据项都有前缀data:
data: foo  data: bar  data: foo data: bar
对应的message事件有3个,event.data的值分别为foo,bar,foo/bar。在服务器端要注意,只有data:后面有空行时才回触发message事件。
data: foo id: 1  id: 2 data: foo data: bar
id可以将事件与一个ID绑定,id的位置可以在data的前后。如果连接断开,会向服务器发送Last-Event-ID头部,以便服务器知道下次该发什么。
Web Socket

其目标是在一个单独的持久连接上提供一个全双工的双向通信。在Web Socket建立以后,会有一个HTTP请求发送到服务器,在取得响应后,连接会开始使用Web Socket协议。其URL模式为ws:// , wss://。
使用Web Socket意味着更小的开销,但是同时这个协议的问题还在不断的被发现,现在支持这个协议的有Firefox 6+ Safari 5+ Chrome iOS 4+版 Safari。
Web Sockets API
Web Socket的构造函数只接受绝对URL。并不要求同源,因为服务器可以判断并决定连接可以获取什么样的资源。
实例化后,连接马上会创建。readyState:
WebSocket.OPENING (0) WebSocket.OPEN (1) WebSocket.CLOSING (2) WebSocket.CLOSE (3)
关闭时使用close()
发送和接收
发送只能是纯文本数据,所以复杂的数据结构要序列化。
var socket = new WebSocket("ws://www.example.com/server.php"); var message = { time: new Date(), text: "Hello world!", clientId: "asdfp8734rew" }; socket.send(JSON.stringify(message));
接受时会触发message事件
socket.onmessage = function(event){ var data = event.data; };
其他事件
open,error,close
安全

跨站请求伪造是Ajax遇到的最大问题,如果你可以通过Ajax的URL获取,直接输URL也可以。
所以我们要确认请求发送者是否有访问资源的权限。有下列方式可以选择:
SSL保护 每次请求附带算法计算的验证码
下面的方法没用,很容易伪造:
检查URL是否可信 基于cookie验证
XHR对象本身提供了一些安全机制,open 方法其实可以传用户名和密码。可是在JS中保存这些,用调试器一下就看到明文了。







猜你喜欢

转载自zhyp29.iteye.com/blog/2304717