客户端回调
Client Callback 是ASP.NET 2.0新增的一個特性。簡單的說,就是在不刷新頁面的情況下,用javascript向服務器端傳遞參數(shù)、調(diào)用服務器端的方法、并且得到服務器端的返回值進行處理。
?
1>?? Why Client Callback
?
HTTP是無狀態(tài)的協(xié)議。在HTTP協(xié)議之上開發(fā)的項目,常常需要從客戶端調(diào)用服務器端的方法、執(zhí)行服務器端的代碼、從服務器端獲取數(shù)據(jù)。這些都首先需要提交當前的頁面,并且產(chǎn)生一次Postback過程。每次Postback都會讓客戶端的瀏覽器重新Render當前的頁面。
?
這樣的事件模型下,開發(fā)員不得不小心地維護客戶端各控件的當前狀態(tài)以保證它們不會丟失、不得不小心地維護ViewState的狀態(tài)以避免出現(xiàn)不一致的情況。同時,龐大的ViewState也會造成頁面的冗雜、下載速度變慢。另一方面,重新Render頁面當然會耗費瀏覽器的時間來處理。如果頁面很“重”——比如有很多的DHTML或者很多的控件——這當然會增加客戶的等待時間,而頻繁的等待常常會讓客戶不耐煩。最后,頁面的頻繁刷新恐怕會把所有人的眼睛弄花。
?
因此,從客戶端代碼(javascript)里調(diào)用服務器端的方法,以避免頁面重新Rener和頻繁刷新就是很有必要了。
?
2>?? XMLHTTP
?
在ASP.NET 2.0之前,我們實現(xiàn)這一功能的方式主要是XMLHTTP。Client Callback的底層實現(xiàn)機制也是XMLHTTP。XMLHTTP是在客戶端詳服務器端發(fā)送請求并且得到服務器端返回值的一種古老而經(jīng)典的技術,比如下面這個例子。
????functiongetXmlHttp?(?)
????{
????????varXmlHttp?=?newActiveXObject("Msxml2.XMLHTTP.4.0");
????????XmlHttp.Open("GET",?"http://www.yahoo.com",?false);
????????XmlHttp.Send();
????????varhomesource?=XmlHttp.responseText?;?
????????document.all.aa.innerHTML?=homesource?;
????}
</script>
<body?id="bodyTag">
????<input?type="button"name="btnyahoo"value="click?4?yahoo"onclick="getXmlHttp();">
????<div?id="aa"name="aa"></div>
</body>
當點擊button時,觸發(fā)的javascript函數(shù)會創(chuàng)建一個HTTP的請求,并發(fā)送給指定的URL。接收到服務器端的響應之后,把收到的信息展現(xiàn)出來,我們就可以在自己的網(wǎng)頁中看到y(tǒng)ahoo的頁面了。
?
這一句XmlHttp.Open("GET", "http://www.yahoo.com", false);的第一個參數(shù)標識HTTP請求的方式是“GET”或者“POST”。第二個參數(shù)是請求發(fā)送目的的指定URL,可以在URL中包括以“?”標識以“&”連接的查詢字符串。第三個參數(shù)是標識頁面的請求與頁面的響應是否同步,即是否應該等待服務器端的響應之后再進行頁面的下一步響應。此處設為false,既不等待服務器端的響應,以異步方式處理。
?
接下來的一句XmlHttp.Send();是發(fā)送剛剛生成的HTTP請求。我們可以將要傳給服務器端的參數(shù)填入Send函數(shù)的參數(shù)里,在服務器端進行解析。
?
?????? 接受的信息不僅僅是ResponseText的信息。如果把XmlHttp.responseText 改成XmlHttp.GetAllResponseHeaders;,我們就可以看到服務器發(fā)回的所有HTTP頭信息了:)
[
注意:增加XMLHttpRequest讀取中文網(wǎng)頁時返回亂碼的解決辦法
XMLHttpRequest?默認是用UTF-8?傳遞數(shù)據(jù)。當服務端的返回數(shù)據(jù)是UTF-8編碼的時候,它工作得很好(開發(fā)web應用,當服務端和客戶端以及數(shù)據(jù)庫統(tǒng)一使用UTF-8編碼可以有效的避免亂碼問題)。如果服務端設置了正確的Content-Type?Response?Header以及編碼信息,那么XmlHttpRequest也可以正確工作。?
可是當使用XMLHttpRequest讀取中文網(wǎng)頁內(nèi)容時,?如果服務端的程序沒有設置Content-Type?Response?Header,或者Header沒有設置編碼類型,那么我們訪問responseText屬性的時候就可能遭遇亂碼。如以下代碼用XMLHttpRequest獲取雅虎中國網(wǎng)站的星座站首頁:??
xmlhttp?=?getXMLHttpRequest();?
var?url?=?"http://cn.astrology.yahoo.com/";;?
xmlhttp.open("GET",?url,?true);?
xmlhttp.onreadystatechange?=?function(){?
if?(xmlhttp.readyState?==?4)?
????if?(xmlhttp.status?==?200)?
????????alert(xmlhttp.responseText);?
};?
xmlhttp.send(null);?
縱使yahoo中國這樣專業(yè)的網(wǎng)站,對web標準的支持還很不徹底,彈出的html源碼中充斥不符合web標準的html標簽,當然還有已預見的亂碼。?
同樣遺憾的是,FireFox?和?IE?的解決方法也是南轅北轍?
FireFox?
FireFox?的XMLHttpRequest對象支持overrideMimeType方法,可以指定返回數(shù)據(jù)的編碼類型,利用該方法可以解決中文亂碼,前面的代碼修改如下:??
xmlhttp?=?getXMLHttpRequest();?
var?url?=?"http://cn.astrology.yahoo.com/";;?
xmlhttp.open("GET",?url,?true);?
xmlhttp.overrideMimeType("text/html;charset=gb2312");//設定以gb2312編碼識別數(shù)據(jù)?
xmlhttp.onreadystatechange?=?function(){?
if?(xmlhttp.readyState?==?4)?
????if?(xmlhttp.status?==?200)?
????????alert(xmlhttp.responseText);?
};?
xmlhttp.send(null);?
Internet?Explorer?
IE不支持overrideMimeType方法,并且只能用一種很蹩腳的方法來解決,此時需要引入一個雜交的函數(shù):??
function?gb2utf8(data){?
????var?glbEncode?=?[];?
????gb2utf8_data?=?data;?
????execScript("gb2utf8_data?=?MidB(gb2utf8_data,?1)",?"VBScript");?
????var?t=escape(gb2utf8_data).replace(/%u/g,"").replace(/(.{2})(.{2})/g,"%$2%$1").replace(/%([A-Z].)%(.{2})/g,"@$1$2");?
????t=t.split("@");?
????var?i=0,j=t.length,k;?
????while(++i<j)?{?
????????k=t[i].substring(0,4);?
????????if(!glbEncode[k])?{?
????????????gb2utf8_char?=?eval("0x"+k);?
????????????execScript("gb2utf8_char?=?Chr(gb2utf8_char)",?"VBScript");?
????????????glbEncode[k]=escape(gb2utf8_char).substring(1,6);?
????????}?
????????t[i]=glbEncode[k]+t[i].substring(4);?
????}?
????gb2utf8_data?=?gb2utf8_char?=?null;?
????return?unescape(t.join("%"));?
}xmlhttp?=?getXMLHttpRequest();?
var?url?=?"http://cn.astrology.yahoo.com/";;?
xmlhttp.open("GET",?url,?true);?
xmlhttp.onreadystatechange?=?function(){?
if?(xmlhttp.readyState?==?4)?
????if?(xmlhttp.status?==?200)?
????????alert(gb2utf8(xmlhttp.responseBody));?//注意這里要用responseBody?
};?
xmlhttp.send(null);?
gb2utf8函數(shù)直接解析XMLHttpRequest返回的二進制數(shù)據(jù),其中要利用execScript方法來執(zhí)行VBScript的函數(shù)。所以說是一個雜交的函數(shù)。感謝blueidea論壇?提供的算法。?
雖然有了解決的辦法,但形式丑陋,而且不符合web標準。所以應該在編程中盡量避免,如果是開發(fā)web應用,應盡量使用UTF-8編碼,或者在服務端設置正確的編碼信息。至于以上范例,有盜取其他網(wǎng)站內(nèi)容的嫌疑,更是不為提倡。??
gb2utf8函數(shù)確實好用!
]?
3>?? Client Callback in ASP.NET 2.0
?
ASP.NET 2.0中新增了ICallbackEventHandler接口和Page.GetCallbackEventHandler方法來實現(xiàn)Client Callback的調(diào)用。
?
ICallbackEventHandler接口只有一個方法需要實現(xiàn):string RaiseCallbackEvent(string eventArgument);這個方法就是在服務器端處理客戶端調(diào)用請求的方法。參數(shù)eventArgument是從客戶端傳遞的參數(shù),返回值則是需要返回給客戶端的處理結果。
?
Page.GetCallbackEventHandler方法共有三個重載方法,但都大同小異。這個方法的作用是生成一段在初始化Callback時供客戶端調(diào)用的javascript代碼段。以下面這種重載實現(xiàn)為例:
public?string?GetCallbackEventReference(System.Web.UI.Control?control,?string?argument,?string?clientCallback,?string?context,?string?clientErrorCallback);
第一個參數(shù)是實現(xiàn)了ICallbackEventHandler接口的控件,.Net Framework將在內(nèi)部調(diào)用該控件實現(xiàn)的RaiseCallbackEvent的方法。一般該參數(shù)為Page本身。第二個參數(shù)是從客戶端代碼中需要傳遞到服務器段的參數(shù)的變量名。第三個參數(shù)是處理服務器端返回給客戶端處理結果后,客戶端處理返回值的函數(shù)名。第四個參數(shù)是context變量的變量名。最后一個參數(shù)是,在回調(diào)過程中如果在服務器端發(fā)生異常,在客戶端接受異常信息并處理該信息的函數(shù)名。
?
對于客戶端的實現(xiàn),需要在客戶端代碼中定義傳參的變量,定義context變量,并且以<%...%>的方式訪問服務器端變量,以調(diào)用服務器段的Page.GetCallbackEventHandler()方法。當調(diào)用Page.GetCallbackEventHandler方法時,客戶端代碼中的<%...%>部分會被替換成對__doCallback()客戶端函數(shù)的調(diào)用。__doCallback函數(shù)是.Net Framework 2.0內(nèi)置的一個javascript函數(shù),作用是產(chǎn)生一段通過XMLHTTP方式對服務器端代碼的調(diào)用。__doCallback函數(shù)的參數(shù)是同服務器端的GetCallbackEventHandler被調(diào)用時的參數(shù)完全一致。
?
客戶端的context變量是一個很有趣的東西。在整個回調(diào)過程中,context變量都被瀏覽器緩存起來,而且不被發(fā)送到服務器端。context變量是由開發(fā)員設計的,它的作用是在多次回調(diào)間對不同的回調(diào)起標識作用。在客戶端的處理服務器處理結果返回值的函數(shù)里,只需要判斷context變量的自定義屬性,就可以得到關于回調(diào)的標識信息。
?
客戶端的實現(xiàn)包括:若干個初始化回調(diào)的函數(shù)、一個處理回調(diào)返回值的函數(shù)、和一個處理回調(diào)異常的函數(shù)。第二個函數(shù)的兩個參數(shù)分別是服務器端返回值的字符串和context變量。第三個函數(shù)的參數(shù)分別是服務器端的異常信息和context變量。
??????? Client Callback的調(diào)用順序,首先通過調(diào)用GetCallbackEventHandler將客戶端代碼里的<%...%>表達式轉化為__doCallback函數(shù)的調(diào)用。第二步,初始化回調(diào)時,通過__doCallback將要傳遞的參數(shù)發(fā)送給服務器端的RaiseCallbackEvent方法。第三步,回調(diào)結束,RaiseCallbackEvent的返回值傳到客戶端代碼的對應函數(shù)處理。如果回調(diào)有異常,則通過客戶端的錯誤處理函數(shù)處理。
?
4>?? Client Callback小結
?
不是所有的瀏覽器都支持Client Callback,因此可能需要使用.Net Framework 2.0新增的兩個屬性:SupportsCallback和SupportsXmlHttp。這兩個屬性都在Request.Browser下面。現(xiàn)在當然所有的瀏覽器對這兩個屬性的返回值都是一樣的,或者全true,或者全false。之所以要設這兩個屬性,是為了將來的擴展。也許,以后的Client Callback不是用XmlHttp實現(xiàn)的呢:)
?
Client Callback通過XmlHttp,其實是對服務器的另一個同名頁面的請求和處理。因此,使用Client Callback的時候要很小心。首先不要在服務器端代碼里試圖取控件的值,因為這是在另一個頁面里處理,此時取得的不是當前頁面的當前值。也就是說,不要使用從客戶端傳遞的參數(shù)以外的任何值。其次,由于整個回調(diào)過程都沒有將當前的表單提交,因此,要小心的維護頁面的ViewState或者Session值。
總結
- 上一篇: 为什么每个人都应该尝试Ubuntu下篇
- 下一篇: 企业应该如何选型ERP?