在asp.net中客戶端與服務器端的交互默認都是整頁面提交, 此時客戶端將當前頁面表單中的數據(包括一些自動生成的隱藏域)都提交到服務器端,服務器重新實例化一個當前頁面類的實例響應這個請求,然后將整個頁面的 內容重新發送到客戶端,這種處理方式對運行結果沒什么影響,不過這種方式加重了網絡的數據傳輸負擔、加大了服務器的工作壓力,並且用戶還需要等待最終處理 結果。假如是我們希望有這么一個功能,當用戶填寫完用戶名之后就檢查服務器數據庫里是否已存在該用戶名,如果存在就給出已經存在此用戶名的提示,如果不存 在就提示用戶此用戶名可用,對於這種情況其實只需要傳遞一個用戶名作為參數即可,上面的做法卻需要提交整個表單,有點小題大做。解決上面的問題的辦法目前 主流做法有三種:純javascript實現、微軟Ajax類庫實現還有用AjaxPro實現。后兩種做法在稍后的文章中會講到,這里我講另外一種實現: 通過回調技術。
創建實現回調技術的網頁與普通asp.net網頁類似,只不過還需要做以下特殊工作:
(1)讓當前頁面實現 ICallbackEventHandler接口,這個接口定義了兩個方法:string GetCallbackResult ()方法和void RaiseCallbackEvent (string eventArgument)方法。其中GetCallbackResult ()方法的作用是返回以控件為目標的回調事件的結果,RaiseCallbackEvent()方法的作用是處理以控件為目標的回調事件。
(2) 為當前頁提供三個javascript客戶端腳本函數。一個javascript函數用於執行對服務器的實際請求,在這個函數中可以提供一個字符串類型的 參數發送到服務器端;另一個javascript函數用於接收服務器端方法的執行后返回的字符串類型結果,並處理這個結果;還有一個是執行對服務器請求的 幫助函數,在服務器代碼中通過GetCallbackEventReference()方法獲取這個方法的引用時由asp.net自動生成這個函數。
下面我以一個詳細的例子來講述如何使用回調,用Dreamweaver創建一個Register. aspx頁面,代碼如下:
1 <%@ Page Language="C#" %>
2 <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
3 <%@ Import Namespace="System.Text" %>
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
6 <script language="javascript">
7 //客戶端執行的方法
8
9 //下面的方法是接收並處理服務器方法執行的返回結果
10 function Success(args, context)
11 {
12 message.innerText = args;
13 }
14 //下面的方式是當接收服務器方法處理的結果發生異常時調用的方法
15 function Error(args, context)
16 {
17 message.innerText = '發生了異常';
18 }
19 </script>
20
21 <script language="c#" runat="server">
22 string result="";
23 // 定義在服務器端運行的回調方法.
24 public void RaiseCallbackEvent(String eventArgument)
25 {
26
27 if (eventArgument.ToLower().IndexOf("admin") != -1)
28 {
29 result = eventArgument + "不能作為用戶名注冊。";
30 }
31 else
32 {
33 result = eventArgument + "可以注冊。";
34 }
35
36 //throw new Exception();
37 }
38
39 //定義返回回調方法執行結果的方法
40 public string GetCallbackResult()
41 {
42 return result;
43 }
44
45 //服務器上執行的方法
46 public void Page_Load(Object sender, EventArgs e)
47 {
48 // 獲取當前頁的ClientScriptManager的引用
49 ClientScriptManager csm = Page.ClientScript;
50
51 // 獲取回調引用。會在客戶端生成WebForm_DoCallback方法,調用它來達到異步調用。這個方式是微軟寫的方法,會被發送到客戶端
52 //注意這里的"Success"和"Error"兩個字符串分別客戶端代碼中定義的兩個javascript函數
53 //下面的方法最后一個參數的意義:true表示執行異步回調,false表示執行同步回調
54 String reference = csm.GetCallbackEventReference(this, "args", "Success", "", "Error",false);
55 String callbackScript = "function CallServerMethod(args, context) {\n" +reference + ";\n }";
56
57 // 向當前頁面注冊javascript腳本代碼
58 csm.RegisterClientScriptBlock(this.GetType(), "CallServerMethod",
59 callbackScript, true);
60 }
61 </script>
62
63 <html >
64 <head runat="server">
65 <title>無題のページ</title>
66 </head>
67 <body>
68 <form id="form1" runat="server">
69 <table border="1" cellpadding="0" cellspacing="0" width="400px">
70 <tr>
71 <td width="100px">用戶名</td><td><input type="text" size="10" maxlength="20"id="txtUserName" onblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td>
72 </tr>
73 <tr>
74 <td>密碼</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td>
75 </tr>
76 </table>
77 </form>
78 </body>
79 </html>
上面的頁面中我已經添加了足夠詳盡的注視,不過我還是要說明幾點:
(1)
- <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
這句表示當前頁面實現了ICallbackEventHandler接口,如果采用頁面與代碼分離的模式,后台cs代碼則應是:
- public partial class Register : System.Web.UI.Page, ICallbackEventHandler
- {
- //cs代碼
- }
(2)
- <input type="text" size="10" maxlength="20" id="txtUserName" onblur="CallServerMethod(txtUserName.value,null)" />
這里有一個onblur="CallServerMethod(txtUserName.value,null),表示當用戶名文本框失去焦點之后激發CallServerMethod這個客戶端方法,這個客戶端方法是由asp.net動態生成的。
(3)
- csm.GetCallbackEventReference(this, "args","Success","","Error",false);
中的"Success"和"Error"分別代表客戶端的javascript函數,可以在代碼中見到,其中"Success"代表調用服務器端方法成功后要執行的客戶端方法名,"Error"代表調用服務器端方法失敗時調用的客戶端方法名。
該頁面在客戶端生成的HTML代碼如下:
1
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
4 <script language="javascript">
5 //客戶端執行的方法
6
7 //下面的方法是接收並處理服務器方法執行的返回結果
8 function Success(args, context)
9 {
10 message.innerText = args;
11 }
12 //下面的方式是當接收服務器方法處理的結果發生異常時調用的方法
13 function Error(args, context)
14 {
15 message.innerText = '發生了異常';
16 }
17 </script>
18
19
20
21 <html xmlns="http://www.w3.org/1999/xhtml" >
22 <head><title>
23 無題のページ
24 </title></head>
25 <body>
26 <form name="form1" method="post" action="Register.aspx" id="form1">
27 <div>
28 <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
29 <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value=""/>
30 <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"value="/wEPDwUKMTUxMzcyMjQyN2RktUwTa0pYHOlQ0OTLFd6fte0EGow=" />
31 </div>
32
33 <script type="text/javascript">
34 <!--
35 var theForm = document.forms['form1'];
36 if (!theForm) {
37 theForm = document.form1;
38 }
39 function __doPostBack(eventTarget, eventArgument) {
40 if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
41 theForm.__EVENTTARGET.value = eventTarget;
42 theForm.__EVENTARGUMENT.value = eventArgument;
43 theForm.submit();
44 }
45 }
46 // -->
47 </script>
48
49
50 <script src="/web/WebResource.axd?d=jAi7Db33LHl_8HdPSGuzAg2&t=633608119083845334" type="text/javascript"></script>
51
52
53 <script type="text/javascript">
54 <!--
55 function CallServerMethod(args, context) {
56 WebForm_DoCallback('__Page',args,Success,"",Error,false);
57 }// -->
58 </script>
59
60 <table border="1" cellpadding="0" cellspacing="0" width="400px">
61 <tr>
62 <td width="100px">用戶名</td><td><input type="text" size="10" maxlength="20"id="txtUserName" onblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td>
63 </tr>
64 <tr>
65 <td>密碼</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td>
66 </tr>
67 </table>
68
69
70 <script type="text/javascript">
71 <!--
72
73 WebForm_InitCallback();// -->
74 </script>
75 </form>
76 </body>
77 </html>
78
在生成的HTML代碼中多了幾段javascipt教本塊,下面分別說明:
(1)第一部分
- <script type="text/javascript">
- <!--
- var theForm = document.forms['form1'];
- if (!theForm) {
- theForm = document.form1;
- }
- function __doPostBack(eventTarget, eventArgument) {
- if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
- theForm.__EVENTTARGET.value = eventTarget;
- theForm.__EVENTARGUMENT.value = eventArgument;
- theForm.submit();
- }
- }
- // -->
- </script>
這部分代碼是每個asp.net頁面發送到客戶端都會生成的,用於提交當前表單,其中eventTarget參數表示激發提交事件的控件,eventArgument參數表示發生該事件時的參數信息。
(2)第二部分
- <script src="/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750" type="text/javascript"></script>
這部分代碼是用來生成一些用於Ajax調用的js腳本。說穿了,asp.net之所以開發起來方便,是因為微軟在幕后默默地為我們做了很多工作,回調的本質其實就是Ajax調用。
我們可以將“/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750”這部分拷貝到瀏覽器地址欄中,如下圖:
![]()
回車之后會彈出一個下載文件對話框,如下圖:

將這個頁面保存到本地,雖然默認的保存文件的后綴為“.axd”,但它其實是一個文本文件,里面是一些javascript代碼,我們可以用記事本打開,在里面我們可以看到“WebForm_DoCallback”這個方法,如下:

在這個axd文件里做了很多幕后工作,所以我們的回調才相對比較簡單。
(3)第三部分
- <script type="text/javascript">
- <!--
- function CallServerMethod(args, context) {
- WebForm_DoCallback('__Page',args,Success,"",Error,false);
- }// -->
- </script>
這部分代碼是后台生成的,通過獲取Page類的ClientScript屬性,也就是ClientScriptManager的實例注冊到頁面的, 里面定義了兩個javascript函數:CallServerMethod函數和WebForm_DoCallback函數,並且是在 CallServerMethod函數中調用WebForm_DoCallback函數。
(4)第四部分
- <script type="text/javascript">
- <!--
- WebForm_InitCallback();// -->
- </script>
這部分代碼也是幕后生成的,這個javascript函數也可以在那個axd文件中找到。如下圖:

后,會得到可以注冊的提示,如下圖:

當我們輸入“admin”作為用戶名時的結果:

另外,我們將服務器端執行的方法做如下處理,也就是RaiseCallbackEvent(String eventArgument)這個方法,我們在這里拋出一個異常,代碼如下:
- // 定義在服務器端運行的回調方法.
- public void RaiseCallbackEvent(String eventArgument)
- {
- /*
- if(eventArgument.ToLower().IndexOf("admin")!=-1)
- {
- result=eventArgument+"不能作為用戶名注冊。";
- }
- else
- {
- result=eventArgument+"可以注冊。";
- }
- */
- throw new Exception();
- }
再次運行,無論我們以什么作為用戶名,都會得到如下結果:

之所以會出現“發生了異常”這個字符串,是因為我們定義了function Error(args, context)這個javascript函數,並且把它作為調用服務器端方法發生異常時的客戶端處理函數,它的處理方式就是顯示“發生了異常”這個字符串。
