JavaScript中的AJAX

本文轉自: http://web.jobbole.com/86097/php

XMLHttpRequest對象

IE7+,FireFox,Chrome,Opera,Safari建立XHR對象:html

var xhr=new XMLHttpRequest();

建立XHR對象的兼容性寫法:web

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=new createXHR();

XHR用法

發送同步請求json

使用 XHR 時,首先要調用 open() 方法,傳遞三個參數:跨域

  1. 要發送的請求類型( get , post 等)
  2. 請求的 url
  3. 是否異步發送

要發送特定的請求,必需像下面這樣調用 send() 方法瀏覽器

xhr.open("get","example.php",false);
xhr.send(null);

這裏 send() 方法接收一個參數,做爲請求主體發送的數據。若是不須要經過請求主體發送數據,這裏必須傳入 null ,由於這個參數對有些瀏覽器來講是必需的。調用 send() 以後,請求就會被分派到服務器。緩存

因爲此次請求是同步的,JavaScript 代碼會等到服務器響應以後再繼續執行。在收到響應以後,相應的數據會自動填充XHR對象的屬性,相關的屬性簡介以下:安全

  • responseText: 做爲響應主體被返回的文本。
  • responseXML: 若是響應的內容類型是 「text/xml」或」application/xml」,這個屬性中將保存包含着相應數據的XML DOM文檔。
  • status: 響應的HTTP狀態。
  • statusText: HTTP狀態的說明。

接受響應以後,第一步是檢查 status 屬性,以肯定響應已經成功返回。狀態碼:服務器

  • 200 表示成功
  • 304 表示請求的資源並無修改,能夠直接使用瀏覽器中緩存的版本,響應也是有效的

像下面這樣檢查上述這兩種狀態碼的狀態:cookie

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);
}

注意:不管內容類型是什麼,響應主體的內容都會保存到 responseText 屬性中;而對於非 XML 數據而言, responseXML 屬性的值將爲 null。

發送異步請求

向前面這樣發送同步請求固然沒問題,但多數狀況下,咱們仍是要發送異步請求,才能讓 JavaScript 繼續執行而沒必要等待響應。此時,能夠檢測 XHR 對象的 readyState 屬性,該屬性表示請求/響應過程當中的當前活動階段。這個屬性可取的值以下:

  • 0:未初始化。還沒有調用 open() 方法。
  • 1:啓動。已經調用 open() 方法,但還沒有調用 send() 方法。
  • 2:發送。已經調用 send() 方法,但還沒有接收響應。
  • 3:接收。已經接收到部分響應數據。
  • 4:完成。已經接收到所有響應數據,並且已經能夠在客戶端使用了。

只要 readyState 屬性的值由一個值變爲另外一個值,都會觸發一次 readystatechange 事件。能夠利用這個事件來檢測每次狀態變化後的 readyState 的值,一般,咱們只對 readyState 值爲 4 的階段感興趣,由於這時全部的數據都已經就緒。不過,必須在調用 open() 以前指定 onreadyState 事件處理程序才能確保跨瀏覽器兼容性。例子以下:

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);

另外,在接收到響應以前還能夠調用 abort() 方法來取消異步請求,以下所示:

xhr.absort();

調用這個方法後,XHR 對象會中止觸發事件,並且也再也不容許訪問任何與響應有關的對象屬性。

HTTP頭部信息

每一個 HTTP 請求和響應都會帶有響應的頭部信息,有的對開發人員有用,有的也沒有什麼用,XHR 對象也提供了操做這兩種頭部(即請求頭部和響應頭部)信息的方法。 默認狀況下,在發送 XHR 請求的同事,還會發送下列頭部信息。

  • Accept: 瀏覽器可以處理的內容類型。
  • Accept-Charset: 瀏覽器可以顯示的字符集。
  • Accept-Encoding: 瀏覽器可以處理的壓縮編碼。
  • Accept-Language: 瀏覽器當前設置的語言。
  • Connection: 瀏覽器與服務器之間鏈接的類型。
  • Cookie: 當前頁面設置的語言。
  • Host: 發出請求的頁面所在的域。
  • Referer: 發出請求的頁面的URI。
  • User-Agent: 瀏覽器的用戶代理字符串。

使用 setRequestHeader() 方法能夠設置自定義的請求頭部信息。這個方法接受兩個參數:頭部字段的名稱和頭部字段的值。要成功發送請求頭部信息,必須在調用 open() 方法以後且調用 send() 方法以前調用 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);

服務器在接收到這種自定義的頭部信息以後,可移植性響應的後續操做。建議使用自定義的頭部字段名稱,不要使用瀏覽器正常發送的字段名稱。

調用 XHR 對象的 getResponseHeader() 方法並傳入頭部字段名稱,能夠取得相應的響應頭部信息。而調用 getAllResponseHeaders() 方法能夠取得一個包含全部頭部信息的長字符串。看下面的例子:

var myHeader=xhr.getResponseHeader("MyHeader");
var allHeader=xhr.getAllResponseHeaders();

GET請求

GET 是最多見的請求類型,最經常使用於向服務器查詢某些信息。必要時,能夠講查詢字符串參數追加到 URL 的末尾,以便將信息發送給服務器。對 XHR 而言,位於傳入 open() 方法的 URL 末尾的查詢字符串必須通過正確的編碼才行。

使用 GET 請求常常會發生的一個錯誤,及時查詢字符串的格式有問題。查詢字符串中每一個參數的名稱和值必須使用 encodeURIComponent() 進行編碼,而後才能放到 URL 的末尾;並且全部名-值對都必須由和號(&)分隔,例子以下:

xhr.open("get","example.php?name1=value1&name2=value2",true);

下面這個函數能夠輔助向現有 URL 的末尾添加查詢字符串參數:

function addURLParam(url,name,value){
  url += (url.indexOf("?") == -1? "?":"&");
  url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
}

使用方法:

var url="example.php";
 
//添加參數
url = addURLParam(url,"name","Nocholas");
url = addRULParam(rul,"book","Professional JavaScript");
 
//初始化請求
xhr.open("get",url,false);

POST請求

POST 請求一般用於向服務器發送應該被保存的數據。POST 請求應該吧數據做爲請求的主體提交,而 GET 請求傳統上不是這樣。

默認狀況下,服務器對 POST 請求和提交 Web 表單的請求並不會一視同仁。所以,服務器端必須有程序來讀取發送過來的原始數據,並從中解析出有用的部分。不過,咱們可使用 XHR 來模仿表單提交:首先將 Content-Type 頭部信息設置爲 application/x-www-from-urlencoded,也就是表單提交時的內容類型,其次是以適當的格式建立一個字符串。以下所示:

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","example.php",true);
  xhr.setRequestHeader("Content-Type","application/x-www-from-urlencoded");
  var form=document.getElementById("user-info");
  xhr.send(serialize(form));
}

這個函數能夠將 ID 爲 「user-info」 的表單中的數據序列化以後發送給服務器。

XMLHttpRequest2級

XMLHttpRequest 1級只是把已有的 XHR 對象的實現細節描述了出來。而 XMLHttpRequest2級則進一步發展了 XHR。並不是全部瀏覽器都完整地實現了 XMLHttpRequest2級規範。

FormData

現代 Web 應用中頻繁使用的一項功能就是表單數據的序列化,XMLHttpRequest2級爲此定義了 FormData 類型。FormData 爲序列化表單以及建立與表單格式相同的數據提供了便利。下面代碼建立了 FormData 對象,並向其中添加了一些數據。

var data = new FormData();
data.append("name","Nicholas");

這個 append() 方法接受兩個參數:鍵和值,分別對應表單字段的名字和字段中包含的值。能夠像這樣添加任意多的鍵值對。而經過向 FormData 構造函數中傳入表單元素,也能夠用表單元素的數據預先向其中填入鍵值對:

var data=new FormData(document.forms[0]);

建立了 FormData 的實例後,能夠將它直接傳給 XHR 的 send() 方法,以下所示:

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","example.php",true);
  var form=document.getElementById("user-info");
  xhr.send(new FormData(form));
}

使用 FormData 的方便之處在於沒必要明確地在 XHR 對象上設置請求頭部。XHR 對象可以識別傳入的數據類型是 FormData 的實例,並配置適當的頭部信息。

支持 FormData 的瀏覽器有 Firefox4+,Safari5+,Chrome和Android3+版WebKit。

跨域資源共享

經過 XHR 實現 Ajax 通訊的一個主要限制,來源於跨域安全策略。

CORS(Cross-Origin Resource Sharing,跨域資源共享)背後的基本思想,及時使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,仍是應該失敗。

好比一個簡單地使用 GET 或 POST 發送的請求,它沒有自定義的頭部,而主題內容是 text/plain。在發送該請求時,須要給它附加一個額外的 Origin 頭部,其中包括請求頁面的源信息(協議、域名、端口),以便服務器根據這個頭部信息來決定是否給予響應,下面是 Oringin 頭部的一個示例:

Origin: http://www.nczonline.net

若是服務器認爲這個請求能夠接受,就在 Access-Control-Allow-Origin 頭部中回發相同的源信息(若是是公共資源,能夠回發 「*」)。例如:

Access-Control-Allow-Origin: http://www.nczonline.net

若是沒有這個頭部,或者有這個頭部但源信息不匹配,瀏覽器就會駁回請求。正常狀況下,瀏覽器會處理請求。注意,請求和響應都不包含 cookie 信息。

IE 對 CORS 的實現

微軟在 IE8 中引入了 XDR (XDomainRequest) 類型。這個對象與 XHR 相似,但能實現安全可靠的跨域通訊。XDR 對象的安全機制部分實現了 W3C 的 CORS 規範。如下是 XDR 與 XHR 的一些不一樣之處。

  • cookie 不會隨請求發送,也不會隨響應返回。
  • 只能設置請求頭部信息的 Content-Type 字段。
  • 不能訪問響應頭部信息。
  • 只支持GET和POST請求。

全部的 XDR 請求都是異步執行的,不能用它來建立同步請求。請求返回以後,會觸發 load 事件,響應的數據也會保存在 responseText 屬性中。

在接收到響應後,你只能訪問響應的原始文本;沒有辦法肯定響應的狀態代碼。並且,只要響應有效就會觸發 load 事件,若是失敗(包括響應中缺乏 Access-Control-Allow-Origin頭部),就會觸發 error 事件。遺憾的是,除了錯誤自己以外,沒有其餘信息可用,所以惟一可以肯定的就只有請求未成功了。要檢測錯誤,能夠像下面這樣指定一個 onerror 事件處理程序。

var xdr=new XDomainRequest();
xdr.onload=function(){
  alert(xdr.responseText);
};
xdr.onerror=function(){
  alert("An erro occurred.");
};
xdr.open("get","http://www.somewhere-else.com/page");
xdr.send(null);

爲支持 POST 請求,XDR 對象提供了 contentType 屬性,用來表示發送數據的格式,以下所示:

var xdr=new XDomainRequest();
xdr.onload=function(){
  alert(xdr.responseText);
};
xdr.onerror=function(){
  alert("An erro occurred.");
};
xdr.open("post","http://www.somewhere-else.com/page");
xdr.contentType="application/x-www-form-urlencoded";
xdr.send("name1=value1&name2=value2");

這個屬性經過 XDR 對象影響頭部信息的惟一方式。

其餘瀏覽器對 CORS 的實現

Forefox3.5+,Sarari4+,Chrome,iOS 版 Sarari 和 Android 平臺中的 WebKit 都經過 XMLHttpRequest 對象實現了對 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);

與 IE 中的 XDR 對象不一樣,經過跨域 XHR 對象能夠訪問 status 和 statusText 屬性,並且還支持同步請求,跨域 XHR 對象也有一些限制,但爲了安全,這些限制是必需的,一下就是這些限制。

  • 不能使用 setRequestHeader() 設置自定義頭部。
  • 不能發送和接收 cookie。
  • 調用 getAllResponseHeaders() 方法總會返回空字符串。

對於本地資源,最好使用相對 RUL,在訪問遠程資源時再使用絕對 URL。

Preflighted Requests

CORS 經過一種叫作 Preflighted Requests 的透明服務器驗證機制支持開發人員使用自定義的頭部,GET 或 POST 以外的方法,以及不一樣類型的主體內容,在使用下列高級選項來發送請求時,就會像服務器發送一個 Preflight 請求。這種請求使用 OPTIONS 方法,發送下列頭部。

  • Origin: 與簡單的請求相同。
  • Access-Control-Request-Method: 請求自身使用的方法。
  • Access-Control-Request-Headers: (可選) 自定義的頭部信息,多個頭部以逗號分隔。

如下是一個帶有自定義頭部的 NCZ 的使用 POST 方法發送的請求。

Origin: http://www.nczonline.net
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ

發送這個請求後,服務器能夠決定是否容許這種類型的請求。服務器經過在響應中發送以下頭部與瀏覽器進行溝通。

Access-Control-Allow-Origin: 與簡單的請求相同。
Access-Control-Allow-Method: 容許的方法,多個方法以逗號分隔。
Access-Control-Allow-Headers: 容許的頭部,多個頭部以逗號分隔。
Access-Control-Max-Age: 應該將這個 Preflight 請求緩存多長時間(以秒錶示)。

例如:

Access-Control-Allow-Origin: http://www.nczonline.net
Access-Control-Allow-Method: GET,POST
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000

Preflight 請求結束後,結果將按照響應中指定的事件緩存起來。而爲此付出的代價只是第一次發送這種請求時會多一次 HTTP 請求。

支持 Preflight 請求的瀏覽器包括 Firefox3.5+, Sarari4+ 和 Chrome。IE10 及更早版本都不支持。

帶憑據的請求(Requests with Credential)

默認狀況下,跨域請求不提供憑據(Cookie、HTTP 認證及客戶端 SSL 證實等)。經過將 withCredentials 屬性設置爲 true,能夠指定某個請求應該發送憑據。若是服務器接收帶憑據的請求,會用下面的 HTTP 頭部來響應。

Access-Control-Allow-Credentials: true

若是發送的是帶憑據的請求,單服務器的響應中沒有包含這個頭部,那麼瀏覽器就不會把響應交給 JavaScript (因而,responseText 中將是空字符串,status 的值爲0,並且會調用 onerror() 事件處理程序)。

支持 withCredentials 屬性的瀏覽器有 Firefox3.5+,Sarari4+ 和 Chrome。IE10 及更早版本都不支持。

下面這篇文章講的特別好,介紹了簡單地跨域請求、Preflight 請求和帶憑據的請求三種請求的區別和請求流程。 文章地址:http://www.cnblogs.com/loveis715/p/4592246.html

跨瀏覽器的 CORS (兼容性 CORS的寫法)

即便瀏覽器對 CORS 的支持程度並不都同樣,但全部瀏覽器都支持簡單地(非 Preflight 和不帶憑據的)請求,所以有必要實現一個跨瀏覽器的方案。檢測 XHR 是否支持 CORS 的最簡單的方式,就是檢查是否存在 withCredentials 屬性,再結合檢測 XDomainRequest 對象是否存在,就能夠兼顧全部瀏覽器了。

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/page/");
if(request){
  request.onload = function(){
    //對request。responseText進行處理
  };
  request.send();
}

其餘跨域技術

JSONP

JSONP 原理:JSONP 是經過動態script元素來使用的,使用時能夠做爲 src 屬性指定一個跨域 URL。 這裏的script元素有能力不受限制地從其餘域加載資源。由於 JSONP 是有效的 JavaScript 代碼,因此在請求完成後,即在 JSONP 響應加載到頁面中之後,就會當即執行。來看一個例子。

function handleResponse(response){
  alert("you are at IP address "+ response.ip+", which is in "+response.city+", "+response.region_name);
}
 
var script=document.createElement("script");
script.src="http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script,document.body.firstChild);

下面這篇文章介紹了 JSON 和 JSONP,值得一看 文章地址:http://kb.cnblogs.com/page/139725/

其餘跨域

其餘跨域技術還有 Comet、SSE、WebSockets等。感興趣的讀者能夠查閱相關資料進行了解。

相關文章
相關標籤/搜索