1得问、XMLHttpRequest 對象
- 在瀏覽器中創(chuàng)建XHR 對象
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined"){
//兼容IE7以下瀏覽器
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();
1.1 XHR 的用法
- 在使用XHR 對象時剩檀,要調(diào)用的第一個方法是open()铲汪,它接受3個參數(shù):要發(fā)送的請求的類型("get"葵诈、"post"等)、請求的URL 和表示是否異步發(fā)送請求的布爾值劲适。
- 調(diào)用open()方法并不會真正發(fā)送請求楷掉,而只是啟動一個請求以備發(fā)送。要發(fā)送特定的請求,需要調(diào)用send(),它接受一個參數(shù)霞势,即要作為請求主體發(fā)送的數(shù)據(jù)靖诗。
- 如果不需要通過請求主體發(fā)送數(shù)據(jù)郭怪,則必須傳入null,因為這個參數(shù)對有些瀏覽器來說是必需的刊橘。
xhr.open("get", "example.txt", false);
xhr.send(null);
- 由于請求是同步的鄙才,JavaScript代碼會等到服務(wù)器響應(yīng)之后再繼續(xù)執(zhí)行。在收到響應(yīng)后促绵,響應(yīng)的數(shù)據(jù)會自動填充XHR 對象的屬性攒庵。
(1)responseText:作為響應(yīng)主體被返回的文本。
(2)responseXML:如果響應(yīng)的內(nèi)容類型是“text/xml”或者“application/xml”败晴,這個屬性將保存包含響應(yīng)數(shù)據(jù)的XML DOM 文檔浓冒。
(3)status:響應(yīng)的HTTP狀態(tài)
(4)statusText:HTTP 狀態(tài)的說明
- 一般來說,可以將HTTP狀態(tài)代碼為200 作為成功的標志尖坤。
- 狀態(tài)代碼為304 表示請求的資源并沒有被修改稳懒。
xhr.open("get", "example.txt", false);
xhr.send(null);
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
- 發(fā)送異步請求時,可以檢測XHR對象的readyState屬性慢味,該屬性表示請求/響應(yīng)過程的當前活動階段场梆。這個屬性可取的值如下。
- 0:未初始化纯路。尚未調(diào)用open()方法或油。
- 1:啟動。已經(jīng)調(diào)用open()方法驰唬,但尚未調(diào)用send()方法顶岸。
- 2:發(fā)送。已經(jīng)調(diào)用send()方法叫编,但尚未收到響應(yīng)辖佣。
- 3:接收。已經(jīng)接收到部分響應(yīng)數(shù)據(jù)搓逾。
- 4:完成凌简。已經(jīng)接收到全部響應(yīng)數(shù)據(jù),而且已經(jīng)可以在客戶端使用了恃逻。
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.txt", true);
xhr.send(null);
- 在接收到響應(yīng)之前還可以調(diào)用abort()方法來取消異步請求雏搂。
xhr.abort();
- 調(diào)用這個方法后,XHR對象會停止觸發(fā)事件寇损,而且也不再允許訪問任何與響應(yīng)有關(guān)的對象屬性凸郑。
- 在終止請求之后,還應(yīng)該對XHR對象進行解引用操作矛市。由于內(nèi)存原因芙沥,不建議重用XHR 對象。
1.2 HTTP頭部信息
- 每個HTTP 請求和響應(yīng)都會帶有相應(yīng)的頭部信息。XHR對象也提供了操作這兩種頭部信息的方法而昨。
- 默認情況下救氯,在發(fā)送XHR請求的同時,還會發(fā)出下列頭部信息歌憨。
- Accept:瀏覽器能夠處理的內(nèi)容類型着憨。
- Accept-Charset:瀏覽器能夠顯示的字符集。
- Accept-Encoding:瀏覽器能夠處理的壓縮編碼务嫡。
- Accept-Language:瀏覽器當前設(shè)置的語音甲抖。
- Connection:瀏覽器與服務(wù)器之間連接的類型。
- Cookie:當前頁面設(shè)置的任何Cookie心铃。
- Host:發(fā)出請求的頁面所在的域准谚。
- Referer:發(fā)出請求的頁面的URI。
- User-Agent:瀏覽器的用戶代理字符串去扣。
- 使用setRequestHeader()方法可以設(shè)置自定義的請求頭部信息柱衔。這個方法接受兩個參數(shù):頭部字段的名稱和頭部字段的值。
- 要成功發(fā)送請求頭部信息愉棱,必須在調(diào)用open()方法之后且調(diào)用send()方法之前調(diào)用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);
- 調(diào)用XHR 對象的getResponseHeader()方法并傳入頭部字段名稱唆铐,可以取得相應(yīng)的響應(yīng)頭部信息。
- 調(diào)用getAllResponseHeaders()方法則可以取得一個包含所有頭部信息的長字符串羽氮。
var myHeader = xhr.getResponseHeader("MyHeader");
var allHeaders = xhr.getAllResponseHeaders();
1.3 GET 請求
- GET 是最常見的請求類型,最常用于向服務(wù)器查詢某些信息惫恼。必要時档押,可以將查詢字符串參數(shù)追加到URL 的末尾,以便將信息發(fā)送給服務(wù)器祈纯。
- 對XHR 而言令宿,位于傳入open()方法的URL末尾的查詢字符串必須經(jīng)過正確的編碼才行。
- 查詢字符串中每個參數(shù)的名稱和值都必須使用encodeURIComponent()進行編碼腕窥,然后才能放到URL的末尾粒没;而且所有名-值對兒都必須由和號(&)分隔。
xhr.open("get", "example.php?name1=value1&name2=value2", true);
//向現(xiàn)有URL 的末尾添加查詢字符串參數(shù)
function addURLParam(url, name, value) {
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
var url = "example.php";
//添加參數(shù)
url = addURLParam(url, "name", "Nicholas");
url = addURLParam(url, "book", "Professional JavaScript");
//初始化請求
xhr.open("get", url, false);
1.4 POST 請求
- POST請求通常用于向服務(wù)器發(fā)送應(yīng)該被保存的數(shù)據(jù)簇爆。POST請求應(yīng)該把數(shù)據(jù)作為請求的主體提交癞松,主體可以包含非常多的數(shù)據(jù),而且格式不限入蛆。
- 可以使用XHR 來模仿表單提交:首先將Content-Type頭部信息設(shè)置為application/x-www-form-urlencoded响蓉,也就是表單提交時的內(nèi)容類型,其次是以適當?shù)母袷絼?chuàng)建一個字符串哨毁。
/**以查詢字符串的格式輸出序列化之后的字符串**/
function serialize(form){
var parts = [],field = null,i,len,j,optLen,option,optValue;
for (i=0, len=form.elements.length; i < len; i++){
field = form.elements[i];
switch(field.type){
case "select-one":
case "select-multiple":
if (field.name.length){
for (j=0, optLen = field.options.length; j < optLen; j++){
option = field.options[j];
if (option.selected){
optValue = "";
if (option.hasAttribute){
optValue = (option.hasAttribute("value") ?
option.value : option.text);
} else {
optValue = (option.attributes["value"].specified ?
option.value : option.text);
}
parts.push(encodeURIComponent(field.name) + "=" +
encodeURIComponent(optValue));
}
}
}
break;
case undefined: //字段集
case "file": //文件輸入
case "submit": //提交按鈕
case "reset": //重置按鈕
case "button": //自定義按鈕
break;
case "radio": //單選按鈕
case "checkbox": //復選框
if (!field.checked){
break;
}
/* 執(zhí)行默認操作 */
default:
//不包含沒有名字的表單字段
if (field.name.length){
parts.push(encodeURIComponent(field.name) + "=" +
encodeURIComponent(field.value));
}
}
}
return parts.join("&");
}
function submitData(){
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("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 請求消耗的資源會更多一些。從性能角度來看,以發(fā)送
相同的數(shù)據(jù)計想幻,GET 請求的速度最多可達到POST 請求的兩倍粱栖。
2、XMLHttpRequest 2 級
- 并非所有瀏覽器都完整地實現(xiàn)了XMLHttpRequest2級規(guī)范脏毯,但所有瀏覽器都實現(xiàn)了它規(guī)定的部分內(nèi)容闹究。
2.1 formData
- XMLHttpRequest 2 級定義了FormData類型。FormData為序列化表單以及創(chuàng)建與表單格式相同的數(shù)據(jù)(用于通過XHR 傳輸)提供了便利抄沮。
var data = new FormData();
data.append("name", "Nicholas");
- 這個append()方法接受兩個參數(shù):鍵和值跋核。分別對應(yīng)表單字段名和字段中包含的值。
- 通過formData構(gòu)造函數(shù)傳入表單元素叛买,也可以用表單元素的數(shù)據(jù)預先向其中填入鍵值對砂代。
var data = new FormData(document.forms[0]);
- 創(chuàng)建了FormData 的實例后,可以將它直接傳給XHR 的send()方法率挣。
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("post","postexample.php", true);
var form = document.getElementById("user-info");
xhr.send(new FormData(form));
- 使用FormData 的方便之處體現(xiàn)在不必明確地在XHR 對象上設(shè)置請求頭部刻伊。XHR 對象能夠識別傳入的數(shù)據(jù)類型是FormData 的實例,并配置適當?shù)念^部信息椒功。
- 支持FormData 的瀏覽器有Firefox 4+捶箱、Safari 5+、Chrome 和Android3+版WebKit动漾。
2.2 超時設(shè)定
- IE8 為XHR 對象添加了一個timeout屬性丁屎,表示請求在等待響應(yīng)多少毫秒之后就終止。在給timeout設(shè)置一個數(shù)值后旱眯,如果在規(guī)定的時間內(nèi)瀏覽器還沒有接收到響應(yīng)晨川,那么就會觸發(fā)timeout 事件,進而會調(diào)用ontimeout 事件處理程序删豺。
var xhr = createXHR();
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){
//假設(shè)由ontimeout 事件處理程序處理
}
}
};
xhr.open("get", "timeout.php", true);
xhr.timeout = 1000; //將超時設(shè)置為1 秒鐘(僅適用于IE8+)
xhr.ontimeout = function(){
alert("Request did not return in a second.");
};
xhr.send(null);
2.3 overrideMimeType()方法
- Firefox 最早引入了overrideMimeType()方法共虑,用于重寫XHR 響應(yīng)的MIME 類型。
- 因為返回響應(yīng)的MIME類型決定了XHR對象如何處理它呀页,所以提供一種方法能夠重寫服務(wù)器返回的MIME 類型是很有用的妈拌。
var xhr = createXHR();
xhr.open("get", "text.php", true);
//強迫XHR 對象將響應(yīng)當作XML 而非純文本來處理
xhr.overrideMimeType("text/xml");
xhr.send(null);
- 支持overrideMimeType()方法的瀏覽器有Firefox、Safari 4+蓬蝶、Opera 10.5 和Chrome尘分。
3、進度事件
- 具有6個進度事件
- loadstart:在接收到響應(yīng)數(shù)據(jù)的第一個字節(jié)時觸發(fā)丸氛。
- progress:在接收響應(yīng)期間持續(xù)不斷的觸發(fā)音诫。
- error:在請求發(fā)生錯誤時觸發(fā)。
- abort:在因為調(diào)用abort()方法而終止連接時觸發(fā)雪位。
- load:在接收到完整的響應(yīng)數(shù)據(jù)時觸發(fā)竭钝。
- loadend:在通信完成或者觸發(fā)error梨撞、abort或load事件后觸發(fā)。
3.1 load 事件
- Firefox 實現(xiàn)中引入了load事件香罐,用以替代readystatechange事件卧波。響應(yīng)接收完畢后將觸發(fā)load事件,因此也就沒有必要去檢查readyState 屬性了庇茫。
var xhr = createXHR();
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);
3.2 progress 事件
- Mozilla 對XHR 的另一個革新是添加了progress事件港粱,這個事件會在瀏覽器接收新數(shù)據(jù)期間周期性地觸發(fā)。
- onprogress 事件處理程序會接收到一個event 對象旦签,其target 屬性是XHR 對象查坪,但
包含著三個額外的屬性:lengthComputable、position 和totalSize宁炫。 - lengthComputable是一個表示進度信息是否可用的布爾值偿曙,position表示已經(jīng)接收的字節(jié)數(shù),totalSize 表示根據(jù)Content-Length 響應(yīng)頭部確定的預期字節(jié)數(shù)羔巢。
var xhr = createXHR();
xhr.onload = function(){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
};
//每次觸發(fā)progress 事件望忆,都會以新的狀態(tài)信息更新HTML 元素的內(nèi)容。
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);
4竿秆、跨源資源共享
通過XHR 實現(xiàn)Ajax 通信的一個主要限制启摄,來源于跨域安全策略。默認情況下幽钢,XHR 對象只能訪問與包含它的頁面位于同一個域中的資源歉备。這種安全策略可以預防某些惡意行為。
CORS定義了在必須訪問跨源資源時匪燕,瀏覽器與服務(wù)器應(yīng)該怎么溝通蕾羊。其背后的思想,就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進行溝通谎懦,從而決定請求或響應(yīng)應(yīng)該是成功還是失敗肚豺。
比如一個簡單的使用GET或POST發(fā)送的請求溃斋,它沒有自定義的頭部界拦,而主體內(nèi)容是text/plain。在發(fā)送該請求時梗劫,需要給它附加一個額外的Origin頭部享甸,其中包含請求頁面的源信息(協(xié)議、域名和端口)梳侨,以便服務(wù)器根據(jù)這個頭部信息來決定是否給予響應(yīng)蛉威。
//Origin 頭部的一個示例:
Origin: http://www.nczonline.net
//如果服務(wù)器認為這個請求可以接受
Access-Control-Allow-Origin: http://www.nczonline.net
4.1 IE 對CORS 的實現(xiàn)
- 微軟在IE8 中引入了XDR(XDomainRequest)類型。這個對象與XHR類似走哺,但能實現(xiàn)安全可靠的跨域通信蚯嫌。
- XDR 與XHR 有一些不同之處。
- cookie 不會隨請求發(fā)送,也不會隨響應(yīng)返回择示。
- 只能設(shè)置請求頭部信息中的Content-Type字段束凑。
- 不能訪問響應(yīng)頭部信息
- 只支持GET和POST請求
- XDR對象的使用方法與XHR對象非常相似。也是創(chuàng)建一個XDomainRequest的實例栅盲,調(diào)用open()方法汪诉,再調(diào)用send()方法。但與XHR對象的open()方法不同谈秫,XDR對象的open()方法只接收兩個參數(shù):請求的類型和URL扒寄。
- 所有XDR 請求都是異步執(zhí)行的,不能用它來創(chuàng)建同步請求拟烫。請求返回之后该编,會觸發(fā)load 事件,響應(yīng)的數(shù)據(jù)也會保存在responseText 屬性中构灸。
var xdr = new XDomainRequest();
xdr.onload = function(){
alert(xdr.responseText);
};
xdr.onerror = function(){
alert("An error occurred.");
};
xdr.timeout = 1000;
xdr.ontimeout = function(){
alert("Request took too long.");
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);
- 為支持POST 請求上渴,XDR對象提供了contentType屬性,用來表示發(fā)送數(shù)據(jù)的格式,這個屬性是通過XDR 對象影響頭部信息的唯一方式喜颁。
xdr.open("post", "http://www.somewhere-else.com/page/");
xdr.contentType = "application/x-www-form-urlencoded";
xdr.send("name1=value1&name2=value2");
4.2 其它瀏覽器對CORS的實現(xiàn)
- Firefox 3.5+稠氮、Safari 4+、Chrome半开、iOS 版Safari 和Android 平臺中的WebKit 都通過XMLHttpRequest對象實現(xiàn)了對CORS 的原生支持隔披。
- 要請求位于另一個域中的資源,使用標準的XHR 對象并在open()方法中傳入絕對URL 即可寂拆。
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", "http://www.somewhere-else.com/page/", true);
xhr.send(null);
- 跨域XHR 對象也有一些限制奢米,但為了安全這些限制是必需的。
- 不能使用setRequestHeader()設(shè)置自定義頭部纠永。
- 不能發(fā)送和接收cookie鬓长。
- 調(diào)用getAllResponseHeaders()方法總會返回空字符串。
4.3 Preflighted Reqeusts
- CORS 通過一種叫做PreflightedRequests的透明服務(wù)器驗證機制支持開發(fā)人員使用自定義的頭部尝江、GET 或POST 之外的方法涉波,以及不同類型的主體內(nèi)容。
- 在使用下列高級選項來發(fā)送請求時炭序,就會向服務(wù)器發(fā)送一個Preflight請求啤覆。這種請求使用OPTIONS 方法,發(fā)送下列頭部惭聂。
- Origin:與簡單的請求相同窗声。
- Access-Control-Request-Method:請求自身使用的方法。
- Access-Control-Request-Headers:(可選)自定義的頭部信息辜纲,多個頭部以逗號分隔笨觅。
//一個帶有自定義頭部NCZ 的使用POST 方法發(fā)送的請求拦耐。
Origin: http://www.nczonline.net
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ
- 發(fā)送這個請求后,服務(wù)器可以決定是否允許這種類型的請求见剩。服務(wù)器通過在響應(yīng)中發(fā)送如下頭部與瀏覽器進行溝通揩魂。
- Access-Control-Allow-Origin:與簡單的請求相同。
- Access-Control-Allow-Methods:允許的方法炮温,多個方法以逗號分隔火脉。
- Access-Control-Allow-Headers:允許的頭部,多個頭部以逗號分隔柒啤。
- Access-Control-Max-Age:應(yīng)該將這個Preflight請求緩存多長時間(以秒表示)倦挂。
- 支持Preflight 請求的瀏覽器包括Firefox 3.5+、Safari 4+和Chrome担巩。IE 10 及更早版本都不支持方援。
4.4 帶憑據(jù)的請求
- 默認情況下,跨源請求不提供憑據(jù)(cookie涛癌、HTTP認證及客戶端SSL證明等)犯戏。通過將withCredentials屬性設(shè)置為true,可以指定某個請求應(yīng)該發(fā)送憑據(jù)拳话。如果服務(wù)器接受帶憑據(jù)的請求先匪,會用下面的HTTP 頭部來響應(yīng)。
Access-Control-Allow-Credentials: true
- 如果發(fā)送的是帶憑據(jù)的請求弃衍,但服務(wù)器的響應(yīng)中沒有包含這個頭部呀非,那么瀏覽器就不會把響應(yīng)交給JavaScript.(于是,responseText 中將是空字符串镜盯,status 的值為0岸裙,而且會調(diào)用onerror()事件處
理程序)
4.5 跨瀏覽器的CORS
- 有必要實現(xiàn)一個跨瀏覽器的方案。檢測XHR 是否支持CORS 的最簡單方式速缆,就是檢查
是否存在withCredentials屬性降允。再結(jié)合檢測XDomainRequest對象是否存在,就可以兼顧所有瀏覽器艺糜。
function createCORSRequest(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined"){
vxhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
return xhr;
}
var request = createCORSRequest("get","http://www.somewhere-else.com/page/");
if (request){
request.onload = function(){
//對request.responseText 進行處理
};
request.send();
}
- Firefox剧董、Safari 和Chrome 中的XMLHttpRequest 對象與IE 中的XDomainRequest 對象類似,都提供了夠用的接口倦踢,這兩個對象共同的屬性/方法如下送滞。
- abort():用于停止正在進行的請求侠草。
- onerror:用于替代onreadystatechange 檢測錯誤辱挥。
- onload:用于替代onreadystatechange 檢測成功。
- responseText:用于取得響應(yīng)內(nèi)容边涕。
- send():用于發(fā)送請求晤碘。
5褂微、其它跨域技術(shù)
- 利用DOM 中能夠執(zhí)行跨域請求的功能,在不依賴XHR對象的情況下也能發(fā)送某種請求园爷。
5.1 圖像Ping
- 第一種跨域請求技術(shù)是使用
<img>
標簽宠蚂。 - 動態(tài)創(chuàng)建圖像經(jīng)常用于圖像Ping。圖像Ping是與服務(wù)器進行簡單童社、單向的跨域通信的一種方式求厕。請求的數(shù)據(jù)是通過查詢字符串形式發(fā)送的,而響應(yīng)可以是任意內(nèi)容扰楼,但通常是像素圖或204 響應(yīng)呀癣。
- 通過圖像Ping,瀏覽器得不到任何具體的數(shù)據(jù)弦赖,但通過偵聽load和error事件项栏,它能知道響應(yīng)是什么時候接收到的。
var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";
- 圖像Ping 最常用于跟蹤用戶點擊頁面或動態(tài)廣告曝光次數(shù)
- 圖像Ping 有兩個主要的缺點蹬竖,一是只能發(fā)送GET請求沼沈,二是無法訪問服務(wù)器的響應(yīng)文本。因此币厕,圖像Ping 只能用于瀏覽器與服務(wù)器間的單向通信列另。
5.2 JSONP
- JSONP 是JSON with padding(填充式JSON 或參數(shù)式JSON)的簡寫,是應(yīng)用JSON 的一種新方法旦装。
- JSONP 看起來與JSON 差不多访递,只不過是被包含在函數(shù)調(diào)用中的JSON。
callback({ "name": "Nicholas" });
- JSONP由兩部分組成:回調(diào)函數(shù)和數(shù)據(jù)同辣】阶耍回調(diào)函數(shù)是當響應(yīng)到來時應(yīng)該在頁面中調(diào)用的函數(shù)『岛回調(diào)函數(shù)的名字一般是在請求中指定的响巢。而數(shù)據(jù)就是傳入回調(diào)函數(shù)中的JSON數(shù)據(jù)。
http://freegeoip.net/json/?callback=handleResponse
- JSONP 是通過動態(tài)<script>元素來使用的棒妨,使用時可以為src 屬性指定一個跨域URL.
//通過查詢地理定位服務(wù)來顯示你的IP 地址和位置信息踪古。
function handleResponse(response){
alert("You’re at IP address " + response.ip + ", which is in " +
response.city + ", " + response.region_name);
}
var script = document.createElement("script");
//典型的JSONP請求,請求一個JSONP 地理定位服務(wù)
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);
-
JSONP優(yōu)點:
- 簡單易用
- 能夠直接訪問響應(yīng)文本券腔,支持在瀏覽器與服務(wù)器之間雙向通信
-
JSONP不足:
- JSONP 是從其他域中加載代碼執(zhí)行伏穆。如果其他域不安全,很可能會在響應(yīng)中夾帶一些惡意代碼纷纫,而此時除了完全放棄JSONP 調(diào)用之外枕扫,沒有辦法追究指郁。
- 要確定JSONP 請求是否失敗并不容易畦戒,開發(fā)人員不得不使用計時器檢測指定時間內(nèi)是否接收到了響應(yīng)。
5.3 Comet
- Ajax 是一種從頁面向服務(wù)器請求數(shù)據(jù)的技術(shù),而Comet則是一種服務(wù)器向頁面推送數(shù)據(jù)的技術(shù)慷彤。
- 有兩種實現(xiàn)Comet 的方式:長輪詢和流蜒简。
(1)長輪詢
長輪詢是傳統(tǒng)輪詢(也稱為短輪詢)的一個翻版拉宗,即瀏覽器定時向服務(wù)器發(fā)送請求咖祭,看有沒有更新的數(shù)據(jù)。
-
無論是短輪詢還是長輪詢砾赔,瀏覽器都要在接收數(shù)據(jù)之前蝌箍,先發(fā)起對服務(wù)器的連接。
-
長輪詢把短輪詢顛倒了一下暴心。頁面發(fā)起一個到服務(wù)器的請求十绑,然后服務(wù)器一直保持連接打開,直到有數(shù)據(jù)可發(fā)送酷勺。發(fā)送完數(shù)據(jù)之后本橙,瀏覽器關(guān)閉連接,隨即又發(fā)起一個到服務(wù)器的新請求脆诉。這一過程在頁面打開期間一直持續(xù)不斷甚亭。
兩者最大的區(qū)別在于服務(wù)器如何發(fā)送數(shù)據(jù)。短輪詢是服務(wù)器立即發(fā)送響應(yīng)击胜,無論數(shù)據(jù)是否有效亏狰,而長輪詢是等待發(fā)送響應(yīng)。輪詢的優(yōu)勢是所有瀏覽器都支持偶摔,因為使用XHR 對象和setTimeout()就能實現(xiàn)暇唾。而你要做的就是決定什么時候發(fā)送請求。
(2)HTTP 流
第二種流行的Comet 實現(xiàn)是HTTP流辰斋。流不同于上述兩種輪詢策州,因為它在頁面的整個生命周期內(nèi)只使用一個HTTP連接。具體來說宫仗,就是瀏覽器向服務(wù)器發(fā)送一個請求够挂,而服務(wù)器保持連接打開,然后周期性地向瀏覽器發(fā)送數(shù)據(jù)
在Firefox藕夫、Safari孽糖、Opera 和Chrome 中,通過偵聽readystatechange 事件及檢測readyState的值是否為3毅贮,就可以利用XHR 對象實現(xiàn)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){
//只取得最新數(shù)據(jù)并調(diào)整計數(shù)器
result = xhr.responseText.substring(received);
received += result.length;
//調(diào)用progress 回調(diào)函數(shù)
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!");
});
5.4 服務(wù)器發(fā)送事件
SSE(Server-Sent Events,服務(wù)器發(fā)送事件)是圍繞只讀Comet 交互推出的API 或者模式滩褥。SSE API用于創(chuàng)建到服務(wù)器的單向連接病蛉,服務(wù)器通過這個連接可以發(fā)送任意數(shù)量的數(shù)據(jù)。
服務(wù)器響應(yīng)的MIME類型必須是text/event-stream,而且是瀏覽器中的JavaScript API 能解析格式輸出铡恕。SSE支持短輪詢、長輪詢和HTTP流丢间,而且能在斷開連接時自動確定何時重新連接探熔。
支持SSE 的瀏覽器有Firefox 6+、Safari 5+烘挫、Opera11+诀艰、Chrome和iOS4+版Safari。
(1)SSE API
- SSE 的JavaScript API與其他傳遞消息的JavaScriptAPI很相似饮六。要預訂新的事件流其垄,首先要創(chuàng)建一個新的EventSource 對象,并傳進一個入口點:
var source = new EventSource("myevents.php");
- 傳入的URL 必須與創(chuàng)建對象的頁面同源(相同的URL模式卤橄、域及端口)绿满。
- EventSource 的實例有一個readyState屬性,值為0表示正連接到服務(wù)器窟扑,值為1表示打開了連接喇颁,值為2 表示關(guān)閉了連接。
- 另外嚎货,還有以下三個事件橘霎。
- open:在建立連接時觸發(fā)。
- message:在從服務(wù)器接收到新事件時觸發(fā)殖属。
- error:在無法建立連接時觸發(fā)姐叁。
source.onmessage = function(event){
//服務(wù)器發(fā)回的數(shù)據(jù)以字符串形式保存在event.data 中。
var data = event.data;
//處理數(shù)據(jù)
/**如果想強制立即斷開連接并且不再重新連接洗显,可以調(diào)用close()
方法外潜。**/
source.close();
};
(2)事件流
- 所謂的服務(wù)器事件會通過一個持久的HTTP響應(yīng)發(fā)送,這個響應(yīng)的MIME類型為text/event-stream挠唆。響應(yīng)的格式是純文本橡卤,最簡單的情況是每個數(shù)據(jù)項都帶有前綴data:
data: foo
data: bar
data: foo
data: bar
- 通過id:前綴可以給特定的事件指定一個關(guān)聯(lián)的ID,這個ID行位于data:行前面或后面皆可损搬。
data: foo
id: 1
- 設(shè)置了ID 后碧库,EventSource對象會跟蹤上一次觸發(fā)的事件。如果連接斷開巧勤,會向服務(wù)器發(fā)送一個包含名為Last-Event-ID的特殊HTTP頭部的請求嵌灰,以便服務(wù)器知道下一次該觸發(fā)哪個事件。在多次連接的事件流中颅悉,這種機制可以確保瀏覽器以正確的順序收到連接的數(shù)據(jù)段沽瞭。
5.5 Web Sockets
由于Web Sockets 使用了自定義的協(xié)議,所以URL模式也略有不同剩瓶。驹溃。未加密的連接不再是http://城丧,而是ws://;加密的連接也不是https://豌鹤,而是wss://亡哄。
使用自定義協(xié)議而非HTTP協(xié)議的好處能夠在客戶端和服務(wù)器之間發(fā)送非常少量的數(shù)據(jù),而不必擔心HTTP 那樣字節(jié)級的開銷布疙。由于傳遞的數(shù)據(jù)包很小蚊惯,因此Web Sockets 非常適合移動應(yīng)用。
使用自定義協(xié)議的缺點在于灵临,制定協(xié)議的時間比制定JavaScript API 的時間還要長.
(1) Web Sokets API
- 要創(chuàng)建Web Socket截型,先實例一個WebSocket 對象并傳入要連接的URL:
var socket = new WebSocket("ws://www.example.com/server.php");
- 與XHR 類似,WebSocket 也有一個表示當前狀態(tài)的readyState 屬性儒溉。
- WebSocket.OPENING (0):正在建立連接宦焦。
- WebSocket.OPEN (1):已經(jīng)建立連接。
- WebSocket.CLOSING (2):正在關(guān)閉連接顿涣。
- WebSocket.CLOSE (3):已經(jīng)關(guān)閉連接赶诊。
- 要關(guān)閉Web Socket 連接,可以在任何時候調(diào)用close()方法园骆。
socket.close();
/**調(diào)用了close()之后舔痪,readyState的值立即變?yōu)?(正在關(guān)閉),而在關(guān)閉連接后就會變成3锌唾。**/
(2)發(fā)送和接收數(shù)據(jù)
- Web Socket 打開之后锄码,就可以通過連接發(fā)送和接收數(shù)據(jù)。要向服務(wù)器發(fā)送數(shù)據(jù)晌涕,使用send()方法并傳入任意字符串滋捶。
var socket = new WebSocket("ws://www.example.com/server.php");
socket.send("Hello world!");
- 因為Web Sockets 只能通過連接發(fā)送純文本數(shù)據(jù),所以對于復雜的數(shù)據(jù)結(jié)構(gòu)余黎,在通過連接發(fā)送之前重窟,必須進行序列化。
var message = {
time: new Date(),
text: "Hello world!",
clientId: "asdfp8734rew"
};
socket.send(JSON.stringify(message));
- 當服務(wù)器向客戶端發(fā)來消息時惧财,WebSocket 對象就會觸發(fā)message事件巡扇。這個message 事件與其他傳遞消息的協(xié)議類似,也是把返回的數(shù)據(jù)保存在event.data 屬性中垮衷。
socket.onmessage = function(event){
var data = event.data;
//處理數(shù)據(jù)
};
(3)其它事件
- WebSocket 對象還有其他三個事件厅翔,在連接生命周期的不同階段觸發(fā)。
- open:在成功建立連接時觸發(fā)搀突。
- error:在發(fā)生錯誤時觸發(fā)刀闷,連接不能持續(xù)。
- close:在連接關(guān)閉時觸發(fā)。
- WebSocket 對象不支持DOM2級事件偵聽器甸昏,因此必須使用DOM0級語法分別定義每個事件處理程序顽分。
var socket = new WebSocket("ws://www.example.com/server.php");
socket.onopen = function(){
alert("Connection established.");
};
socket.onerror = function(){
alert("Connection error.");
};
socket.onclose = function(){
alert("Connection closed.");
};
6、安全
對于未被授權(quán)系統(tǒng)有權(quán)訪問某個資源的情況施蜜,我們稱之為CSRF(Cross-Site Request Forgery卒蘸,跨站點請求偽造)。未被授權(quán)系統(tǒng)會偽裝自己花墩,讓處理請求的服務(wù)器認為它是合法的悬秉。
受到CSRF 攻擊的Ajax程序有大有小澄步,攻擊行為既有旨在揭示系統(tǒng)漏洞的惡作劇冰蘑,也有惡意的數(shù)據(jù)竊取或數(shù)據(jù)銷毀。
-
為確保通過XHR 訪問的URL安全村缸,通行的做法就是驗證發(fā)送請求者是否有權(quán)限訪問相應(yīng)的資源祠肥。
- 要求以SSL 連接來訪問可以通過XHR 請求的資源。
- 要求每一次請求都要附帶經(jīng)過相應(yīng)算法計算得到的驗證碼梯皿。
-
下列措施對防范CSRF 攻擊不起作用仇箱。
- 要求發(fā)送POST 而不是GET 請求——很容易改變。
- 檢查來源URL 以確定是否可信——來源記錄很容易偽造东羹。
- 基于cookie 信息進行驗證——同樣很容易偽造剂桥。
小結(jié)
- Ajax 是無需刷新頁面就能夠從服務(wù)器取得數(shù)據(jù)的一種方法。關(guān)于Ajax属提,可以從以下幾方面來總結(jié)一下权逗。
(1)負責Ajax 運作的核心對象是XMLHttpRequest(XHR)對象。
(2)XHR 對象由微軟最早在IE5 中引入冤议,用于通過JavaScript 從服務(wù)器取得XML 數(shù)據(jù)斟薇。
(3)在此之后,F(xiàn)irefox恕酸、Safari堪滨、Chrome 和Opera 都實現(xiàn)了相同的特性,使XHR 成為了Web 的一個事實標準蕊温。
(4)雖然實現(xiàn)之間存在差異袱箱,但XHR對象的基本用法在不同瀏覽器間還是相對規(guī)范的,因此可以放心地用在Web 開發(fā)當中义矛。
- 同源策略是對XHR 的一個主要約束犯眠,它為通信設(shè)置了“相同的域、相同的端口症革、相同的協(xié)議”這一限制筐咧。試圖訪問上述限制之外的資源,都會引發(fā)安全錯誤,除非采用被認可的跨域解決方案量蕊。
- 這個解決方案叫做CORS(Cross-Origin Resource Sharing铺罢,跨源資源共享),IE8 通過XDomainRequest 對象支持CORS残炮,其他瀏覽器通過XHR對象原生支持CORS韭赘。圖像Ping 和JSONP 是另外兩種跨域通信的技術(shù),但不如CORS 穩(wěn)妥势就。
Comet 是對Ajax 的進一步擴展泉瞻,讓服務(wù)器幾乎能夠?qū)崟r地向客戶端推送數(shù)據(jù)。實現(xiàn)Comet 的手段主要有兩個:長輪詢和HTTP流苞冯。所有瀏覽器都支持長輪詢袖牙,而只有部分瀏覽器原生支持HTTP 流。SSE(Server-SentEvents舅锄,服務(wù)器發(fā)送事件)是一種實現(xiàn)Comet 交互的瀏覽器API鞭达,既支持長輪詢,也支持HTTP 流皇忿。
Web Sockets 是一種與服務(wù)器進行全雙工畴蹭、雙向通信的信道。與其他方案不同鳍烁,Web Sockets 不使用HTTP 協(xié)議叨襟,而使用一種自定義的協(xié)議。這種協(xié)議專門為快速傳輸小數(shù)據(jù)設(shè)計幔荒。雖然要求使用不同的Web 服務(wù)器糊闽,但卻具有速度上的優(yōu)勢。