閱讀目錄
開始
第一代技術:產生用戶端代理指令碼調用服務端
新技術的改進方向
第二代技術:jQuery直接調用WebService
第三代技術:更簡單的資料格式
第四代技術:直接提交表單
多submit按鈕的提交(用jQuery.form實現)
批量輸入控制項的提交(用jQuery.form實現)
提交複雜表單(用jQuery.form實現)
各種AJAX開發方法的對比與總結
相關連結
本文收集了在ASP.NET平台上,一些具體代表性的AJAX開發方法,我將用實際的範例程式碼來示範如何使用它們,讓您感受AJAX的進化曆程,同時也希望將一些優秀的AJAX開發方法介紹給您。
為了方便地介紹這些AJAX開發方法,我將它們劃分為四代技術。
注意:按代劃分AJAX技術純屬我個人的觀點,只為了更好了區分它們。
此外,一些不藉助任何架構類庫的原始AJAX開發方法,本文將不討論它們。
第一代技術:產生用戶端代理指令碼調用服務端
這類技術展示了第一代的AJAX架構的主要設計思想:在服務端為用戶端組建代理程式指令碼, 然後由這些代理指令碼調用服務端,調用者可以不必知道整個調用過程是如何?的, 而且在用戶端的調用風格也基本與服務端的代碼類似。
這類技術的代表作有:ASP.NET AJAX, AjaxPro 二個服務端架構。
下面我將用ASP.NET AJAX架構來示範如何進行AJAX開發。
首先,我們可以建立一個WebService服務:
[WebService(Namespace = "http://tempuri.org/")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]//若要允許使用 ASP.NET AJAX 從指令碼中調用此 Web 服務,請取消對下行的注釋。 [System.Web.Script.Services.ScriptService]public class WebService1 : System.Web.Services.WebService { [WebMethod] public int Add(int a, int b) { return a + b; }
這段代碼就是一個普通的WebService代碼,唯獨需要注意的是:在類的定義上加了一個ScriptService修飾特性。
接下來,我們還需要在一個ASPX頁面中,用ScriptManager為它產生用戶端的代理指令碼:
<asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="/WebService1.asmx" InlineScript="true" /> </Services></asp:ScriptManager>
說明:InlineScript="true"的設定並不是必須的,只是為了讓我們看到ScriptManager到底產生了什麼代碼。
從截圖可以看到,除了引入了二個必要的AJAX用戶端類庫外,還在用戶端為WebService1產生了代理指令碼。
有了這些代碼後,我們可以用下面的JavaScript代碼調用服務端:
function Call_Add(){ WebService1.Add(1,2, ShowResult);}function ShowResult(result){ document.getElementById("output").value = result;}
前面這個樣本太簡單了,再來個參數複雜的樣本吧,還是從先服務端開始,先定義一個參數類型:
public class Customer{ public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Tel { get; set; } public string Email { get; set; }}
WebSerice的代碼:
[WebMethod]public string AddCustomer(Customer customer){ if( customer == null ) return "customer is null."; // 簡單地返回一個XML字串。 // 告訴用戶端:服務端收到了什麼樣的資料。 return XmlHelper.XmlSerialize(customer, Encoding.UTF8);}
仍然借用前面的ScriptManager設定,來看JavaScript的調用代碼:
function Call_AddCustomer(){ var customer = {Name: document.getElementById("txtName").value, Age: document.getElementById("txtAge").value, Address: document.getElementById("txtAddress").value, Tel: document.getElementById("txtTel").value, Email: document.getElementById("txtEmail").value}; WebService1.AddCustomer(customer, ShowResult);}
基本上還是與服務端的編碼方式差不多,先建立一個customer對象,再傳給調用方法。
在那個年代之前(2006年),原始的AJAX實現方式非常複雜,而這種方法讓用戶端代碼風格看起來與服務端十分類似,這確實是個了不起的設計思路。 然而各種技術一直在改進中,現在,2013年,當我們再來回顧這種方法時,會發現它確實存在一些不完善的地方, 以至於現在使用這種方法的人很少了,這種技術被淘汰了!
其實我們可以從另一外角度來思考:如果這種方法真的很優秀,它就不可能被淘汰,正是因為有更優秀的方法出現了,它才會遭到淘汰的命運。
新技術的改進方向
前面介紹的那種AJAX方法能讓用戶端的調用代碼與服務端的代碼風格基本一致,看似很完美的方法為什麼會被淘汰了呢?
我來分析一下這種開發方法的缺陷:
1. 前端代碼不夠獨立,必須要在頁面中添加引用之後才能調用服務端,這其實是一種強耦合。
2. 出現了更優秀的前端架構能簡少擷取調用參數的代碼量。
繼續閱讀本文,您會發現後面我將要介紹的新方法都朝著解決這些缺陷在努力, 這些缺陷也算是指出了新技術的改進方向。
由於前端在調用服務端時,需要事先組建代理程式指令碼,這種設計會阻礙前端代碼的封裝性。 您可以想象一下:如果用戶端需要一個【擷取目前使用者資訊】的功能,而這個功能必須由服務端實現的, 此時,我們就只能指望服務端為用戶端組建代理程式類來調用這個功能了。 但這個功能太有用,許多地方都需要使用,您是不是會想將它提取到一個公用的檔案中?
遺憾的是:就算您將這段調用代碼提取到一個公用的public.js檔案中,每個頁面在引用public.js後, 並不能調用【擷取目前使用者資訊】功能,因為代理指令碼並不一定存在,public.js中的代碼還不能運行起來。 怎麼辦?
答:為每個引用public.js的頁面,再添加ScriptManager引用那個服務吧。
共用性越高的功能,您會發現這種引用代碼的重複度也就越高。
簡單說來,這種方法將WebService, aspx頁面, js代碼耦合在一起了。
由於耦合,您越用越發現越麻煩。
這種組建代理程式指令碼的開發方法雖然能讓前端代碼與後端代碼的風格一致,然而, 前端與後端畢竟不是同一種開發語言,它們要關注的方向也是不一樣的。尤其是當更優秀的前端架構出現後, 這種後端包辦前端的方法不僅讓後端與前端耦合在一起,而且還限制了前端技術的發展, 最終只能是被拋棄的命運!
現在請記住我們為了提交一個Customer資訊寫了什麼樣的代碼:
var customer = {Name: document.getElementById("txtName").value, Age: document.getElementById("txtAge").value, Address: document.getElementById("txtAddress").value, Tel: document.getElementById("txtTel").value, Email: document.getElementById("txtEmail").value};
我在介紹第四代技術時,您會發現它們消失了!
第二代技術:jQuery直接調用WebService
隨意jQuery前端類庫的流行,另一種新的開發方法也開始流行起來了。
HTTP調用本來是個很簡單很透明的技術,只要指定一個URL,構造一個請求體就可以了, 前端代理指令碼的方法將這個過程封裝了起來,由於它的封裝製造了耦合并限制前端的發展。 新的AJAX技術只能突破這個限制,捨棄這些代理指令碼,直接調用後端代碼。
下面的範例程式碼還是基於前面的樣本,唯獨不同的是:不是需要代理類,現在是直接調用服務端。
由於後端的服務代碼沒有改變,我也就不再貼出它們了,而且頁面也不需要再添加什麼引用,我們就直接看前端代碼好了:
$.ajax({ type:"POST", url: "/WebService1.asmx/Add", contentType:"application/json", data:"{a: 1, b: 2}", dataType:'json', success:function(result){ $("#output").val(result.d); }});
這段代碼也能調用服務端的Add方法。
由於服務端採用JSON資料格式,所以需要在用戶端多指定一些要求標頭,而這些事情以前是由代理指令碼完成的。 雖然現在的代碼稍微多一點,但是耦合沒有了,更便於提取一些公用代碼了。
事實上,如果您一直用這種方法調用WebService,那麼jQuery提供了設定預設參數的功能, 我們可以利用這個特性減少代碼量。
還是再來看一下前面那個複雜的參數類型的前端調用代碼吧:
var customer = {Name: $("#txtName").val(), Age: $("#txtAge").val(), Address: $("#txtAddress").val(), Tel: $("#txtTel").val(), Email: $("#txtEmail").val()};var jsonStirng = $.toJSON( {customer: customer} );$.ajax({ type:"POST", url: "/WebService1.asmx/AddCustomer", contentType:"application/json", data: jsonStirng, dataType:'json', success:function(result){ $("#output").val(result.d); }});
主要的代碼還是一樣的,集中在擷取調用參數,但是要轉成JSON格式。
再次一次提醒:不要老是盯著要指定一大堆的jQuery參數,它們可以通過設定預設值的方式解決。
我之所以現在不想讓它們消失,是因為後面還有更好的方法,先留著它們吧。
說明:這種方法不僅可以用於調用WebService,也可以調用WCF (basicHttpBinding),畢竟它們都使用HTTP協議。 不過,WCF還有一堆煩人的配置要設定,但這不是jQuery的問題,這是服務端架構的缺陷。
第三代技術:更簡單的資料格式
前面我們看到了可以利用jQuery調用WebService,不過JSON的轉換過程感覺有些多餘,瀏覽器的提交就沒有這個轉換步驟嘛。 有時看到一些傢伙們著還在JavaScript中拼接JSON字串,我非常反感,所以這次的範例程式碼並沒有給那種方法抹黑,我採用了一個JSON外掛程式。
第三代技術就完美地解決了輸入輸出必須採用JSON問題,而且解決了POST的限制。
由於這次變革改變了資料格式,所以服務端也發生了改變, 新的架構解決了這些問題,例如:ASP.NET MVC架構,MyMVC架構都支援這個開發方式。
來看一下現在服務端的代碼:
[Action]public int Add(int a, int b){ return a + b;}[Action]public string AddCustomer(Customer customer){ // 簡單地返回一個XML字串。 // 告訴用戶端:服務端收到了什麼樣的資料。 return XmlHelper.XmlSerialize(customer, Encoding.UTF8);}
注意:這種AJAX技術沒有與用戶端的任何耦合,只要知道一個URL就可以調用了。 來看用戶端的代碼吧:
$.ajax({ type:"POST", url: "/AjaxDemo/Add.cspx", data: {a: 1, b: 2}, success:function(result){ $("#output").val(result); }});// 第二個調用var customer = {Name: $("#txtName").val(), Age: $("#txtAge").val(), Address: $("#txtAddress").val(), Tel: $("#txtTel").val(), Email: $("#txtEmail").val()};$.ajax({ type:"POST", url: "/AjaxDemo/AddCustomer.cspx", data: customer, success:function(result){ $("#output").val(result); }});
注意:type:"POST"並不是必須的,您也可以把它們改成GET方式提交。
如果您此時用Fiddler查看請求內容,會發現請求的資料採用的是key=value&key=vlaue的格式,與瀏覽器的方式一致。 由於沒有JSON資料格式的限制,現在的參數項簡單了。
現在再看上面這段代碼,主要代碼量在哪裡?
是不是在擷取調用參數那塊?
繼續閱讀本文,我要讓它消失。
第四代技術:直接提交表單
我們來看一下樣本用的表單:
<form id="form1" action="/AjaxDemo/AddCustomer.cspx" method="post"> <p><b>新增客戶資料</b></p> <span>Name: </span><input type="text" name="Name" value="abc" /><br /> <span>Age: </span><input type="text" name="Age" value="20" /><br /> <span>Address: </span><input type="text" name="Address" value="武漢" /><br /> <span>Tel:</span> <input type="text" name="Tel" value="12345678" /><br /> <span>Email: </span><input type="text" name="Email" value="test@163.com" /><br /> <br /> <input type="submit" name="btnAddCustomer" value="儲存客戶資料" /></form>
前面用了三種方法在提交這個表單,下面我們再來看一下更簡單的提交方式:
<script type="text/javascript">$(function(){ // 只需要下面這個調用就可以將表單改成非同步提交方式! $("#form1").ajaxForm({ success:function(result){ $("#output").val(result); } });});</script>
為了更清楚展示這種方法,我甚至把script標籤也貼出來了。
如果您用過jQuery就應該能發現,真正的代碼就只有ajaxForm的那個調用。
說明:ajaxForm是jQuery.form外掛程式提供的功能。
服務端的代碼繼續使用前面樣本的代碼,所以就不貼出了。
再對比前面幾種AJAX的實現方法,您說哪種方法最簡單?
您對第四代AJAX技術有興趣嗎?
我還為它設計了三種不同情境下的樣本,讓您感受它的強大與簡單,請繼續閱讀。
多submit按鈕的提交(用jQuery.form實現)
您認為前面的樣本太簡單了,是嗎?
可能有人會說,如果有多個submit按鈕,這種方法就不合適了,我要響應每個按鈕,為它們指定不同的URL !
真是這樣嗎? 看下面的樣本吧。
相關的前端代碼如下:
<form id="form1" action="/AjaxTestAutoAction/submit.cspx" method="post"> <p><span>Input:</span> <input type="text" name="input" style="width: 300px" value="Fish Li" /></p> <p><span>Output:</span> <input type="text" id="output" style="width: 300px" readonly="readonly" /></p> <input type="submit" name="Base64" value="轉換成Base64編碼" /> <input type="submit" name="Md5" value="計算md5" /> <input type="submit" name="Sha1" value="計算sha1" /></form><script type="text/javascript">$(function(){ $("#form1").ajaxForm(function(result) { $("#output").val(result); });});</script>
服務端代碼:
public class AjaxTestAutoAction{ [Action] public string Base64(string input) { return Convert.ToBase64String(Encoding.Default.GetBytes(input)); } [Action] public string Md5(string input) { byte[] bb = Encoding.Default.GetBytes(input); byte[] md5 = (new MD5CryptoServiceProvider()).ComputeHash(bb); return BitConverter.ToString(md5).Replace("-", string.Empty); } [Action] public string Sha1(string input) { byte[] bb = Encoding.Default.GetBytes(input); byte[] sha1 = (new SHA1CryptoServiceProvider()).ComputeHash(bb); return BitConverter.ToString(sha1).Replace("-", string.Empty); }}
代碼仍然很清晰:
1. 服務端定義三個方法,對應三個submit按鈕。
2. 前端還是只調用一個ajaxForm解決所有問題。
這種方法就是由前端的 jQuery, jQuery.form 以及服務端的MyMVC架構 共同實現的。 想像一下利用其它三種方法需要多少代碼吧。
批量輸入控制項的提交(用jQuery.form實現)
再來展示另一個現實的例子,批量輸入介面的提交。
頁面表單代碼如下:
JavaScript代碼:
<script type="text/javascript">$(function(){ $("#form1").ajaxForm({ success:function(result){ $("#output").val(result); } });});
服務端代碼如下:
這個樣本的全部代碼就這麼多,廢話不想多說,您自己去想用其它方法需要多少代碼!
提交複雜表單(用jQuery.form實現)
前面的樣本都是直接提交表單,沒有驗證表單的過程,而且都以Textbox控制項為主,再來個複雜的表單樣本。
頁面表單代碼如下:
JavaScript代碼:
<script type="text/javascript">$(function(){ $("#form1").ajaxForm({ beforeSubmit: ValidateForm, success:function(result){ $("#output").val(result); } }); function ValidateForm(formData, jqForm, options) { if( jqForm.context.ProductName.value.length == 0 ){ alert("商品名稱不可為空。"); $(jqForm.context.ProductName).focus(); return false; } return true; }});</script>
服務端代碼:
[Action]public string AddProduct(Product product){ // 簡單地返回一個XML字串。 // 告訴用戶端:服務端收到了什麼樣的資料。 return XmlHelper.XmlSerialize(product, Encoding.UTF8);}
各種AJAX開發方法的對比與總結
看過了這些樣本後,我們再來回顧這些AJAX方法的進化過程:
1. 以ASP.NET AJAX為代表的【產生用戶端代理指令碼調用服務端】技術, 為了封裝原始AJAX的複雜過程,服務端為用戶端產生了代理指令碼, 這種封裝後的用戶端在調用方式上與服務端基本一致,看起來簡化了不少,而且降低了開發門檻, 然而,它的優點也是它是它的最大缺點:服務端包辦了用戶端的事情,它完全沒有想到用戶端技術也在進步! 當更優秀的前端技術出現時,它的結局只能是淘汰。
2. 【jQuery直接調用WebService】可以看做是第一代技術的改進,它丟棄了服務端組建代理程式指令碼的功能, 直接在用戶端準備服務端所需要的資料格式,藉助jQuery內部對XmlHttpRequest的封裝,也能方便地調用服務端。 這種方法解決了用戶端、服務、頁面三方的耦合,但是資料格式卻受到序列化方式的限制,使得起來怪怪的。 這種怪怪的感覺其實也是不方便的表現。
3. 為了讓前端更方便地調用服務端,服務端架構只能改變,ASP.NET MVC架構和MyMVC架構都支援更簡單的資料格式, 而且不需要那個多餘的asmx檔案,直接用類庫就可以響應用戶端請求。 這類技術的最主要特點是:用更簡單的資料格式就可以調用服務端。 由於資料格式簡單化,為以後的繼續改進留下了機會。
4. 由於服務端不要求序列化的資料格式,且前端技術仍在進步,終於jQuery.form外掛程式可以讓我們不需要關注表單資料的收集過程, 它能類比瀏覽器的提交行為,正確識別【成功控制項】並將它們提交到服務端,因此代碼得到最大限度的縮減,讓AJAX開發過程更加容易。
前面幾個採用jQuery.form的樣本也讓我們看到:不管表單是什麼樣的,永遠只需要一個調用。
而且它將jQuery的調用過程也做了非常好的封裝,所以我認為這是最容易使用的AJAX開發方法。