【javascript】Ajax 與 Comet

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)勢。

好好學習
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铺峭,一起剝皮案震驚了整個濱河市墓怀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卫键,老刑警劉巖傀履,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異莉炉,居然都是意外死亡钓账,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門絮宁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梆暮,“玉大人,你說我怎么就攤上這事绍昂±泊猓” “怎么了偿荷?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唠椭。 經(jīng)常有香客問我跳纳,道長,這世上最難降的妖魔是什么贪嫂? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任寺庄,我火速辦了婚禮,結(jié)果婚禮上力崇,老公的妹妹穿的比我還像新娘斗塘。我一直安慰自己,他們只是感情好亮靴,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布馍盟。 她就那樣靜靜地躺著,像睡著了一般台猴。 火紅的嫁衣襯著肌膚如雪朽合。 梳的紋絲不亂的頭發(fā)上俱两,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天饱狂,我揣著相機與錄音,去河邊找鬼宪彩。 笑死休讳,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的尿孔。 我是一名探鬼主播俊柔,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼活合!你這毒婦竟也來了雏婶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤白指,失蹤者是張志新(化名)和其女友劉穎留晚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體告嘲,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡错维,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了橄唬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赋焕。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖仰楚,靈堂內(nèi)的尸體忽然破棺而出隆判,到底是詐尸還是另有隱情犬庇,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布侨嘀,位于F島的核電站械筛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏飒炎。R本人自食惡果不足惜埋哟,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望郎汪。 院中可真熱鬧赤赊,春花似錦、人聲如沸煞赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽照筑。三九已至吹截,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凝危,已是汗流浹背波俄。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛾默,地道東北人懦铺。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像支鸡,于是被迫代替她去往敵國和親冬念。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 國家電網(wǎng)公司企業(yè)標準(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 10,869評論 6 13
  • Ajax:Asynchronous JavaScript + XML的簡寫牧挣。Ajax技術(shù)的核心是XMLHttpRe...
    exialym閱讀 869評論 0 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理急前,服務(wù)發(fā)現(xiàn),斷路器瀑构,智...
    卡卡羅2017閱讀 134,600評論 18 139
  • ajax作為前端開發(fā)必需的基礎(chǔ)能力之一裆针,你可能會使用它,但并不一定懂得其原理检碗,以及更深入的服務(wù)器通信相關(guān)的知識据块。在...
    蕭玄辭閱讀 812評論 0 0
  • 90分滿分,我考了80分在大家?guī)椭?/div>
    趙歡喜麻麻閱讀 99評論 0 0