JavaScript Ajax與Comet——“其他跨域技術”的注意要點

Oliveryoung發表於2016-02-26

影像Ping技術

根據一個網頁可以從任何網頁中載入影像而不用擔心使用跨域的原理, 我們可以動態的建立影像, 使用他們的onload和onerror事件處理程式來確定是否收到響應。

動態的建立影像經常用於影像Ping。 影像Ping是與伺服器進行簡單的單向的跨域通訊的一種方式。

請求的資料通過查詢字串形式傳送的,而響應可以是任意內容, 但通常是畫素圖或者204響應

通過影像Ping,瀏覽器得不到任何具體的資料, 但是通過偵聽loaderror事件, 它能知道是什麼時候接受的。

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服務中非常流行。

callback({ "name": "Oli" });

JSONP由兩部分組成:回撥函式和資料。

回撥函式是當響應到來時應該在頁面中呼叫的函式,回撥函式的名字一般是在請求中指定的。資料就是傳入回撥函式的json資料。

下面是以個JSONP的例子:

http: //freegeoip.net/json/?callback=handleResponse //回撥函式的名字就是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方式是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請求的資源
要求每一次請求都要附帶經過相應演算法計算得到的驗證碼

相關文章