天天看點

asp.net夜話之三:表單和控件

        在今天我主要要介紹的有如下知識點:

HTML表單的送出方式

HTM控件

擷取HTML表單内容

亂碼問題

SQL注入

伺服器端表單

HTML伺服器控件

        對于一個普通HTML表單來說,它有兩個重要的屬性:action和method。

action屬性指明目前表單送出之後由哪個程式來處理,這個處理程式可以是任何動态網頁或者servlet或者CGI(Common Gateway Interface),在asp.net裡面一般都是都aspx頁面來處理。

        method屬性指明form表單的送出方式。它有兩個可能值get和post。

下面我們以一個例子來說明get和post的差別。用Dreamweaver8建立兩個aspx頁面,分别為Register.aspx和 GetUserInfo.aspx。暫時我們不需要在GetUserInfo.aspx頁面寫任何代碼,Register.aspx頁面的代碼如下:

     1. <%@ Page Language="C#" ContentType="text/html" ResponseEncoding="gb2312" %> 

     2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

     3. <html xmlns="http://www.w3.org/1999/xhtml"> 

     4. <head> 

     5. <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> 

     6. <title>使用者注冊</title> 

     7. </head> 

     8. <body> 

     9. <form action="GetUserInfo.aspx" method="get"> 

    10. <table border="1" width="400px"> 

    11. <tr><td colspan="2">使用者注冊</td></tr> 

    12. <tr><td>使用者名</td><td><input type="text" name="username" /></td></tr> 

    13. <tr><td>密碼</td><td><input type="password" name="pwd" /></td></tr> 

    14. <tr><td><input type="submit" value="送出" /></td><td><input type="reset" value="重置" /></td></tr> 

    15. </table> 

    16. </form> 

    17. </body> 

    18. </html>

        注意上面的代碼中“<form action="GetUserInfo.aspx" method="get">”這句,現在我們僅僅将method由“get”變成“post”,我們再次輸入“zhoufoxcn”變成 “123456”,送出表單,我們看到如下結果:

        細心的朋友可能會注意到,當我們以get方式送出的時候,在浏覽器位址欄裡除了接收參數的網頁名之外,還有我們的表單名和參數值,在這裡是 “username=zhoufoxcn&pwd=123456”,而以post方式送出的時候位址欄除了接收參數的網頁之外并沒有這樣參數。具體說來get和post送出方式有如下兩個差別:

        (2)由于浏覽器位址欄能輸入的最大字元數有限制,是以用get方式送出不能處理參數值更大的表單,而post方式則沒有這個限制。

     1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

     2. <html xmlns="http://www.w3.org/1999/xhtml"> 

     3. <head> 

     4. <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> 

     5. <title>使用者注冊</title> 

     6. </head> 

     7. <body> 

     8. <form action="http://www.netskycn.com/GetUserInfo.aspx" method="post"> 

     9. <table border="1" width="400px"> 

    10. <tr><td colspan="2">使用者注冊</td></tr> 

    11. <tr><td>使用者名</td><td><input type="text" name="username"    value="zhoufoxcn" /></td></tr> 

    12. <tr><td>密碼</td><td><input type="password" name="pwd"    value="123';delete * from users" /></td></tr> 

    13. <tr><td><input type="submit" value="送出" /></td><td><input type="reset" value="重置" /></td></tr> 

    14. </table> 

    15. </form> 

    16. </body> 

    17. </html>

        注意表單的action屬性我用了全路徑的url位址,隻要我們能連上網際網路,那麼這個表單一樣可以正确送出的,同樣也能産生觸目驚心的危害!對于這個問題如何避免我會專門抽出一篇文章來講如何防範,這裡暫不贅述。

HTML控件

        HTML控件在上面的例子裡已經用到,它就是指用HTML表單裡的一些列元素來提供使用者互動,它們都是類似“<input type=”text” name=”name””這樣的标記。要呈現什麼的形式由type屬性來決定,可以有“text”、“password”、“radio”、 “checkbox”、“submit”及“reset”等,分别呈現為文本框、密碼框、單選框、複選框及送出按鈕和重置按鈕等。

擷取表單值

        通過用get方式送出表單我們可以看到送出到伺服器的時候,在網頁後面有 “?username=zhoufoxcn&pwd=12345”這麼一個字元串,也就是以表單裡HTML控件的名字=控件值的方式,并且如果存在多個控件,彼此之間以“&”分割,那麼我們就可以以控件名來擷取控件值。擷取HTML控件值常見有以下集中方式:

擷取方式  表單送出方式

 Request.QueryString["控件名"]   适合于get方式送出的表單

Request.Form["控件名"]  适合于post方式送出的表單

Request["控件名"]  同時适合于get和post方式送出的表單

        從上面我們可以看到用Request["控件名"]這種方式對于get和post兩種方式都可行,那麼我們就可以用這種方式來應付所有送出的表單。現在我們在“GetUserInfo.aspx”頁面編寫如下代碼:

     6. <title>系統資料庫單送出的資訊</title> 

     9. 使用者名:<%=Request["username"]%><br /> 

    10. 密碼:<%=Request["pwd"]%> 

    11. </body> 

    12. </html>

        我們再次輸入“zhoufoxcn”和“123456”并送出表單,得到如下畫面:

        得到了我們期望的效果。

        亂碼問題

        對于上面的表單,我們在使用者名和密碼處分别輸入“周公”和“123456”,會得到如下的結果,如下圖:

        這裡使用者就變成了“???”了,之是以出現這樣的情況就是因為我們的編碼原因。這就像我們在酒吧裡對一個懂國語的服務員說來一杯酒,他馬上會送你一杯酒;可是當我們對一個不懂國語的服務員用國語說來一杯酒,他就會一頭霧水了。

        之是以出現亂碼就是因為在這裡客服端請求的編碼和伺服器響應的編碼不一緻,這個問題在《asp.net夜話之二:内置對象》中講Request和Response對象的時候講到過,沒有看過的朋友請傳回去看一下,這裡隻貼出結果:

        從上面的圖上我們看到預設情況下Request的ContentEncoding為UTF8Encoding,而Response的 ContentEncoding為System.Text.DBCSCodePageEncoding,二者的不一緻導緻了輸入中文成了亂碼。

我們常見的編碼有gb2312、gbk和unicode幾種,gb2312能顯示日常生活最常用的6000多個漢字,這對于一般的公文足夠了,可是如果要用來顯示一個古文獻就不行了(據說康熙詞典收錄了4萬多漢字),當它不能顯示的時候也會出現亂碼。Gbk是在gb2312的基礎上擴充的,大概能顯示1萬8 千多個漢字,這對于一般古文獻也差不多夠了。Unicode則更大一些,它能顯示20901個漢字(範圍是從\u4e00到\u9fa5),并且還能顯示日文、韓文、台灣文字、香港文字和新加坡等文字,是以目前很多網站都采用unicode編碼。Utf8編碼就是unicode編碼中的一種,關于它們的編碼原理有興趣的朋友可以查詢有關資料。

        因為目前用Dreamweaver建立動态網頁的時候預設都是采用gb2312編碼(asp和jsp的程式員的網站編碼很多都采用了這個預設值)。如果我們要想正确擷取表單的值,我們可能就需要更改預設編碼,對于上面的亂碼問題,我們隻需要更改 Register.aspx頁面就行,将Register.aspx頁面的這部分更改一下:

   1. <%@ Page Language="C#" ContentType="text/html" ResponseEncoding="gb2312" %>

更改為:

   1. <%@ Page Language="C#" ContentType="text/html" ResponseEncoding="utf-8" %>

        這樣就能正常顯示了,如下:

        注意,使用Microsoft Visual Studio 2005建立網站的時候預設編碼就是utf-8,無需更改。

        在此之前我們見到的表單都是如下格式:

   1. <form action="接收資料頁面" method="post">

并且我們都是利用的HTML控件。現在我們要介紹伺服器端表單,伺服器端表單與前面的表單相比,多了一個runat=”server”标記,如下:

   1. <form id="form1" runat="server">

在伺服器端表單裡可以不用指定action屬性,表示由目前頁面處理,也可以不指定method屬性,預設為post方式送出表單。在伺服器端表單裡,我們不光可以使用HTML控件,還可以使用HTML伺服器控件,還可以使用asp.net控件(asp.net控件稍後會專門花篇幅介紹)。

另外需要注意的是,在一個asp.net頁面中可以有多個不帶runat=”server”标記的表單,但是隻能有一個伺服器端表單。

        HTML伺服器控件與普通伺服器控件不同的是:在普通HTML控件中加上了一個id屬性和一個runat=”server”标記。如下就是一個HTML伺服器控件:

   1. <input type="text" runat="server" id="txtUserName" />

HTML伺服器控件有幾個限制:

在整個asp.net頁面中這個控件id的必須唯一,并且HTML伺服器控件隻能放在HTML伺服器表單中。因為一個asp.net頁面隻能有一個伺服器表單,是以說在伺服器表單中控件的id值必須唯一,因為我們在程式設計的時候通過這個id來通路HTML伺服器控件。如果不唯一就會報錯,如下:

        下面就是一個使用了HTML伺服器控件并且能正确運作的表單:

     6. <title>伺服器表單和HTML伺服器控件</title> 

     9. <form runat="server"> 

    10. <input type="text" runat="server" id="txtUserName" /> 

    11. <input type="password" runat="server" id="txtPassword" /> 

    12. <input type="submit" runat="server" id="btnOK" value="送出" /> 

    13. </form> 

    14. </body> 

    15. </html>

        這個頁面在浏覽器端的HTML源代碼如下:

     5. <title>伺服器表單和HTML伺服器控件</title> 

     8. <form name="ctl00" method="post" action="serverform.aspx" id="ctl00"> 

     9. <div> 

    10. <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNTQ0MjI5MTUzZGR1mFZ52ODFgAKe7Qx9/67qMGFJCA==" /> 

    11. </div> 

    12. <input name="txtUserName" type="text" id="txtUserName" /> 

    13. <input name="txtPassword" type="password" id="txtPassword" /> 

    14. <input name="btnOK" type="submit" id="btnOK" value="送出" /> 

    15. <div> 

    16.    <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWBAKIpeT6DgKl1bKzCQK1qbSRCwLdkpmPAcJ7Zy/C66ypRIq49nr3hQNayqwk" /> 

    17. </div></form> 

    18. </body> 

    19. </html>

        我們可以看到在客服端得到的HTML代碼中都是标準的HTML代碼,我們的文本框和密碼框及伺服器端送出按鈕(因為它在伺服器代碼裡也有runat=”server”标記)變成了如下代碼:

<input name="txtUserName" type="text" id="txtUserName" />

<input name="txtPassword" type="password" id="txtPassword" />

<input name="btnOK" type="submit" id="btnOK" value="送出" />

        也就是,所有的伺服器控件經過伺服器運作之後都會變成标準的HTML控件。這樣我們可以得出一個結論:如果我們的控件功能本來就很簡單,我們就可以直接使用HTML控件,這樣就可以減輕伺服器的負擔,提高運作效率。另外,在上面的代碼中多了一些以前我們沒有見過的部分:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNTQ0MjI5MTUzZGR1mFZ52ODFgAKe7Qx9/67qMGFJCA==" />

<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWBAKIpeT6DgKl1bKzCQK1qbSRCwLdkpmPAcJ7Zy/C66ypRIq49nr3hQNayqwk" />

        因為伺服器會儲存伺服器控件的狀态和屬性,是以它會利用一些隐藏域來儲存這方面的資訊,這部分的内容是經過Base64編碼的。

伺服器控件的好處是我們可以動态在代碼中動态控制伺服器控件的屬性,對于上面的代碼我們改造如下:

     8. <script runat="server"> 

     9. protected void btnOK_Click(Object Src, EventArgs E) 

    10. { 

    11. Response.Write("使用者名:"+this.txtUserName.Value); 

    12.     Response.Write("密碼:"+this.txtPassword.Value); 

    13.     this.btnOK.Disabled=true; 

    14. } 

    15. </script> 

    16. <body> 

    17. <form runat="server"> 

    18. <input type="text" runat="server" id="txtUserName" /> 

    19. <input type="password" runat="server" id="txtPassword" /> 

    20. <input type="submit" runat="server" id="btnOK" value="送出" onserverclick="btnOK_Click" /> 

    21. </form> 

    22. </body> 

    23. </html>

        上面代碼中多了“<script runat="server"></script>”,并且送出按鈕多了一個onserverclick="btnOK_Click" 屬性。在“<script runat="server"></script>”标記中我們寫了一個protected void btnOK_Click(Object Src, EventArgs E)方法,這個方法符合System.EventHandler委托的标準。這個方法有兩個參數,第一個表示由什麼控件激發了這個事件,第二個參數表示事件發生時的一些相關資訊。

        在protected void btnOK_Click(Object Src, EventArgs E)方法中我們利用了類似WinForm中操作控件的方式來操作我們的伺服器控件,這也就是為什麼asp.net頁面成為WebForm的原因。在這個方法裡我們擷取了控件的值,并最後将送出按鈕禁用。

        送出按鈕的onserverclick屬性值表示當這個按鈕點選後由伺服器上的哪個方法進行處理,這個方法要滿足System.EventHandler委托的定義,這裡我們寫了btnOK_Click這個方法名。

這個頁面初次運作的效果如下:

        然後我們分别輸入”zhoufoxcn”和”123456”,送出表單之後的效果如下:

        我們看到在目前頁面輸出了表單控件的值,并且最後送出按鈕呈現灰色狀态,也就是被禁用了。

        上面例子中我們确實就像在WinForm一樣控制asp.net控件,非常友善。其實在asp.net開發中,用的最多的是asp.net伺服器控件,而不是HTML伺服器控件,asp.net控件提供了比HTML伺服器控件更多的靈活性,以後的文章中會繼續探讨asp.net控件。

        實際上不管是HTML控件還是HTML伺服器控件在asp.net裡面用的都不是太多,介紹這部分主要是提及一些被Microsoft Visual Studio 2005隐藏的細節,還有其它動态頁面與asp.net頁面進行互動等問題。

        後記:寫完這篇文章已經是2008年9月18日臨晨兩點了,最近工作比較忙,整個系列下來可能會有30篇左右,我會在工作之餘繼續醞釀。同時也在考慮如何安排更合理,并且盡可能地将實際項目中應該注意到的一些問題貫穿進來。比如這次文章中講到了SQL注入問題,以後在ADO.NET部分我會講解如何防止SQL注入。希望大家多多提寶貴意見。

本文轉自周金橋51CTO部落格,原文連結: http://blog.51cto.com/zhoufoxcn/166808,如需轉載請自行聯系原作者