典型的http通信:瀏覽器向服務器發出請求,服務器向客戶端返回響應,瀏覽器重新加載頁面,這種不連續的頁面加載方式導致用戶的體驗變得雜亂,缺乏連貫性。
如:
在一般的web應用程序中,用戶填寫表單字段然后單擊submit按鈕,然后整個表單發送到服務器,服務器將它轉發給處理該表單的腳本(PHP或JAVA),腳本執行完成后再發送回全新的頁面。該頁面返回的可能是帶有已經填充某些數據的新表單的html,也可能返回的是一個確認頁面。在服務器上的腳本處理該表單時和返回新頁面的這段時間里,用戶必須等待且屏幕是一片空白,等到服務器返回數據后再重新繪制出頁面。
ajax,用戶在填寫表單時,數據就會發送給一些javascript代碼而不是直接發送給服務器。javascript代碼捕獲表單數據并向服務器發送請求。此時,用戶屏幕上的表單不會閃爍,消失或延遲。用戶不用等待服務器的響應就可以繼續輸入數據,滾動屏幕和繼續使用頁面。然后,服務器將數據返回給javascript代碼(仍然在web表單中),javascript代碼決定如何處理這些數據。它可以迅速更新表單數據,讓人感覺應用程序是立即完成的,表單沒有提交或刷新而用戶就得到了新數據。javascript甚至可以對收到的數據執行一些操作然后再發送另一個請求,完全不需要用戶干預。這就是ajax的強大之處,它可以根據需要自行與服務器進行交互,用戶甚至不知道幕后所發生的一切。
Ajax允許客戶端javascript向服務器請求和接收數據,而無須刷新頁面。這種技術允許開發人員創建不中斷的應用程序,用新數據重載頁面的某些部分。
Ajax表示使用javascript收發來自web服務器的數據,且無須重載整個頁面(僅刷新頁面的局部)的模式------在形式和功能上模擬桌面應用程序。
就目前而言,編寫應用程序時有兩種基本的選擇:
- 桌面應用程序
- WEB應用程序
桌面應用程序通常從其他網站下載,安裝到您的計算機上。桌面應用程序可能使用互聯網下載更新,但運行這些應用程序的代碼在您的桌面計算機上。
WEB應用程序運行在互聯網的某臺WEB服務器上,要通過WEB瀏覽器訪問這種應用程序。
桌面應用程序一般很快,就在您的計算機上運行,不用等待互聯網的鏈接;web應用程序提供了在桌面上不能實現的服務(比如天貓,淘寶這種具有支付功能的網站),但是,隨著web的強大而出現的是等待,等待服務器的響應,等待屏幕的刷新和等待請求返回和生成新的頁面。
XMLHttpRequest的一個優點在于它易于使用,我們只需創建對象,發送請求,等待服務器的響應即可。
ajax是由html,javascript技術,DHTML和DOM組成。
應用Ajax的例子:
QQ空間:
你不停的拉滾動條,動態就一直在更新內容,頁面沒有刷新。
百度搜索:
你開始輸入數據時,一個下拉框就會顯示你可能干興趣的與搜索關鍵字相關的條目。
瀏覽器支持:
IE5+
FF1+
Opera9+
Safari2+
Chrome1+
創建XMLHttpRequest對象:
所有瀏覽器都有javascript對象XMLHttpRequest。
XMLHttpRequest對象起源于一個Microsoft組件XmlHttp,組件在庫MSXML(Active XML解析器)中隨IE5發布,該組件為開發人員提供了一種打開HTTP連接并檢索XML數據的簡單方法。盡管XMLHttpRequest對象名稱包含有XML,但可以使用它獲取其他類型的數據。
跨瀏覽器問題:
瀏覽器對XMLHttpRequest對象的支持分兩種,ActiveX(ie5/6)和內置支持(其他瀏覽器)。這兩類瀏覽器僅在創建XMLHttpRequest對象時有區別,在創建后,其余的代碼則兼容各種瀏覽器。
第一類:使用ActiveX(IE5/6)
因為XMLHttpRequest對象來源于庫MSXML(Active XML解析器),所以在這些瀏覽器中實例化XMLHttpRequest對象,就需要創建一個ActiveX對象。
var myhttp=new ActiveXObject("Microsoft.XMLHttp");
這行代碼創建Microsoft的XMLHttpRequest對象的第一個版本。Microsoft的XmlHttp對象還有其他許多的版本。后面的例子將給出使用最新版本的方式。
第二類:調用內置的構造函數(IE7+,其他瀏覽器)
var myhttp=new XMLHttpRequest();
這行代碼創建的XMLHttpRequest對象沒有不同的版本,只需調用構造函數,就能創建和使用XMLHttpRequest對象。
?兼容:一個函數創建適用于所有的瀏覽器的對象。
function createXmlHttpRequest(){if (window.XMLHttpRequest) {var oHttp = new XMLHttpRequest();return oHttp;} else if (window.ActiveXObject) {var versions = ["MSXML2.XmlHttp.6.0","MSXML2.XmlHttp.3.0"];for (var i = 0; i < versions.length; i++) {try {oHttp = new ActiveXObject(versions[i]);return oHttp;} catch (error) {//do nothing here}}}return null;
}
首先檢查是否存在window.XMLHttpRequest,如果存在,函數即使用XMLHttpRequest構造函數來創建XMLHttpRequest對象(函數遇到return語句退出函數不再執行函數后面的代碼)。否則,就檢測用于ie5/6的window.ActiveXObject,并嘗試用最新的XmlHttp版本創建對象。如果所有的情況都不能創建XMLHttpRequest對象,那么就返回null。
注意:測試順序很重要。先測試window.XMLHttpRequest對象,是因為ie7支持window.XMLHttpRequest和window.ActiveXObject。
使用XMLHttpRequest對象:
一旦創建了XMLHttpRequest對象,就可以準備用它來請求數據。該對象提供的方法和屬性,都與發送請求及處理響應有關。XMLHttpRequest對象唯一的目的就是讓您發送請求和接收響應,其他一切都是javascript,css或者頁面中的其他代碼的工作:改變用戶界面,切換對象,解析服務器返回的數據。
第一步,open()方法:
myHttp.open(requestType,url,boolean)
- false:同步模式發出的請求會暫停所有javascript代碼的執行,知道服務器獲得響應為止,如果瀏覽器在連接網絡時或者在下載文件時出了故障,頁面就會一直掛起。?
- true:異步模式發出的請求,請求對象收發數據的同時,瀏覽器可以繼續加載頁面,執行其他javascript代碼。
myHttp.open("GET","http://localhost/myfile.txt",false);
第二步,send()方法:
var myHttp=createXmlHttpRequest();
myHttp.open("GET","http://localhost/myfile.txt",false);
myHttp.send(null);
這段代碼發出一個GET請求,以同步模式獲取myfile.txt文件。調用send()方法會把該請求發送給服務器。
注意:send()方法必須接收一個參數,甚至可以是null。
status屬性:
如果服務器響應請求并完成了處理但是報告了一個錯誤怎么辦?不管是頁面不存在404,或者訪問數據收到保護404或禁止訪問401.無論哪種情況,這些錯誤碼都是從完成的響應中得到的。換句話說,服務器履行了請求(http就緒狀態是4)但是沒有返回客戶機預期的數據。
每個XMLHttpRequest對象獨有status屬性,該屬性包含了與服務器的響應一起發送回來的http狀態碼。服務器返回狀態碼200表示請求成功,404表示找不到請求的文件。
var myHttp=createXmlHttpRequest();myHttp.open("GET","http://localhost/myfile.txt",false);myHttp.send(null);if(myHttp.status==200){alert("############");}else if(myHttp.status==404){alert("$$$$$$$$$$$$$$$");}else{alert(myHttp.status);}
這段代碼通過檢查status屬性,確定給用戶顯示什么信息。如果返回的狀態碼為200即表示存在請求文件,404則請求文件不存在。
HTTP狀態碼有很多,因此不可能檢查每個狀態碼,大多數情況下,我們只需要關心請求是否成功即可(狀態碼200)。
精簡代碼:
var myHttp=createXmlHttpRequest();myHttp.open("GET","http://localhost/myfile.txt",false);myHttp.send(null);if(myHttp.status==200){alert("success");}else{alert("error"+myHttp.status);}
http狀態碼
- 401:未經授權
- 403:禁止訪問
- 404:沒有找到網頁
異步請求:
http就緒狀態
前面事例演示了同步請求的簡潔性,而異步請求會給代碼添加一些復雜性。因為異步請求必須處理readystatechange事件。在異步請求中,XMLHttpRequest對象提供了readyState屬性,該屬性包含一個數值,每個值都代表請求生存期中的特定狀態(http就緒狀態)。如:
- 0:已經創建對象,但還沒有調用open()方法。
- 1:已經調用open()方法,但還沒有發送請求。
- 2:請求已經發送,標題和狀態已經收到,并可用。
- 3:接收到來自服務器的響應。
- 4:接收完請求數據,表示已經完成請求。
var myHttp=createXmlHttpRequest();function myHttp_readyStateChange(){if(myHttp.readyState==4){if(myHttp.status==200){alert(myHttp.responseText); }else{alert("error"+myHttp.status)}}}myHttp.open("GET","http://localhost/myfile.txt",true);myHttp.onreadystatechange=myHttp_readyStateChange; myHttp.send(null);
這段代碼先利用函數createXmlHttpRequest()創建XMLHttpRequest對象,并把結果返回給變量myHttp。
然后定義一個myHttp_readyStateChange()函數,函數體,先測試readyState屬性是否為4,確定請求是否完成。
接下來檢查請求狀態碼status屬性,確定服務器是否返回所請求的數據。
如果該兩個條件都滿足,就顯示responseText?屬性的值(這是XMLHttpRequest異步請求的數據)。
注意:因為即使請求成功,也有可能得不到需要的信息。在請求服務器端可能發生了錯誤(404,500或其他錯誤)。因此,仍然需要檢查請求的狀態碼。一般在檢查了readyState屬性后還要檢查下status狀態碼。
讀取響應數據
XMLHttpRequest對象屬性--------responseText
確保請求已經處理完成(就緒狀態4),服務器給出了正常的響應(狀態碼200),那么我們就可以處理服務器返回的數據了。返回的數據保存在XMLHttpRequest對象的responseText屬性中。返回的文本以什么形式,逗號分隔的值,管道符(|)分隔的值,還是返回長文本字符,都由服務器決定。
返回XMLHttpRequest異步請求的數據,responseText返回的是純文本。
var response = request.responseText.split("|");document.getElementById("order").value = response[0];document.getElementById("address").innerHTML =response[1].replace(/\n/g, "");
將得到的responseText使用javascript中的split()方法從管道符號進行分隔,得到的數組放到變量response中,那么就可以訪問里面的內容了,數組中的第一個值通過response[0]獲得。
與就緒狀態類似,responseText屬性的值在整個請求的生命周期中也會發生變化。不同瀏覽器之間這種變化還不盡相同。
ie7+:狀態為2時開始有值。
其他瀏覽器:狀態為3時開始有值。
測試responseText屬性的值的代碼:
function myHttp_readyStateChange(){alert(myHttp.responseText);//查看responseText變化if(myHttp.readyState==4){if(myHttp.status==200){// alert(myHttp.responseText); //responseText 返回的僅僅是你PHP代碼執行完,在瀏覽器輸出什么,他就返回什么的.如果它直接返回是PHP代碼,那么說明你的PHP文件沒有執行,查看服務器是否正常運行了PHP文件if(myHttp.responseText=="available"){alert("你的用戶名"+unameValue+"可以用")}else{alert("你的用戶名"+unameValue+"不可以用")}}else{alert("error"+myHttp.status)}}}
}
在所有的文檔和規范中都強調,只有在就緒狀態為4的時候數據才可以安全使用,上面的測試中我們可以知道就算在狀態為3時也是可以得到完整的數據。但是最好還是在4的時候才獲取數據。比較好的做法是是向用戶提供一些反饋,說明處于就緒3時,很快就有響應了------可以在就緒狀態發生變化時更新一些數據,比如,進度條,狀態1時25%,狀態2時50%,狀態3時75%,狀態4時100%(完成)。但是,在不同的瀏覽器中狀態值是不同的。
XMLHttpRequest對象屬性----------responseXML
如果服務器使用XML響應則需要用該屬性來處理響應。
實踐
Ajax驗證表單字段實現在表單提交之前,把數據發送給服務器----允許服務器驗證數據并告訴用戶驗證結果,而無須重載頁面。
因為Ajax是瀏覽器和服務器之間的通信,所以需要一個簡單的服務器應用程序,本例將通過從PHP中請求數據,并給javascript發回響應。
例子:
準備工作
- web服務器 :我這里用的是一個web小軟件你們可以自己下載本地PHP服務器搭建工具MiniServer(迷你WAMP)下載即用;
- PHP文件
把相關文件放到服務器中并啟動服務器,然后瀏覽器http打開HTML文件輸入測試
這里通過PHP驗證用戶名和郵箱。
PHP文件formvalidator.php
<?phpheader("Content-Type: text/plain");
header("Cache-Control: no-cache");$array = Array(Array("jmcpeak", "someone@zyx.com"),Array("pwilton", "someone@xyz.com")
);if (isset( $_GET["username"]) || isset( $_GET["email"]))
{$result = false;if (isset( $_GET["username"])) {$searchTerm = $_GET["username"];for ($i = 0; $i < count($array); $i++) {if (strtolower($array[$i][0]) == strtolower($searchTerm)) {$result = true;}}}if (isset( $_GET["email"])) {$searchTerm = $_GET["email"];for ($i = 0; $i < count($array); $i++) {if (strtolower($array[$i][1]) == strtolower($searchTerm)) {$result = true;}}}$strResult = ($result)?"not available":"available";echo $strResult;
}
else
{echo "php是正常工作。恭喜你!";
}
?>
以上PHP文件提供測試username和email。檢查用戶名Http://本機IP(服務器)/formvalidator.php?username=要查詢的用戶名;檢查郵箱Http://本機IP(服務器)/formvalidator.php?email=要查詢的郵箱;
下面給出HTML文件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title>
</head>
<script>
function checkUname(){var unameValue=document.getElementById("uname").value;if(unameValue==""){alert("請輸入用戶名");return;}var myHttp=createXmlHttpRequest();myHttp.open("GET","formvalidator.php?username="+unameValue,true);myHttp.onreadystatechange=myHttp_readyStateChange; /*每次readyState屬性值發生變化的時候就調用myHttp_readyStateChange()方法*/myHttp.send(null);function myHttp_readyStateChange(){if(myHttp.readyState==4){if(myHttp.status==200){// alert(myHttp.responseText); //responseText 返回的僅僅是你PHP代碼執行完,在瀏覽器輸出什么,他就返回什么的.如果它直接返回是PHP代碼,那么說明你的PHP文件沒有執行,查看服務器是否正常運行了PHP文件if(myHttp.responseText=="available"){alert("你的用戶名"+unameValue+"可以用")}else{alert("你的用戶名"+unameValue+"不可以用")}}else{alert("error"+myHttp.status)}}}
}
function createXmlHttpRequest(){if (window.XMLHttpRequest) {var oHttp = new XMLHttpRequest();return oHttp;} else if (window.ActiveXObject) {var versions = ["MSXML2.XmlHttp.6.0","MSXML2.XmlHttp.3.0"];for (var i = 0; i < versions.length; i++) {try {oHttp = new ActiveXObject(versions[i]);return oHttp;} catch (error) {//do nothing here}}}return null;
}
</script>
<body><form><table><tr><td>用戶名:</td><td><input type="text" id="uname"></td><td><a href="javascript:checkUname()">驗證</a></td></tr><tr><td>郵箱:</td><td><input type="text" id="email"></td><td><a href="javascript:checkEmail()">驗證</a></td></tr></table></form>
</body>
</html>
表單中的用戶名后面的驗證被點擊就執行checkUname()函數,首先獲得ID為uname的值,判斷是否為空,空則提醒輸入并退出函數不執行后面的代碼。條件不符合(不為空)則執行后面的代碼。通過函數createXmlHttpRequest()創建XMLHttpRequest對象并賦值給變量myHttp,?調用open()函數初始化XMLHttpRequest對象 GET方式傳遞參數 ,接收地址是formvalidator.php, true采取異步模式。最后通過send()函數發出請求,前面說過該函數一定要包含參數,實在沒有就給個null。???
前面說過采取異步請求的話XMLHttpRequest對象會提供readyState屬性,該值包含一個數值,代表了不同的請求狀態,每當readyState屬性發生變化的時候都會觸發onreadystatechange事件處理程序。這里把myHttp_readyStateChange里的代碼賦值給onreadystatechange事件處理程序,那么每次readyState屬性發生變化時都執行等號右邊的代碼。
createXmlHttpRequest()函數:根據不同的瀏覽器創建XMLHttpRequest對象;
myHttp_readyStateChange()函數:根據服務器傳回來的信息決定該如何顯示給用戶。
注意:responseText 返回的僅僅是你PHP代碼執行完所輸出的代碼,它在瀏覽器輸出什么,就返回什么的.如果它直接返回是PHP代碼,那么說明你的PHP文件沒有執行,查看服務器是否正常運行了PHP文件
測試:
隨意輸入jmcpeak,pwilton意外的字符點擊驗證:
清空文本框后輸入pwilton點擊驗證:
上面利用Ajax的用法的步驟可以總結為:
創建XMLHttpRequest對象----------open()初始化對象---------send()發出請求----------處理請求返回信息responseText,但是具體的步驟還是按照下面給出的步驟為好。
以后都用這樣的方式用Ajax顯得有的麻煩了,現在我們把運用Ajax的關鍵代碼封裝起來,方便我們以后用吧。
封裝Ajax:創建簡單的Ajax模塊
使用ajax的步驟:
在所有ajax應用程序中基本都雷同的流程:
- 從web表單中獲取需要的數據。
- 建立要鏈接的url。
- 打開到服務器的鏈接。
- 設置服務器在完成后要運行的函數。
- 發送請求
在程序設計中,代碼重用的概念非常重要。
編寫自定義的Ajax模塊XMLHttpRequest使異步請求更易于使用和管理。
function HttpRequest(sUrl, fpCallback)
{this.request = this.createXmlHttpRequest();this.request.open("GET", sUrl, true);var tempRequest = this.request;function request_readystatechange() {if (tempRequest.readyState == 4) {if (tempRequest.status == 200) {fpCallback(tempRequest.responseText);} else {alert("An error occurred while attempting to contact the server.");}}}this.request.onreadystatechange = request_readystatechange;
}HttpRequest.prototype.createXmlHttpRequest = function ()
{if (window.XMLHttpRequest) {var oHttp = new XMLHttpRequest();return oHttp;} else if (window.ActiveXObject) {var versions = ["MSXML2.XmlHttp.6.0","MSXML2.XmlHttp.3.0"];for (var i = 0; i < versions.length; i++) {try {oHttp = new ActiveXObject(versions[i]);return oHttp;} catch (error) {//do nothing here}}}return null;
}HttpRequest.prototype.send = function ()
{this.request.send(null);
}
定義HttpRequest(sUrl, fpCallback) 構造函數:
接受兩個參數,sUrl是XMLHttpRequest對象的請求URL。fpCallback是一個回調函數,當收到服務器的響應時(請求的readyState為4,status為200)調用這個回調函數。
構造函數的一行代碼初始化request屬性,執行createXmlHttpRequest()函數并返回給它一個XMLHttpReques對象。
然后用XMLHttpReques對象的open()方法初始化請求對象。該方法將請求類型設置為GET,使用sUrl參數指定要請求的URL,并把請求對象設置為使用異步方式。
接下來定義了request_readystatechange()函數。
在一個函數中定義另一個函數,稱為:“閉包”。“閉包”(這里指request_readystatechange()函數)不能在容器函數(這里指構造函數)以外訪問,但內部函數可以訪問其容器函數的變量和參數。
在這函數前定義了tempRequest變量,這個變量指向當前對象的request屬性的指針。
fpCallback(tempRequest.responseText); 這行代碼調用了構造函數fpCallback()參數定義的回調函數(下面例子中是fun1()),將responseText屬性傳遞給該回調函數。這樣回調函數(fun1())就可以使用從服務器中接收的信息。就是讓傳進來的函數可以使用responseText屬性。
下面的我用上面的代碼寫一個例子
<script>function fun1(srespon){alert(srespon)}var myhttp=new HttpRequest("http://mytext.txt",fun1);myhttp.send();
</script>
fun1()函數將作為HttpRequest()函數的參數傳入,主要是用以如何處理服務器返回的信息。這里是彈出。
然后執行HttpRequest()函數,參數為要請求的服務器地址和處理服務器返回的信息的函數,然后就交給前面定義好的函數處理,結果是返回XMLHttpReques對象給變量myhttp.
上面的模塊封裝了與異步XMLHttpRequest請求相關的代碼,不需要創建請求對象,處理readystatechange事件,或者檢查請求狀態,因為封裝好的HttpRequest()函數會完成所有這些工作。
現在我們用前面創建的Ajax模塊來做前面的表單例子
代碼:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title>
</head>
<script>
function checkUname(){var uname=document.getElementById("uname").value;if(uname==""){alert("請輸入用戶名");}var myhttp=new HttpRequest("formvalidator.php?username="+uname,fun01);myhttp.send();
}function fun01(r){if(r=="available"){alert("該用戶可用")}else{alert("該用戶不可用")}}function HttpRequest(sUrl, fpCallback)
{this.request = this.createXmlHttpRequest();this.request.open("GET", sUrl, true);var tempRequest = this.request;function request_readystatechange() {if (tempRequest.readyState == 4) {if (tempRequest.status == 200) {fpCallback(tempRequest.responseText);} else {alert("An error occurred while attempting to contact the server.");}}}this.request.onreadystatechange = request_readystatechange;
}HttpRequest.prototype.createXmlHttpRequest = function ()
{if (window.XMLHttpRequest) {var oHttp = new XMLHttpRequest();return oHttp;} else if (window.ActiveXObject) {var versions = ["MSXML2.XmlHttp.6.0","MSXML2.XmlHttp.3.0"];for (var i = 0; i < versions.length; i++) {try {oHttp = new ActiveXObject(versions[i]);return oHttp;} catch (error) {//do nothing here}}}return null;
}HttpRequest.prototype.send = function ()
{this.request.send(null);
}</script>
<body><form><table><tr><td>用戶名:</td><td><input type="text" id="uname"></td><td><a href="javascript:checkUname()">驗證</a></td></tr><tr><td>郵箱:</td><td><input type="text" id="email"></td><td><a href="javascript:checkEmail()">驗證</a></td></tr></table></form>
</body></html>
一個javascript方法捕捉用戶輸入表單的信息并將其發送給服務器,另一個javascript方法監聽和處理響應。所有的這些實際上都依賴于第一個javascript方法(?checkUname()方法),它啟動了整個過程。當然你可以在用戶修改表單值得時候觸發該函數onChange="checkUname()".
首先把前面創建好的Ajax模塊函數HttpRequest()復制進去。模塊函數中包含了創建XMLHttpRequest對象,open方法.send()方法還需調用。
然后new通過HttpRequest()函數創建出一個XMLHttpRequest對象返回給變量myhttp。該HttpRequest()函數需要來個參數,要請求的地址和一個處理返回信息的函數(一個函數作為另一個函數的參數稱為回調函數),然后通過send()方法發出請求,這時不用傳入參數是因為在模塊HttpRequest()函數中早已封裝。
var myhttp=new HttpRequest("formvalidator.php?username="+uname,fun01);
myhttp.send();
最后定義一下要做為模塊HttpRequest()函數參數的回調函數fun01(),該回調函數在模塊HttpRequest()函數中需要一個參數來代表responseText(服務器返回的信息)。fun01()的函數體是根據返回的值做出了一些處理。
這里的運用模塊HttpRequest()函數的順序可以總結為:
用new和HttpRequest創建XMLHttpRequest對象----------然后send()出去---------然后處理回調函數
在請求和響應中使用XML
XML是編程中最常用的數據格式之一。在用Ajax的時候你也許也注意到了ajax中的x并且也知道它代表的是XML。還有就是其核心對象的名稱-------XMLHttpRequest.所以很多人會認為XML是ajax的核心技術之一,有的甚至認為XMLHttpRequest對象只能使用XML。但我想說的是XML確實有應用在ajax中,而且XMLHttpRequest也支持這種用法。但是使用ajax不是一定要使用XML。XMLHttpRequest也只是一個名稱。還有就是XML是一種數據格式而不是傳輸協議。
使用XML
在異步應用程序中XML有兩種基本用法:
- 以XML格式從網頁向服務器發送請求;
- 以XML格式在網頁中從服務器中接收請求。
從客戶機到服務器的XML
使用普通的文本向服務器發送名/值對
在90%的web應用程序中都會用名/值發送到服務器。
var unameValue=document.getElementById("name").value;
var myHttp=createXmlHttpRequest();
url="a.php?username="+escape(unameValue);
myHttp.open("GET",url,true);
myHttp.onreadystatechange=myHttp_readyStateChange;
myHttp.send(null);
向服務器發送XML
將名/值對轉化為XML
如果你想使用XML作為數據格式,首先要做的是找到一種基本XML格式來存儲數據。顯然,名/值對可以全部轉化成XML元素,以其中的名稱作為元素名,值作為元素內容。
<people><name>rose</name>
</people>
在網絡上傳輸XML之前,要保證服務器以及發送數據的腳本能接受XML,很多人認為只要通過網絡發送XML就能被服務器正確的接收和解析。
實際上,需要注意兩點來保證發送的XML數據能被正確的接收:
- 保證向其發送XML的腳本能接受XML數據格式。
- 保證腳本發送的XML數據所采用的格式和結構符合服務的要求的格式和結構。
<address>
<firstname>rose</firstname>
</address>
看起來和上面的XML類似,但有兩點不同:
- 來自客戶機的XML要求封裝在address元素中而不是封裝在people元素中。
- 來自客戶機的XML的名字要求使用firstname元素而不是使用name元素。
使用XML發送名/值對
function sendXML(){
var firstname=document.getElementById("firstname").value;
var xmlString="<address>"+"<firstname>"+firstname+"</firstname>"+"</address>"
var myHttp=createXmlHttpRequest();
url="test.php"
myHttp.setRequestHeader("Content-Type","text/xml");
myHttp.onreadystatechange=myHttp_readyStateChange;
myHttp.send(xmlString);
}
myHttp.setRequestHeader("Content-Type","text/xml");
告訴服務器要發送的是XML而不是一般的名/值對。如果要使用名/值對,對應行該是:
myHttp.setRequestHeader("Content-Type","text/plain");
注意:使用XML要花和實際數據同樣多的實際來處理尖括號和標簽名稱,這有可能使本來很少的輸入變得非常大。除了這些問題之外,和普通的文本以及名/值對相比,在請求中使用XML實際上沒有多少好處。建議除非和只接受XML的腳本打交道,否則在請求中最好使用普通文本方式而不是XML。
- 服務器僅接受XML請求。
- 您正在調用一個僅接受XML請求的遠程API。這實際是上一種情況的特列。
從服務器到客戶機的XML
進入XML
從服務器接收XML
有兩種基本的方式處理一個來自服務器的XML響應:
- 被格式化為XML的純文本
- XML文檔,由一個document對象表示
<address><show><firstname>rose</firstname><place>hk</place> </show> <show><firstname>rose02</firstname><place>hk02</place> </show> <show><firstname>rose03</firstname><place>hk03</place> </show>
</address>
將XML作為純文本(無格式的純文本處理)
處理XML最簡單的選擇就是用于處理服務器返回的其他文本片段相同的方法來處理它,也就是說,基本上就是忽略數據格式,只關注服務器的響應。
這種情況下,你要使用請求對象的是responseText屬性,就像服務器返回的不是XML響應時一樣。
if(myHttp.readyState==4){if(myHttp.status==200){var text=myHttp.responseText;}else{alert("error"+myHttp.status)}}
最終,你將得到把所有一切串聯在一起的XML響應,位于變量text中,如果你輸出此變量,代碼是連續的一個代碼行。
<address><show><firstname>rose</firstname><place>hk</place></show><show><firstname>rose02</firstname><place>hk02</place></show><show><firstname>rose03</firstname><place>hk03</place></show></address>
大多情況下,服務器不會使用空格和回車來格式化XML,而是將一切串聯在一起,當然你的應用程序不會過于在意空格,所以這算不了什么問題,只是會使代碼閱讀起來有些困難。
在這里可以用javascript split()函數來拆分此數據,并通過基本的字符串操作來獲得元素的名稱和值,毫無疑問這是個令人頭疼的過程,你不應該選擇這種方法來獲得XML數據。
將XML當成XML(使用dom處理)
盡管可以如上所述將服務器的XML格式的響應視同為其他任何文本響應來處理,但這樣做沒有很好的理由。你可以使用dom操作XML,javascript中的XMLHttpRequest對象提供了一個屬性(responseXML屬性),可以更好地獲取服務器的XML響應,并且是以DOM document對象的形式獲取它。
現在我們用的不是responseText屬性,而是responseXML屬性,該屬性在XMLHttpRequest對象上可用,它以DOM文檔的格式返回服務器的響應。
if(myHttp.readyState==4){
if(myHttp.status==200){
var xmldoc=myHttp.responseXML;
}else{alert("error"+myHttp.status)}}
現在你就有了一個DOM ?document。
比如我要獲得所有的show元素:
var showelement=xmldoc.getElementsByTagName('show')
現在你可以使用你所了解的全部DOM方法,輕松操縱從服務器處接收到的XML。
解析XML的其他可選方法
除了將XML作為無格式的文本處理或使用DOM處理之外,還有一種選擇,那就是JSON,只要提到XML和ajax應用程序,你就可能會聽到JSON。
大體上,可以用JSON做的事,用DOM都可以做,反之亦然。
處理延遲
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title>
</head>
<body><script>var myhttp=new HttpRequest("http://localhost/file.txt",fun01);document.getElementById("divloading").style.display="block";myhttp.send();function fun01(r){document.getElementById("divloading").style.display="none";}</script>
</body>
</html>
這段代碼利用前面的HttpRequest()函數創建請求。在發送前,在文檔中獲取id為divloading的html元素,這個元素將告訴用戶,數據正在加載,請求完成時就把它隱藏。
注意事項:
XMLHttpRequest對象不能訪問任何不同來源的文檔或文件。
ajax代碼(具體來說就是XMLHttpRequest對象)只能對所在的同一個域發送請求。也就是說在本地機器上運行的代碼只能對本地機器上的服務器端腳本發送請求。、
如:
http://www.mysite.com/page1.html
http://www.mysite.com/page2.html
這是兩個同源頁面,它們有相同的主機(www.mysite.com),相同的協議(http)相同的端口(80)。因為這兩個頁面是同源頁面,所以其中一個頁面的javascript可以訪問另一個頁面。
再如:
http://www.mysite.com/page1.html
https://www.mysite.com/page2.html
主機相同,協議不同一個http,另一個是https.端口不同一個是80另一個是443.所以任何一個頁面都不能訪問另一個頁面。
XMLHttpRequest對象不能訪問任何不同來源的文檔或文件。這個問題很容易解決:使用一個同源的頁面作為代理(proxy),獲取另一個非同源服務器上的數據。
同源策略還影響到frame/iframe技術,即兩個不同來源的頁面位于同一個框架集中,javascript也就無法與這兩個頁面交互。
最后:
總結下發出請求的步驟
接下來就是在所有 Ajax 應用程序中基本都雷同的流程:
- 從 Web 表單中獲取需要的數據。
- 建立要連接的 URL。
- 打開到服務器的連接。
- 設置服務器在完成后要運行的函數。
- 發送請求。
function callServer() {var city = document.getElementById("city").value;var state = document.getElementById("state").value;if ((city == null) || (city == "")) return;if ((state == null) || (state == "")) return;var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);xmlHttp.open("GET", url, true);xmlHttp.onreadystatechange = updatePage;xmlHttp.send(null);
}
記住,XMLHttpRequest
?惟一的目的是讓您發送請求和接收響應。其他一切都是 JavaScript、CSS 或頁面中其他代碼的工作:改變用戶界面、切換圖像、解釋服務器返回的數據。
重定向和重新路由
我們來討論下在ajax中不需要考慮的問題----重定向。在http狀態碼中,這是300系列的狀態碼。包括:
- 301:永久移動。
- 302:找到(請求被重新定向到另一個url/uri上)。
- 305:使用代理(請求必須使用一個代理來訪問所請求的資源)
- ajax應用程序通常都是為一個特定的服務器端腳本,servlet或應用程序而編寫的,因此有時你會知道資源已經移動了,接下來只需修改請求中的url即可。
- ajax只能對所在的同一個域發送請求,也就是說提供生成ajax請求的web頁面的域必須是對這些請求進行響應的域名。因此 ebay.com 所提供的 Web 頁面就不能對一個在 amazon.com 上運行的腳本發出Ajax請求;在 ibm.com 上的 Ajax 應用程序也無法對在 netbeans.org 上運行的 servlets 發出請求。
邊界情況
其他請求類型
HEAD請求
?myHttp.open("HEAD",url,true);
當你這樣生成HEAD請求時,服務器不會像對GET和POST請求一樣返回一個真正的響應。相反,服務器只會返回資源的頭部,這包括響應中內容最好修改的時間,請求資源是否存在和很多其他有用信息。您可以在服務器處理并返回資源之前使用這些信息來了解有關資源的信息。
通過XMLHttpRequest
?對象getAllResponseHeaders()可以獲得所有的響應頭的內容
?myHttp.open("HEAD","a.php?username="+unameValue,true);function myHttp_readyStateChange(){if(myHttp.readyState==4){if(myHttp.status==200){alert(myHttp.getAllResponseHeaders())}else{alert("error"+myHttp.status)}}
檢查URL
您已經知道了當url不存在時就會出現404錯誤。但可能不是服務器的問題而可能是缺少了一個特定的腳本或servlet,所以有的時候我們希望在生成完整的GET或POST請求之前來檢查這個url。
function myHttp_readyStateChange(){if(myHttp.readyState==4){if(myHttp.status==200){alert("url存在");}else if(myHttp.status==404){alert("url不存在");}else{alert("error"+myHttp.status);}}
這段代碼,服務器必須對請求進行響應,并構造一個響應來填充內容長度的響應頭,因此不能節省任何處理時間。