XHR對象只能訪問與包含它的頁面位於同一個中的資源。這種安全策略能夠預防某些惡意行爲。
CORS(Cross-Origin Resource Sharing,跨域資源共享)是W3C的一個工做草案,定義了在必須訪問跨域資源時,瀏覽器與服務器應該如何溝通。CORS基本思想:使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功仍是失敗。
如:一個使用GET或POST發送的請求,沒有自定義的頭部,主體內容是text/plain。發送請求時,須要給它附加一個額外的Origin頭部,其中包含請求頁面的源信息(協議、域名和端口),以便服務器根據這個頭部信息來決定是否給與響應。
Origin頭部如:
Origin: http://www.nczonline.net
若服務器認爲這個請求能夠接受,就在Access-Control-Allow-Origin頭部中回發相同的源信息(如果公共資源,能夠回發「*」)。如:
Access-Control-Allow-Origin: http://www.nczonline.net
若沒有這個頭部,或有這個頭部但源信息不匹配,瀏覽器就會駁回請求。正常狀況下,瀏覽器會處理請求。
注:請求和響應都不包含cookie信息。
一、IE對CORS的實現
微軟在IE8中引入了XDR(XDomainRequest)類型,可以實現安全可靠的跨域通訊。XDR對象的安全機制部分實現了W3C的CORS規範。
XDR與XHR的一些不一樣之處:
* cookie不會隨請求發送,也不會隨響應返回。
* 只能設置請求頭部信息中的Content-Type字段。
* 不能訪問響應頭部信息。
* 只支持GET和POST請求。
被請求的資源能夠根據它認爲合適的任意數據(用戶代理、來源頁面等)來決定是否設置Access-Control-Allow-Origin頭部。做爲請求的一部分,Origin頭部的值表示請求的來源域,以便遠程資源明確的識別XDR請求。
XDR對象的使用方法與XHR對象很是類似。也是建立一個XDomainRequest的實例,調用open()方法,再調用send()方法。但與XHR對象的open()方法不一樣,XDR對象的open()只接收2個參數:請求的類型和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);
在接收到響應後,只能訪問響應的原始文件,沒有辦法肯定響應的狀態代碼。並且只要響應有效就會觸發load事件,若是失敗就會觸發error事件。遺憾的是,除了錯誤自己以外,沒有其餘信息可用,所以惟一可以肯定的就只有請求未成功了。要檢測錯誤,能夠指定一個onerror事件處理程序:
var xdr = new XDomainRequest();
xdr.onload = function(){
alert(xdr.responseText);
};
xdr.onerror = function(){
alert("An error occurred.");
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);
注:致使XDR請求失敗的因素不少,因此建議經過onerror事件處理程序來捕獲該事件;不然,即便請求失敗也不會有任何提示。
在請求返回前調用abort()方法能夠終止請求:
xdr.abort(); //終止請求
XDR對象也支持timeout屬性以及ontimeout事件處理程序。如:
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);
運行1s後超時,並隨機調用ontimeout事件處理程序。
爲支持POST請求,XDR對象提供了contentType屬性,用來表示發送數據的格式,如:
1 var xdr = new XDomainRequest();
2 xdr.onload = function(){
3 alert(xdr.responseText);
4 };
5 xdr.onerror = function(){
6 alert("An error occurred.");
7 };
8 xdr.open("post", "http://www.somewhere-else.com/page/");
9 xdr.contentType = "application/x-www-form-urlencoded";
10 xdr.send("name1=value1&name2=value2");
這個屬性是經過XDR對象影響頭部信息的惟一方式。
二、其餘瀏覽器對CORS的實現
1 var xhr = createXHR();
2 xhr.onreadystatechange = function(){
3 if(xhr.readyState ==4){
4 if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
5 alert(xhr.responseText);
6 }else{
7 alert("Request was unsuccessful: " + xhr.status);
8 }
9 }
10 };
11 //跨域請求的url爲絕對的連接
12 xhr.open("get", "http://www.somewhere-else.com/page/", true);
13 xhr.send(null);
限制:
* 不能使用setRequestHeader()設置自定義頭部。
* 不能發送和接收cookie。
* 調用getAllResponseHeaders()方法總會返回空字符串。
對於本地資源,最好使用相對URL,訪問遠程資源時使用絕對URL,能夠消除歧義,避免出現限制訪問頭部或本地cookie信息等問題。
三、跨瀏覽器的CORS
全部瀏覽器都支持簡單的(非Preflight和不帶憑據的)請求,所以有必要實現一個跨瀏覽器的方案。
1 function createCORSRequest(method, url){
2 var xhr = new XMLHttpRequest();
3 if("withCredentials" in xhr){
4 xhr.open(method, url, true);
5 }else if(typeof XDomainRequest != "undefined"){
6 xhr = new XDomainRequest();
7 xhr.open(method, url);
8 } else {
9 xhr = null;
10 }
11 return xhr;
12 }
13 var request = cresteCORSRequest("get", "http://www.somewhere-else.com/page/");
14 if(request){
15 request.onload = function(){
16 //對request.responseText進行處理
17 };
18 request.send();
19 }
Firefox、Safari、Chrome中的XMLHttpRequest對象與IE中的XDomainRequest對象相似,都提供了夠用的接口,所以以上模式仍是至關有用的。
這兩個對象共同的屬性/方法以下: * abort():用於中止正在進行的請求。 * onerror:用於替代onreadystatechange檢測錯誤。 * onload:用於替代onreadystatechange檢測成功。 * responseText:用於取得相應內容。 * send():用於發送請求。以上成員都包含在createCORSRequest()函數返回的對象中,在全部瀏覽器中都能正常使用。