在由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的易用性慣壞,多學習原理知識,不要過分滿足於做出來,而要多追求為什麼,這樣才能不斷進步。