在由ASP.NET所謂前台調用后台、后台調用前台想到HTTP——理論篇中描述了一下ASP.NET新手的三個問題及相關的HTTP協議內容,在由ASP.NET所謂前台調用后台、后台調用前台想到HTTP——實踐篇(一)中已經解答了關於服務器怎么改變頁面元素或“調用”JavaScript的問題,為了避免上篇啰啰嗦嗦一大推,這次直奔主題——ASP.NET前台如何調用后台方法
問題細化
所謂前台調用后台這種問題的提出無非是因為很多新手受了ASP.NET服務器端控件的“誤導”,你不是說前台沒有把JavaScript語句、頁面元素什么的傳到后台,所以前台調用后台,或者后台監視頁面變化是扯淡嘛!那ASP.NET中為什么服務器端控件一點擊就能調用后台制定方法呢?按照你前面的理論即使用了服務器端控件,那么到了瀏覽器頁面也變成最普通不過的HTML文本了,怎么還能夠調用服務器端方法呢?!
確實,瀏覽器呈現的是HTML文本,我們先不用拘泥於ASP.NET怎么做到的前台“調用”后台,我們可以自己實現一下,不過在此之前我們得明白一些過於Post的問題
簡單聊聊Post
我們在看很多網頁的時候都會看到這樣的內容
<form id="form1" method="post" action="Default.aspx"> <input type="text" name="wd" /> <input type="submit" name="test" value="test" /> </form>
這段HTML文本同學們都能看懂,頁面上有一個form,form內有兩個input,點擊submit的時候,頁面form內容會以post方式被提交到Default.aspx這個頁面。
HTTP協議中的Post方法會將所處的form提交到form指定頁面,如果form指定的方法為post(還可能是get等),那么form內有些數據會一並被提交。
既然不是整個form被提交,那么哪些數據會被提交呢?
1.input的name和value
2.input type=”submit” 只有被點擊的時候才將其本身name與value提交
3.對於多個name相同的input type=”checkbox”,只有選中項的值會被提交(多個value以,連接)
4.對於多個name相同的input type=”radio”,只有選中項的value會被提交(同名的radio只能單選)
5.select的name和被選中的option的vaue(不是option的text)
6.textarea 的name與value
這樣被提交的數據可以在提交的頁面的后台中以Request.Form[“name”]的方式查到其值
input type=”submit” “調用”后台方法
知道了form和post的最基本知識,我們就可以模擬ASP.NET的button調用后台方法了,首先准備這樣一個頁面
<form id="form1" method="post" action="Default.aspx"> <input type="submit" name="test" value="test"/> </form>
在后台添加想調用的方法
protected void test_Click(object sender, EventArgs e) { Response.Write("Button Click!"); }
很簡單的內容,后台方法的兩個參數完全可以不用(和事件有關),填上只是為了增加其逼真性,代碼中有幾個需要注意的地方
1.form的方法需要設為post,這個很明顯。。。
2.form的action設為頁面本身,也就是讓頁面提交給自己
3.input要有name
准備工作做好后就可以模擬JavaScript調用后台方法了,在頁面的 OnLoad方法中寫入代碼
protected override void OnLoad(EventArgs e) { if (Request.Form["test"]!=null) { test_Click(this, new EventArgs()); } base.OnLoad(e); }
聰明的同學肯定可以看出端倪了,在點擊test的時候會提交表單,頁面的OnLoad會自動執行,這時候會判斷提交過來的表單中是否包含test(submit被點擊后會被提交,不點擊不提交,所以可以根據name可以判斷出哪個按鈕被按了),就這么簡單,點擊submit的時候,預定義的方法就被調用了,要是頁面上有很多submit,這時候就需要if-else或者switch或者for這樣的東西來判斷到底是哪個submit被點擊了,然后調用對應方法。
asp: button
對比一下,ASP.NET正好用,首先人家的from是這樣的
<form id="form1" runat="server"> </form>
人家自動就給render成這樣了
我們也不需要自己在OnLoad中添加if-else,只要在control的屬性中添加OnClick=”XXX”就可以了,我沒看過.NET源碼,但是我大膽的揣測,雖然不用再OnLoad里if-else了,但應該也是類似的方式處理。
不自動提交表單的怎么辦
細心的同學可能要問了,你這么做是可以,但是依賴於表單提交,我們知道並不是所有的頁面元素都會點一下就提交表單,像控件asp: DropDownList的OnSelectIndexChanged事件,人家也會調用回台方法,那是怎么做的?
其實非常簡單,對於這種需求,仍然可以用表單提交的方式處理,不能自動提交表單,那么我們可以用JavaScript幫助其提交,看個select change “調用”后台函數的例子,首先像這樣修改頁面,添加一個select
<form id="form1" method="post" action="Default.aspx"> <input type="hidden" id="__param" name="__param" /> <input id="test" name="test" type="submit" value="test" /> <select id="select1" name="select1"> <option value="0">0</option> <option value="1">1</option> <option value="2">2</option> </select> <script type="text/javascript"> document.getElementById('select1').onchange = function () { document.getElementById('__param').value = this.name; document.getElementById('form1').submit(); }; </script> </form>
我們除了select元素還添加了兩個內容,一個input type=”hidden”的隱藏元素,一段JavaScript腳本,在JavaScript腳本中對select的onchange綁定了一個方法,當select選擇項變化的時候,把其name賦值給隱藏域的value,然后提交表單,由於隱藏域可定會被提交,這樣我們可以同樣在頁面的Onload中判斷誰的onchange觸發了,然后調用預定義方法,后台代碼是這樣的
protected override void OnLoad(EventArgs e) { string param= Request.Form["__param"]; if (Request.Form["test"]!=null) { test_Click(this, new EventArgs()); } else if (param == "select1") { select_Change(this, new EventArgs()); } base.OnLoad(e); } protected void test_Click(object sender, EventArgs e) { Response.Write("Button Click!"); } protected void select_Change(object sender, EventArgs e) { string newValue = Request.Form["select1"]; Response.Write("Seclect value chaned to " + newValue); }
是不是很簡單呢?先判斷是不是submit,不是的話判斷隱藏域,命中后調用預定義方法。
細心的同學仍然會發現問題,要是一個元素上同時又兩個事件怎么辦?比如還想對這個select添加cilck事件怎么辦,這時候我們可以修改隱藏域的值,不僅記錄name,還記上事件類型,類似這樣
document.getElementById('select1').onchange = function () { document.getElementById('__param').value = this.name+"$ onchange"; document.getElementById('form1').submit(); }; document.getElementById('select1').onclick = function () { document.getElementById('__param').value = this.name+"$ onclick"; document.getElementById('form1').submit(); };
然后在后台判斷的時候,我們可以先把參數按$分割為兩個字符串,分別判斷元素與事件類型,當然我們也可以再添加一個隱藏域,用來記事件類型,大同小異。
還有同學回問,如果我在客戶端同樣寫了select的onclick,不就把你的代碼覆蓋了嗎?這樣我就不能post了,對於這個問題我們大可以換種方式綁定事件處理程序,用addEventListener(attachEven)的方式,這樣post的代碼和自己寫的客戶端的就可以同時存在了,這樣
var select = document.getElementById('select1'); select.onclick = function () { // // // }; select.addEventListener('clcik', function () {//瀏覽器兼容性問題要處理,這里就不演示了 document.getElementById('__param').value = this.name + "$ onclick"; document.getElementById('form1').submit(); }, false);
asp: DropDownList
我們可以看看ASP.NET是怎么處理的,在頁面上寫一個DropDownList,設起AutoPostBack屬性為true
<form id="form1" runat="server"> <asp:DropDownList ID="ddlTest" runat="server" AutoPostBack="true"> <asp:ListItem>1</asp:ListItem> <asp:ListItem>2</asp:ListItem> <asp:ListItem>3</asp:ListItem> </asp:DropDownList> </form>
生成的頁面源碼是這樣的
<html xmlns="http://www.w3.org/1999/xhtml"><head><title> </title><style type="text/css"></style></head> <body cz-shortcut-listen="true"> <form method="post" action="Default.aspx" id="form1"> <div class="aspNetHidden"> <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value=""> <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value=""> </div> <script type="text/javascript"> //<![CDATA[ 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> <div class="aspNetHidden"> <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAAWUpg6MZl3t8/pBYubwqWOJqSwDGOXu15HYfC2efhAyHc1gIfz8erLrnvHjMLCTGHuRsEl8lEzJ1SVLtM7lgCLfggLVPRtDk70yN7TcfPOwwBRLeEs10HSMSLT9zq5aYmmjMFX5QrdQAuJDrw+EXFTJ"> </div> <select name="ddlTest" onchange="javascript:setTimeout('__doPostBack(\'ddlTest\',\'\')', 0)" id="ddlTest"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> </form> </body></html>
主要是這個方法,認真讀讀是不是和我們剛才寫的類似呢,人家傳了兩個隱藏域,一個用來是Target,一個用來及參數
onchange="javascript:setTimeout('__doPostBack(\'ddlTest\',\'\')', 0)"
function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } }
未完待續,與君共勉
通過上面例子分析可以看得出來,JavaScript根本沒有調用后台函數,只是提交了含有預設參數的表單,服務器根據參數調用后台方法,生成特定HTML,所謂前台調用后台也就這么回事兒。
有一個問題值得思考,為什么這種很想當然的做法PHP程序員可以輕松想到,很多.NET初級程序員卻不行,甚至有時候我們都已經在使用了,比如根據Url的QueryString來調用不同方法,或進行不同處理,卻沒有進一步想到更常用的Button、DropDownList是怎么做到的?還是那句話,.NET程序員當自勉,不要被.NET的易用性慣壞,多學習原理知識,不要過分滿足於做出來,而要多追求為什么,這樣才能不斷進步。