posts - 495,comments - 227,trackbacks - 0
          轉自msdn
          AJAX依靠代理(broker)指派和處理往返服務器的請求。對此,.NET包裝器依靠客戶端XmlHttpRequest對象。多數瀏覽器都支持XmlHttpRequest對象,這就是選擇它的原因。因為包裝器的目的是隱藏XmlHttpRequest的實現,我們就不再詳細討論它了。
          包裝器本身通過將.NET函數標記為AJAX方法來工作。標記之后,AJAX就創建對應的JavaScript函數,這些函數(和任何JavaScript函數一樣)作為代理可以在客戶端使用XmlHttpRequest調用。這些代理再映射回服務器端函數。
          復雜嗎?并不復雜。我們來看一個例子。假設有一個.NET函數:
          ublic int Add(int firstNumber, int secondNumber)
          {
          return firstNumber + secondNumber;
          }
          AJAX .NET包裝器將自動創建名為“Add”、帶有兩個參數的JavaScript函數。使用JavaScript(在客戶機上)調用該函數時,請求將傳遞給服務器并把結果返回給客戶機。
          ?
          初始設置
          我們首先介紹“安裝”項目中使用的.dll的步驟。如果您很清楚如何添加.dll文件引用,可以跳過這一節。
          首先,如果還沒有的話,請下載最新的AJAX版本。解壓下載的文件并把ajax.dll放到項目的引用文件夾中。在Visual Studio.NET中有機Solution Explorer的“References(引用)”節點并選擇Add Reference(添加引用)。在打開的對話框中,單擊Browse(瀏覽)并找到ref/ajax.dll文件。依次單擊Open(打開)和Ok(確認)。這樣就可以用AJAX .NET包裝器編程了。
          建立HttpHandler
          為了保證正常工作,第一步是在web.config中設置包裝器的HttpHandler。不需要詳細解釋HttpHandlers是什么及其如何工作,只要知道它們用于處理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而不是默認處理程序工廠來處理。不需要創建ajax子目錄,使用這個神秘的目錄只是為了讓其他HttpHandlers能夠在自己建立的子目錄中使用.ashx擴展。
          ?
          建立頁面
          現在我們可以開始編碼了。創建一個新頁面或者打開已有的頁面,在file后的代碼中,為Page_Load事件添加以下代碼:
          public class Index : System.Web.UI.Page{
          ? private void Page_Load(object sender, EventArgs e){
          ? ? ? Ajax.Utility.RegisterTypeForAjax(typeof(Index));? ? ?
          ? ? ? //...? ?
          ? }
          ? //...?
          }
          調用RegisterTypeForAjax將在頁面上引發后面的JavaScript(或者在頁面中手工加入以下兩行代碼):
          <script language="javascript" src="ajax/common.ashx"></script>
          <script language="javascript"
          src="ajax/Namespace.PageClass,AssemblyName.ashx"></script>
          其中最后一行的含義是:
          下面是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路徑(查看源代碼,復制粘貼路徑)檢查是否一切正常。如果兩個路徑都輸出一些(似乎)毫無意義的文本,就萬事大吉了。如果什么也沒輸出或者出現ASP.NET錯誤,則表明有些地方出現問題。
          即便不知道HttpHandlers如何工作,上面的例子也很容易理解。通過web.config,我們已經保證所有對ajax/*.ashx的請求都由自定義的處理程序處理。顯然,這里的兩個腳本標簽將由自定義的處理程序處理。
          ?
          創建服務器端函數
          現在來創建可從客戶端調用中異步訪問的服務器端函數。因為目前還不支持所有的返回類型(不用擔心,將在目前的基礎上開發新的版本),我們繼續使用簡單的ServerSideAdd函數吧。在file后的代碼中,向頁面添加下列代碼:
          [Ajax.AjaxMethod()]
          public int ServerSideAdd(int firstNumber, int secondNumber)
          {
          ? return firstNumber + secondNumber;
          }
          要注意,這些函數具有Ajax.AjaxMethod屬性集。該屬性告訴包裝器這些方法創建javaScript代理,以便在客戶端調用。
          ?
          客戶端調用
          最后一步是用JavaScript調用該函數。AJAX包裝器負責創建帶有兩個參數的JavaScript函數Sample.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>
          當然,我們不希望僅僅用這種強大的能力來警告用戶。這就是所有客戶端代理(如JavaScript Sample.ServerSideAd函數)還接受其他特性的原因。這種特性就是為了處理響應而調用的回調函數:
          Sample.ServerSideAdd(100,99, ServerSideAdd_CallBack);

          function ServerSideAdd_CallBack(response){
          ?if (response.error != null){
          ? alert(response.error);
          ? return;
          ?}
          ?alert(response.value);
          }
          從上述代碼中可以看到我們指定了另外一個參數。ServerSideAdd_CallBack(同樣參見上述代碼)是用于處理服務器響應的客戶端函數。這個回調函數接收一個響應對象,該對象公開了三個主要性質
          • Value——服務器端函數實際返回的值(無論是字符串、自定義對象還是數據集)。
          • Error——錯誤消息,如果有的話。
          • Request——xml http請求的原始響應。
          • Context——上下文對象。
          首先我們檢查error只看看是否出現了錯誤。通過在服務器端函數中拋出異常,可以很容易處理error特性。在這個簡化的例子中,然后用這個值警告用戶。Request特性可用于獲得更多信息(參見下一節)。
          ?
          處理類型
          ?
          返回復雜類型
          Ajax包裝器不僅能處理ServerSideAdd函數所返回的整數。它目前還支持integers、strings、double、booleans、DateTime、DataSets和DataTables,以及自定義類和數組等基本類型。其他所有類型都返回它們的ToString值。
          返回的DataSets和真正的.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屬性標記。假設有如下的類:
          [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和UserId)。
          ?
          自定義轉換器
          我們已經看到,Ajax .NET包裝器能夠處理很多不同的.NET類型。但是除了大量.NET類和內建類型以外,包裝器對不能正確返回的其他類型僅僅調用ToString()。為了避免這種情況,Ajax .NET包裝器允許開發人員創建對象轉換器,用于在服務器和客戶機之間平滑傳遞復雜對象。
          ?
          其他事項
          ?
          在其他類中注冊函數
          ?
          上面的例子中,我們的服務器端函數都放在執行頁面背后的代碼中。但是,沒有理由不能把這些函數放在單獨的類文件中。要記住,包裝器的工作方式是在指定類中發現所有帶Ajax.AjaxMethod的方法。需要的類通過第二個腳本標簽指定。使用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
          通過指定類的類型而不是頁面就可以讓Ajax包裝器創建代理:
          private void Page_Load(object sender, EventArgs e){
          ? Ajax.Utility.RegisterTypeForAjax(typeof(AjaxFunctions));
          ? //...
          }
          要記住,客戶端代理的名稱是<ClassName>.<ServerSideFunctionName>。因此,如果ServerSideAdd函數放在上面虛構的AjaxFunctions類中,客戶端調用就應該是: AjaxFunctions.ServerSideAdd(1,2)。
          ?
          代理到底是如何工作的
          ?
          Ajax工具生成的第二個腳本標簽(也可以手工插入)傳遞了頁面的名稱空間、類名和程序集。根據這些信息,Ajax.PageHandlerFactory就能夠使用反射得到具有特定屬性的任何函數的詳細信息。顯然,處理函數查找具有AjaxMethod屬性的函數并得到它們的簽名(返回類型、名稱和參數),從能夠創建必要的客戶端代理。具體而言,包裝器創建一個和類同名的JavaScript對象,該對象提供代理。換句話說,給定一個帶有Ajax ServerSideAdd方法的服務器端類AjaxFunctions,我們就會得到公開ServerSideAdd函數的AjaxFunction 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
          ?
          服務器端函數中很可能需要訪問會話信息。為此,只需要通過傳遞給Ajax.AjaxMethod屬性的一個參數告訴Ajax啟用這種功能。
          在考察包裝器會話能力的同時,我們來看看其他幾個特性。這個例子中我們有一個文檔管理系統,用戶編輯的時候會對文檔加鎖。其他用戶可以請求在文檔可用的時候得到通知。如果沒有AJAX,我們就只能等待該用戶再次返回來檢查請求的文檔是否可用。顯然不夠理想。使用支持會話狀態的Ajax就非常簡單了。
          首先來編寫服務器端函數,目標是循環遍歷用戶希望編輯的documentId(保存在會話中)并返回所有已釋放的文檔。
          [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和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秒鐘調用一次。回調函數檢查響應看看是否有返回值,有的話則在div標簽中顯示該用戶可使用的新文檔。
          結束語
          AJAX技術已經催生了原來只有桌面開發才具備的健壯而豐富的Web界面。Ajax .NET包裝器讓您很容易就能利用這種新的強大技術。請注意,Ajax .NET包裝器和文檔仍在開發之中。


          posted on 2007-01-25 17:05 SIMONE 閱讀(1258) 評論(0)  編輯  收藏 所屬分類: .NET
          主站蜘蛛池模板: 兴化市| 牟定县| 泸溪县| 靖远县| 元阳县| 肥东县| 晴隆县| 普洱| 太谷县| 确山县| 遂溪县| 白水县| 岢岚县| 乌拉特前旗| 阿克陶县| 法库县| 缙云县| 克山县| 尚志市| 安徽省| 天镇县| 汪清县| 永昌县| 广东省| 淮安市| 玉屏| 浠水县| 安庆市| 青龙| 扶沟县| 基隆市| 弥勒县| 旌德县| 格尔木市| 开封市| 永清县| 灵宝市| 甘谷县| 平遥县| 绥化市| 永春县|