JavaScript高级程序设计读书笔记21

第21章 Ajax与Comet

  • Ajax核心是XMLHttpRequest对象,能够以异步的方式从服务器获取更多信息,意味着用户单击后,可以不必刷新页面也能取得数据

XMLHttpReques对象

  • 浏览器兼容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}else{
throw new Error("No XHR Object available");
}
}
//使用示例
var xhr = createXHR();

HXR的用法

  • open,接收3个参数:要发送的请求的类型(“get”,“post”)、请求的URL、表示是否异步发送请求的布尔值
  • URL相对于执行代码的当前页面
  • 调用open()方法并不会真正发送请求
  • send接受一个参数:作为请求主体发送的数据,不需要则要传入null。
  • 请求是同步的,JavaScript代码会等到服务器响应之后再继续执行。在收到响应后,响应的数据会自动填充xhr对象的属性:

    responseText: 作为响应主体被返回的文本。
    responseXML: 如果响应的内容类型是”text/xml”或”application/xml”,这个属性中将保存包含着响应数据> 的XML DOM文档。
    status: 响应的HTTP状态。
    statusText: HTTP状态的说明。

1
2
3
4
5
6
7
8
xhr.open("get","example.php",false);               //同步请求
xhr.send(null);

if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.resopnseText);
}else{
alert("Request was unsuccessful: "+xhr.status);
}
  • 如果要发送异步请求,可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段:

    0: 未初始化。尚未调用open()方法
    1: 启动。已经调用open()方法但尚未调用send()方法
    2:发送。已经调用send()方法但尚未接收到响应。
    3:接受。已经接受到部分响应数据。
    4:完成。已经接受到全部响应数据,而且可以在客户端使用了。

  • readyState属性的值由一个值变成另一个值,都触发一次readystatechange事件。

  • 必须在调用open之前指定onreadystatechange事件处理程序才能确保跨浏览器兼容性
1
2
3
4
5
6
7
8
9
10
11
12
var xhr = createXHR();
xhr.onreadystatechange = function(){ //DOM0级方法,不是所有浏览器都支持DOM2级方法
if(xhr.readyState == 4){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.resopnseText);
}else{
alert("Request was unsuccessful: "+xhr.status);
}
}
};
xhr.open("get","example.php",true); //异步请求
xhr.send(null);

HTTP头部

  • 默认情况下,发送XHR请求的同时,还会发送下列头部信息

    Accept:浏览器能够处理的内容类型
    Accept-Charset:浏览器能够显示的字符集
    Accept-Encoding:浏览器能够处理的压缩编码
    Accept-Language:浏览器当前设置语言
    Connection:浏览器与服务器之间的链接类型
    Cookie:当前页面设置的热河Cookie
    Host:发出请求的页面所在的域
    Referer:发出请求的页面的URI。(正确拼法是referrer)
    User-Agent:浏览器的用户代理字符串

  • setRequestHeader方法设置自定义的请求头部信息。接收两个参数:头部字段名称,头部字段值,

  • 要成功发送请求头部信息,必须在调用open方法之后,调用send方法之前调用setRequestHeader
  • 调用XHR对象的getRequestHeader方法并传入头部字段名称,可以取得相应头部信息
  • 调用getAllResponseHeaders()方法则可以获得一个包含所有头部信息的长字符串。

GET请求

  • GET请求最常用语想服务器查询某些信息
  • 经常发生的一个错误,就是查询字符串的格式有问题,查询字符串的每个参数的名称和值都必须使用encodeURIComponent()编码,然后才能放到URL末尾
  • 下面这个函数可以辅助向现有URL的末尾添加查询字符串参数
1
2
3
4
5
function addURLParam(url, name, value){
url += (url.indexOf("?")) == -1? "?" : "&";
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}

POST请求 ###​

  • 通常用于向服务器发送应该保存的数据,应该把数据作为请求的主体提交,可以包含非常多的数据,且格式不限。
  • 如果需要将页面中的表单数据进行序列化,然后通过XHR放到服务器,则可以使用14章的serialize函数来创建字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function submitData(){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.resopnseText);
}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));
}

XMLHttpRequest 2级

FormData

  • FormData类型:为序列化表单以及创建与表单格式相同的数据(用于通过XHR传输)提供了便利。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
var form = document.getElementById("user-info");
xhr.send(new FormData(form));
}

超时设定

  • timeout属性,表示请求在等待响应多少毫秒之后就终止。如果在规定的时间内浏览器还没有接收到响应,那么就会触发timeout事件,进而会调用ontimeout事件处理程序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var xhr = createXHR();        
xhr.onreadystatechange = function(event){
try {
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
} catch (ex){
//assume handled by ontimeout
}
};
xhr.open("get", "timeout.php", true);
xhr.timeout = 1000;
xhr.ontimeout = function(){
alert("Request did not return in a second.");
};
xhr.send(null);

overrideMimeType()方法

  • overrideMimeType()方法:重写XHR响应的MIME类型
  • 必须在send()方法之前调用overrideMimeType(),才能保证重写相应的MIME类型
1
2
3
4
var xhr = createXHR();
xhr.open("get", "text.php", true);
xhr.overrideMimeType("text/xml");
xhr.send(null);

进度事件

  • loadstrart:在接收到响应数据的第一个字节时触发。
  • progress:在接收响应期间持续不断地触发。
  • error:在请求发生错误时触发
  • abort:在因为调用abort()方法而终止连接时触发。
  • load:在接收到完整的响应数据时触发。
  • loadend:在通信完成或者触发error、abort或load事件后触发。

load事件

  • 用于替代readystatechange事件。onload事件处理程序会接收到一个event对象,其target属性就指向XHR对象实例,因而可以访问到XHR对象的所有方法和属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
window.onload = function(){
var xhr = createXHR();
xhr.onload = function(event){
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事件

  • 这个事件会在浏览器接收新数据期间周期性地触发。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
window.onload = function(){
var xhr = createXHR();
xhr.onload = function(event){
if ((xhr.status >= 200 && xhr.status < 300) ||
xhr.status == 304){
console.log(xhr.responseText);
} else {
console.log("Request was unsuccessful: " + xhr.status);
}
};
xhr.onprogress = function(event){
var divStatus = document.getElementById("status");
if (event.lengthComputable){
divStatus.innerHTML = "Received " + event.position + " of " + event.totalSize + " bytes";
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
};

跨资源共享 CORS

  • 在发送请求时,需要给它附加一个额外的Origin头部,其中包含请求页面的源信息,以便服务器根据这个头部信息来决定是否给予响应。
  • 如果服务器认为这个请求可以接受,就在Access-Control_Allow-Origin头部中发相同的源信息。

IE对CORS的支持

  • IE8中引入了XDR(XDomainRequest)类型,与XHR类似,但能实现安全可靠的跨域通信。与XHR不同:

    1.cookie不会随请求发送,也不会随请求返回
    2.只能设置头部信息中的Content-Type
    3.不能访问响应头部信息
    4.只支持GET和POST请求

  • 使用方法,也是创建一个XDomainRequest实例,调用open,调用send,open方法只接受两个参数,请求类型,URL。所有请求都是异步。

其他浏览器对CORS的支持

  • Firefox3.5+,Safari4+,Chrome,ios版Safari,Android平台中的WebKit都通过XHR对象实现了对CORS的原生支持。
  • 限制:

    1.不能使用setRequestHeader()设置自定义头部
    2.不能发送和接受cookie
    3.调用getAllResponseHeaders()方法总会返回空字符串

Preflighted Reqeusts

  • CORS通过一种叫做PreFlighted Requests的透明服务器验证机制支持使用自定义头部、get和post之外的方法以及不同类型的主体内容。

带凭据的请求

  • 默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。
  • 服务器如果接受带凭据的请求,会响应Access-Control-Allow-Credentials: true

跨浏览器的CORS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
}

其他跨域技术

图像ping

  • 图像ping最常用于跟踪用户点击页面或动态广告曝光次数。
  • 图像ping有两个主要的缺点:

    只能发送get请求
    无法访问服务器的响应文本。

  • 因此,图像ping只能用于浏览器与服务器的单向通信。

1
2
3
4
5
var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";

JSONP

  • JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名称一般是在请求中指定的。而数据就是传入回调函数中的JSON数据。
  • 与图像Ping相比优点:

    能够直接访问相应文本
    支持在浏览器与服务器之间双向通信

  • 不足:

    是从其他域中家在代码执行,可能会夹带一些恶意代码
    要确定JSONP请求是否失败并不容易

Comet

  • Comet一种更高级的Ajax技术。Ajax是从页面向服务器请求数据的技术,Comet是服务器向页面推送数据的技术。能够让信息近乎实时的被推送到页面上。
  • 实现Comet的方式:长轮询和流

服务器发送数据

  • SSE(Server-Sent Events,服务器发送事件)是围绕只读Comet交互推出的API或者模式

Web Socket

  • Web Socket的目标是在一个单独的持久连接上提供全双工、双向通信。
  • 使用标准的HTTP服务器无法实现Web Socket,只有支持这种协议的专门服务器才能工作。

Web Sockets API

  • 表示当前状态的readyState属性:

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

发送和接收数据

  • send():只能发送纯文本数据,发杂结构的数据要经过序列化。

其他事件

  • open:成功建立连接触发
  • error:发生错误触发,连接不能持续
  • close:连接关闭触发

安全

  • 为确保通过XHR访问的URL安全,通行的做法就是验证发送请求者是否有权限访问相应的资源,有下列几种方式可供选择

    要求以SSL连接来访问可以通过XHR请求的资源
    要求每一次请求都要附带经过相应算法计算得到的验证码。