文章目錄
AJAX依靠伺服器作為中介來分發和處理請求。為了完成這項工作,.net封裝類依賴於用戶端的請求對象
概述
AJAX依靠伺服器作為中介來分發和處理請求。為了完成這項工作,.net封裝類依賴於用戶端的請求對象,而xmlHttpRequest對象被大部分的瀏覽器支援,因此使用這個對象是一個不錯的解決方案。因為封裝的目的是隱藏xmlHttpRequest的實現,故我們不討論他的實現細節。
封裝類是通過在.net的方法上增加AJAX屬性標記來實現的,一旦被標記,AJAX建立用戶端的javascript函數(這類似於用戶端編寫的javascript函數),並使用xmlhttprequest建立伺服器代理,這個代理映射用戶端的函數到伺服器的處理函數。
複雜嗎?不會的,讓我們看看下面的簡單例子,給出的.net 函數
'VB.Net public function Add(firstNumber as integer, secondNumber as integer) as integer return firstNumber + secondNumber end sub |
//C# public int Add(int firstNumber, int secondNumber) { return firstNumber + secondNumber; } |
Ajax.net會立即自動的建立帶有兩個參數、名字為Add的javascript函數,當用戶端調用這個javascript函數時,請求將從後台送到伺服器端並從將計算結果返回給用戶端。
初始安裝
我們首先從如何把”.dll”安裝到你的項目開始,當然,如果你瞭解如何使用,這一節可以跳過。
如果你還沒有Ajax.dll,可以首先下載AJAX的最新版本。解壓檔案放到可以被你的項目引用的地方,在.net項目中,添加上對其的引用,然後就可以開始使用ajax.dll封裝進行開發了。
|
如果你在安裝引用時遇到了麻煩,可以參考這個連結的說明: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtskaddingremovingreferences.asp |
設定HttpHandle
為了使其可以工作,第一步必須做的是在web.config中安裝設定封裝包的HttpHandle,不去詳細解釋HttpHandle是如何工作的,我們只需要瞭解他們可以用來處理asp.net請求。例如,所有的目的為*.aspx的請求可以通過System.Web.UI.PageHandlerFactory類發送到控制控制代碼,簡單的說,我們把任何向ajax/*.ashx的請求發送到Ajax.PageHandlerFactory的請求處理控制代碼:
<configuration> <system.web> <httpHandlers> <add verb="POST,GET" path="ajax/*.ashx" type="Ajax.PageHandlerFactory, Ajax" /></httpHandlers> ... <system.web> </configuration> |
上面的代碼告訴asp.net把任何匹配到特定的路徑(ajax/*.ashx)請求發送到Ajax.PageHandlerFactory產生的HttpHandle,而不再是預設的Handler factory。你不需要建立ajax子目錄,這是一個只用來臨時使用的虛擬目錄,因此別的HttpHandler可以用他們自己的目錄來使用.ashx副檔名的檔案。
配置頁面
現在我們準備好開始代碼編寫了。開啟一個新的網頁或者已經存在的頁面,在其codebehind檔案中的Page_Load事件中增加以下代碼:
'vb.net Public Class Index Inherits System.Web.UI.Page Private Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load Ajax.Utility.RegisterTypeForAjax(GetType(Index)) '... end sub '... End Class |
//C# public class Index : System.Web.UI.Page{ private void Page_Load(object sender, EventArgs e){ Ajax.Utility.RegisterTypeForAjax(typeof(Index)); //... } //... } |
對RegisterTypeForAjax方法的調用在頁面產生如下的javascript代碼(另外一種選擇,你也可以人工在頁面上添加如下的javascript代碼)
<script language="javascript" src="ajax/common.ashx"></script> <script language="javascript" src="ajax/NAMESPACE.PAGECLASS,ASSEMBLYNAME.ashx"></script> |
上面這段代碼的粗體部分NAMESPACE.PAGECLASS,ASSEMBLYNAME含義如下:
NAMESPACE.PAGECLASS |
當前頁面的命名空間和類 |
ASSEMBLYNAME |
當前頁面的程式集的名稱 |
下面是在AjaxPlay項目中sample.aspx的樣本輸出:
<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %> <html> <head> <script language="javascript" src="ajax/common.ashx"></script> <script language="javascript" src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script></head> <body> <form id="Form1" method="post" runat="server"> ... </form> </body> </html> |
你可以測試一下,人工通過瀏覽器將src path(通過查看源檔案並copy)開啟,一切都能正常的工作。如果輸出了無意義的文本表示到目前為止是正確的,如果輸出asp.net錯誤,則表示中間出現了錯誤。
即使你不瞭解HttpHandle的工作方式,也應該可以理解上面的描述。通過web.config,我們可以確保發送向ajax/*.ashx的請求由我們自訂的控制代碼來處理,很顯然,兩個指令碼標記由自訂控制代碼處理。
編寫服務端函數
現在我們編寫伺服器端函數,他們可以被用戶端非同步調用。儘管現在還不能支援全部的傳回型別,我們仍堅持伺服器端添加功能。在codebehind檔案的頁面類裡,添加下面的方法:
'VB.Net <Ajax.AjaxMethod()> _ Public Function ServerSideAdd (byval firstNumber As Integer, byval secondNumber As Integer) As Integer Return firstNumber + secondNumber End Function |
//C# [Ajax.AjaxMethod()] public int ServerSideAdd(int firstNumber, int secondNumber) { return firstNumber + secondNumber; } |
注意,這個函數有Ajax.AjaxMethod()定製屬性,屬性服務會告知ajax封裝類為此方法建立一個javascript代理,這樣才能被用戶端調用。
定製用戶端調用
接下來在用戶端用javascript調用函數。Ajax封裝類會建立一個javascript函數,帶有兩個參數,名字是 類名.ServerSideAdd。作為最基本的功能,我們所需要作的只是調用這個方法並且傳遞參數:
<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %> <html> <head> <script language="javascript" src="ajax/common.ashx"></script> <script language="javascript" src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script></head> <body> <form id="Form1" method="post" runat="server"> <script language="javascript"> var response = Sample.ServerSideAdd(100,99); alert(response.value); </script> </form> </body> </html> |
當然,我們不能把這麼強大的功能僅僅用來通過alert來提醒瀏覽者,這就是為什麼所有的用戶端代理(如 類名.ServerSideAdd函數)同時帶有一個額外的定製屬性。這個屬性是用來處理伺服器響應的回呼函數:
Sample.ServerSideAdd(100,99, ServerSideAdd_CallBack); function ServerSideAdd_CallBack(response){ if (response.error != null){ alert(response.error); return; } alert(response.value); } |
從上面的代碼中可以看出,我們為ServerSideAdd函數增加了一個額外參數ServerSideAdd_CallBack,這個參數就是用來處理伺服器端響應的用戶端函數。這個callback函數接受一個帶有四個關鍵屬性的response對象:
value |
伺服器端函數執行的傳回值(可能是一個字串、自訂對象或者dataset) |
error |
如果發生錯誤,則返回錯誤資訊. |
request |
原始的xmlHttpRequest請求 |
context |
一個內容物件 |
我們首先應該檢查是否有錯誤發生,你可以通過在伺服器端函數拋出異常來實現這個error屬性。在上面這個例子中,我們簡單的alert了一個值,就是value屬性;request屬性可以用來取得額外的資訊(見下面的表格)
|
如果你想瞭解更多的關於XmlHttpRequest的知識,可以查看下面的連結: http://www.quirksmode.org/blog/archives/2005/02/xmlhttp_linkdum.html |
處理類型
返回一個複雜類型
Ajax可以支援除了我們上面ServerSideAdd函數返回的Int值以外很多類型。他可以直接支援integers, strings, double, booleans, DateTime, DataSets 和 DataTables,也支援簡單的自訂類型和數組。其他的類型通過其ToString方式來返回字串。
返回DataSet的工作就像真正的.net Dataset.給出一個返回DataSet的伺服器端函數,我們可以通過下面的方法在用戶端顯示:
<script language="JavaScript"> //Asynchronous call to the mythical "GetDataSet" server-side function function getDataSet(){ AjaxFunctions.GetDataSet(GetDataSet_callback); } function GetDataSet_callback(response){ var ds = response.value; if(ds != null && typeof(ds) == "object" && ds.Tables != null){ var s = new Array(); s[s.length] = "<table border=1>"; for(var i=0; i<ds.Tables[0].Rows.length; i++){ s[s.length] = "<tr>"; s[s.length] = "<td>" + ds.Tables[0].Rows[i].FirstName + "</td>"; s[s.length] = "<td>" + ds.Tables[0].Rows[i].Birthday + "</td>"; s[s.length] = "</tr>"; } s[s.length] = "</table>"; tableDisplay.innerHTML = s.join(""); } else{ alert("Error. [3001] " + response.request.responseText); } } </script> |
Ajax也可以支援自訂類,但是需要這個類是可以被序列化的。如下面的類:
[Serializable()] public class User{ private int _userId; private string _firstName; private string _lastName;public int userId{ get { return _userId; } } public string FirstName{ get { return _firstName; } } public string LastName{ get { return _lastName; } } public User(int _userId, string _firstName, string _lastName){ this._userId = _userId; this._firstName = _firstName; this._lastName = _lastName; } public User(){} [AjaxMethod()] public static User GetUser(int userId){ //Replace this with a DB hit or something return new User(userId,"Michael", "Schwarz"); } } |
我們需要通過調用RegisterTypeForAjax向伺服器註冊GetUser代理:
private void Page_Load(object sender, EventArgs e){ Utility.RegisterTypeForAjax(typeof(User)); } |
在用戶端我們可以通過這樣的方式調用GetUser函數:
<script language="javascript"> function getUser(userId){ User.GetUser(GetUser_callback); }function GetUser_callback(response){ if (response != null && response.value != null){ var user = response.value; if (typeof(user) == "object"){ alert(user.FirstName + " " + user.LastName); } } } getUser(1); </script> |
傳回值同伺服器端對象一樣有三個屬性(FirstName, LastName and UserId)。
譯者註:其他的類型只能由開發人員通過在伺服器端函數在傳回值時自訂轉換為ajax支援的類型來實現了,ajax推薦使用ToString方法。
其他工作方式
在其他類註冊函數
在上面的例子及描述中,我們都是通過在頁面的codebehind檔案裡完成函數的註冊,但並不是說只能在頁面的codebehide檔案裡完成註冊,我們也可以在其他類中註冊函數。回憶一下,Ajax封裝類是通過在特定類裡面尋找那些有Ajax.AjaxMethod()屬性的方法來完成工作的,這些類在用戶端又通過兩個script片斷來完成傳回值描述。使用Ajax.Utility.RegisterTypeForAjax,我們可以得到任何我們想得到類的詳細內容。例如,下面的例子可以說明我們在其他類中使用伺服器端函數是合法的:
Public Class AjaxFunctions <Ajax.AjaxMethod()> _ Public Function Validate(username As String, password As String) As Boolean 'do something 'Return something End Function End Class |
不過我們需要首先在調用頁面註冊這個代理類,類的名字不再是頁面類,而是我們使用的這個類:
'Vb.Net Private Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load Ajax.Utility.RegisterTypeForAjax(GetType(AjaxFunctions)) '... End Sub |
//C# private void Page_Load(object sender, EventArgs e){ Ajax.Utility.RegisterTypeForAjax(typeof(AjaxFunctions)); //... } |
記住,用戶端調用使用這種格式的名字<ClassName>.<ServerSideFunctionName>。因此,如果上面的Serversideadd函數位於AjaxFunctions類,而不是頁面類的話,用戶端調用則變為:AjaxFunctions.ServerSideAdd(1,2)
代理是怎樣工作的呢?
第二個script標籤,如下面的樣本
<script type="text/javascript" src="/cqyd/ajax/cqyd.SchemeSendWatch,cqyd.ashx"></script>
是由Ajax utility通過命名空間、類以及頁面程式集自動產生的(當然也可以人工加入),從這一點我們可以想到Ajax.PageHandlerFactory是通過反射來取得有定製屬性的函數的細節。很顯然,Handler尋找帶有AjaxMethod定製屬性的函數,取得他們的特徵(傳回型別、名稱、參數)並依據這些資訊建立用戶端代理。特別的,ajax建立一個與類型相同的JavaScript對象作為代理。
返回Unicode字元
Ajax.net可以從伺服器端向用戶端返回Unicode字元,為了做到這一點,在服務端函數返回時返回的值必須是Html編碼的:
[Ajax.AjaxMethod] public string Test1(string name, string email, string comment){ string html = ""; html += "Hello " + name + "<br>"; html += "Thank you for your comment <b>"; html += System.Web.HttpUtility.HtmlEncode(comment); html += "</b>."; return html; } |
SessionState
在服務端函數,你可能需要接受傳送的session資訊,為了做到這一點,必須要在想實現這個方式的服務端函數的Ajax.AjaxMethod屬性上傳遞一個參數。
在查看ajax可以支援session的時候,我們先看看其他的特徵。在下面這個例子中,我們有一個文件管理系統,當一個使用者對文檔進行編輯的時候會給這個文檔加鎖,其他使用者需要等到這個文檔可用時才能修改。不使用Ajax,使用者需要不斷等待重新整理,因為不得不不斷的去檢查文檔的狀態是否為可用,這當然不是一個很好的方案。用ajax的session state支援,這就比較容易了。
我們首先寫一個函數,這個函數通過遍曆文檔ID找到使用者需要的文檔,儲存到session裡,並返回沒有佔用的文檔:
'Vb.Net <Ajax.AjaxMethod(HttpSessionStateRequirement.Read)> _ Public Function DocumentReleased() As ArrayList If HttpContext.Current.Session("DocumentsWaiting") Is Nothing Then Return Nothing End If Dim readyDocuments As New ArrayList Dim documents() As Integer = CType(HttpContext.Current.Session("DocumentsWaiting"), Integer()) For i As Integer = 0 To documents.Length - 1 Dim document As Document = document.GetDocumentById(documents(i)) If Not document Is Nothing AndAlso document.Status = DocumentStatus.Ready Then readyDocuments.Add(document) End If Next Return readyDocuments End Function |
//C# [Ajax.AjaxMethod(HttpSessionStateRequirement.Read)] public ArrayList DocumentReleased(){ if (HttpContext.Current.Session["DocumentsWaiting"] == null){ return null; }ArrayList readyDocuments = new ArrayList(); int[] documents = (int[])HttpContext.Current.Session["DocumentsWaiting"]; for (int i = 0; i < documents.Length; ++i){ Document document = Document.GetDocumentById(documents[i]); if (document != null && document.Status == DocumentStatus.Ready){ readyDocuments.Add(document); } } return readyDocuments; } } |
我們在屬性參數中指明了HttpSessionStateRequirement.Read(還可以是Write and ReadWrite)
下面寫javascript函數來使用這個方法帶來的結果:
<script language="javascript"> function DocumentsReady_CallBack(response){ if (response.error != null){ alert(response.error); return; }if (response.value != null && response.value.length > 0){ var div = document.getElementById("status"); div.innerHTML = "The following documents are ready!<br />"; for (var i = 0; i < response.value.length; ++i){ div.innerHTML += "<a href=\"edit.aspx?documentId=" + response.value[i].DocumentId + "\">" + response.value[i].Name + "</a><br />"; } } setTimeout('page.DocumentReleased(DocumentsReady_CallBack)', 10000); } </script> <body onload="setTimeout('Document.DocumentReleased(DocumentsReady_CallBack)', 10000);"> |
頁面載入後每10秒鐘向伺服器函數請求一次。如果有返回,則call back函數檢查response,並把最新的結果顯示出來。
結論
Ajax技術可以給用戶端提供豐富的客戶體驗,而ajax.net為您容易的實現這樣強大的功能提供了可能。