由UseSubmitBehavior 引發的“血案” (前章)

前章

前段時間碰到button控件的一個屬性問題UseSubmitBehavior。

在MSDN上查看,UseSubmitBehavior 屬性時,有這樣一句話
“獲取或設置一個布爾值,該值指示 Button 控件使用客戶端瀏覽器的提交機制還是 ASP.NET 回發機制。 ”

" 默認情況下,此屬性的值爲 true ,從而導致 Button 控件使用瀏覽器的提交機制。如果指定爲 false ,則 ASP.NET 頁框架將客戶端腳本添加到頁面,以將窗體發送到服務器。"
這裏講到 客戶端瀏覽器的提交機制 和 ASP.NET 回發機制 ,這兩種機制有些不明白。於是在論壇上發了個帖子,只有一個人簡單的回覆了下。講的內容也只是先前我再msdn上看的介紹UseSubmitBehavior的內容。

 

可能是問題沒有問清楚,於是google了下。發現了一篇文章http://blog.csdn.net/W3031213101/archive/2007/12/18/1946769.aspx 內容如下:

現在就分段進行分析下:

轉載   頁面回傳與js調用服務端事件、PostBack的原理 

第一章、  Asp.net 中服務端控件事件是如何觸發的

Asp.net 中在客戶端觸發服務器端事件分爲兩種情況:
一.   WebControls 中的 Button HtmlControls 中的 Type submit HtmlInputButton
這兩種按鈕最終到客戶端的表現形式爲: < input type ="submit" value ="Submit"> ,這是 Form 表單的提交按鈕,點擊以後會作爲參數發送到服務端,參數是這樣的:
控件的 name 屬性 = 控件的 value 值,對應上面的例子就是: Submit1= Submit 。服務器端會根據接收到的控件的 name 屬性的這個 key 來得知是這個按鈕被點擊了,從而在服務端觸發這個按鈕的點擊事件。

:這裏講到兩種服務器控件<asp:button/>和<asp:imagebutton/>,他們默認 觸發事件的方式就如同前面MSDN講到的瀏覽器的提交機制。我們可以做個測試查看下,到底是怎麼回事。這個很常見,往往最習以爲常的東西卻是最讓人忽視的。
例1:
前臺
<asp:Button ID="Button4" runat="server" Text="Button" οnclick="Button4_Click" />
        <asp:ImageButton ID="ImageButton1" runat="server"
            οnclick="ImageButton1_Click" />
後臺:
protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
        {
           response.write("1");
        }
        protected void Button4_Click(object sender, EventArgs e)
        {
          response.write("2");
        }
在第一次請求頁面以後查看頁面源代碼,
發現沒有什麼可以引發事件觸發的代碼,代碼如下:


<div
>

<input
type
="hidden"
name
="__VIEWSTATE"
id
="__VIEWSTATE"
value
="/wEPDwUJMzg1OTE4NTYzZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUMSW1hZ2VCdXR0b24xkOp6AfsL58gS89iqJEbq4u4u1ec="
/

>

</div
>



<div
>



<input
type
="hidden"
name
="__EVENTVALIDATION"
id
="__EVENTVALIDATION"
value
="/wEWAwKCp4bMAgKF2fXbAwLSwpnTCHJGAELeUt/C0D8Kcm4sB62EZr5U"
/

>
</div

>

<div
>

<input
type

=
"submit"

name
="Button4"
value
="Button"
id
="Button4"
/

>

<input
type

=
"image"

name
="ImageButton1"
id
="ImageButton1"
src
="" style
="border-width:0px;"
/

>

</div
>

但是當我們點擊button 和imagebutton的時候,事件方法會觸發,輸出1,或2。我想這就是客戶端瀏覽器的提交機制。

二.   HtmlControls 中的 Type button HtmlInputButton 和其它所有的控件事件,比如 LinkButton 點擊, TextBox Change 事件等等:
這些事件在客戶端產生後會經過一個統一的機制發送到服務端。
 
1. 首先 asp.net 頁框架會使用兩個 Hidden 域來存放表示是哪個控件觸發的事件,以及事件的參數:
<!― 表示觸發事件的控件,一般是這個控件的 name -->
< input type ="hidden" value ="" />
<!― 表示觸發事件的參數 , 一般是當某個控件有兩個以上的事件時,用來區別是哪個事件 -->
< input type ="hidden" value ="" />
 
2. 服務端會生成一個 jscript 的方法來處理所有這些事件的發送,這段代碼是:
< script language ="javascript" type ="text/javascript">
    function __doPostBack(eventTarget, eventArgument)
    {
        var theform = document.WebForm2;
        theform.__EVENTTARGET.value = eventTarget;
        theform.__EVENTARGUMENT.value = eventArgument;
        theform.submit();
    }
</ script >   

3. 每個會引發服務端事件的控件都會在響應的客戶端事件中調用上面的代碼:
比如, HtmlControls 中的 Type button HtmlInputButton 的點擊事件
<!― 客戶端的點擊事件調用 __doPostBack eventTarget 參數爲 'Button2' ,表示是 name 'Button2’ 控件觸發的事件, eventArgument 爲空,表示這個 Type button HtmlInputButton 只有一個客戶端觸發的服務端事件 -->
<input language="javascript" Button2','')" type="button"   value="Button" />
又比如, TextBox 控件的 Change 事件
<!― 客戶端的 onchange 事件調用 __doPostBack eventTarget 參數爲 ’TextBox1’ ,表示是 name ’TextBox1’ 控件觸發的事件,而 TextBox 控件只有一個客戶端觸發的服務端事件 TextChanged ,故服務器就會去觸發這個 TextBox TextChanged 事件 ->
<input type="text" TextBox1','')" language="javascript" />
 
4. 客戶端觸發事件後調用 __doPostBack 方法,將表示觸發的控件源的 eventTarget 和事件參數 eventArgument 分別付給兩個隱藏域 __EVENTTARGET __EVENTARGUMENT ,然後提交 Form ,在服務端根據 __EVENTTARGET __EVENTARGUMENT 來判斷是哪個控件的什麼事件觸發了。

以上4個描述講的大概意思就是,除了第一講的服務器控件以外,其他的事件的觸發都是通過__doPostBack(eventTarget, eventArgument)來解決的。然後我從工具箱裏託了一些控件出來,第一個放的是<asp:LinkButton/>,運行查看源代碼發現了
function __doPostBack(eventTarget, eventArgument){....}內容存在.。覺得很正確。又拖放了個<asp:TextBox/>控件,運行查看源代碼發現並沒有 __doPostBack(){}方法,心裏就有點懷疑上面的說法。接着拖放了<asp:DropdownList/>,<asp:CheckBox/>,等等幾個服務器控件,並沒有發現 __doPostBack()。是不是事件沒有加,於是在 <asp:TextBox/>加了TextChanged()事件,發現還是老樣子。別的幾個控件也對應的加上了對應的事件。可也是同樣的結果。調試進去後發現,而且事件方法也並沒有觸發。又開始懷疑是不是上面的錯了,最後想了想服務器控件想要回發,不是還要設置AutoPostBack ="true"嘛,於是將此屬性添加上去,果然看到了 <asp:LinkButton/>同樣的效果,接着我又把對應的事件方法又去掉,只留下 AutoPostBack ="true"。 __doPostBack()依然存在。原來autoposback="true"纔是關鍵.那爲什麼 <asp:LinkButton/>爲什麼就不用設置Autopostpack屬性,發現 <asp:LinkButton/> 根本就沒有這個屬性。(嘿嘿,爲什麼沒有呢,·····有待調查)。回頭想想 __doPostBack(){}方法和 Autopostpack屬性兩個從名字上就是相關聯的嘛!

 
第二章  PostBack 的原理
__doPostBack 是一個純粹並且是非常簡單的 javascript 函數,大部分的頁面 PostBack 都是由它觸發的。注意,這裏是 大部分 ,因爲只有兩個 Web Server Control    會自己觸發頁面的 PostBack, 其它的所有控件都是通過 __doPostBack 函數觸發頁面的 PostBack ,那先來看一下這個函數的定義吧:
CODE1:
<input type="hidden" value="" />
<input type="hidden" value="" />
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
通過上面的代碼可以看到, __doPostBack 帶有兩個參數, eventTarget 是標識將要引發頁面 PostBack 的控件 ID eventArgument 參數提供了在引發頁面 PostBack 事件時所帶的額外參數。當然這個函數被函數時,這兩個參數的值將賦值給頁面的兩個隱含變量 __EVENTTARGET __EVENTARGUMENT ,然後調用頁面的 submit 方法提交頁面表單。這就是爲什麼我們可以通過 Request.Form[“__EVENTTARGET”] 獲取得到引發頁面 PostBack 的控件 ID 的原因。
瞭解了 __doPostBack 函數後,我們可以很容易的利用它非常方便地自己觸發自定義的 PostBack 事件。那上面也說了,大部分的控件都是調用
析: Request.Form[“__EVENTTARGET”]方法可以查看到觸發該事件的控件編號(應該是name纔對)(除了button和imagebutton這兩種服務器控件,下面會有介紹)。
 
第三章 Button PostBack 做法
引了頁面的 PostBack ,只有兩個控件是例外, Button ImageButton ,正是因爲它們不是通過調用 __doPostBack 來回發事件,所以通過表單隱含變量 __EVENTTARGET __EVENTARGUMENT 是無法獲取得到引發 PostBack Button ImageButton ID 和參數值的,可通過下面的方式實現
1 )在頁面中加如 LinkButton ,頁面就會在頁面中加載 POSTBACK 所需的 JS
<input type="hidden" value="" />
<input type="hidden" value="" />
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
 
2 )利用 GetPostBackEventReference 給客戶端生成 __doPostBack()
如:
比如前臺頁面
< asp:Button  id ="Button1"  runat ="server"  Text ="Button"></ asp:Button >
1
< href ="#"  onclick ="document.getElementById('Button1').click()"> 觸發服務器端按鈕事件 </ a >
2
利用 GetPostBackEventReference 給客戶端生成 __doPostBack()
前臺
< href ="#"  onclick ="<%=PostBack()%>"> 觸發服務器端按鈕事件 </ a >
後臺
protected  string  PostBack()
        {
            
return  this .Page.GetPostBackEventReference( this .Button1,"haha");
        }
通過 __EVENTARGUMENT="haha" 可以判斷是不是點了那個鏈接的 PostBack Button1 的按鈕事件這麼寫:
if (Request["__EVENTARGUMENT" ]=="haha")
            {
                Response.Write("
這個是鏈接的 PostBack");
            }
            
else
            {
                Response.Write("
這個不是鏈接的 PostBack");
            }

析: 上述主要告訴我們,<asp:button/>和<asp:imagebutton />是不能通過 Request.Form[“__EVENTTARGET”]方法獲取到觸發控件的編號的。作者告訴了一種驗證的方法。但是覺得不太好,自己想了下,之前查過的 UseSubmitBehavior剛好驗證這裏。
總結上面的,再結合到MSDN其實我們可以得到,button服務器控件是特殊的,它除了通過瀏覽器來觸發意外,還可以通過function __doPostBack(eventTarget, eventArgument){....}這個來實現。那怎麼讓這個js的方法出來呢,就是呼應了前面的 UseSubmitBehavior=“false”屬性。
例2:
前臺:
<asp:Button ID="Button4" runat="server" Text="Button" οnclick="Button4_Click" UseSubmitBehavior="false"/>
後臺:
protected void Button4_Click(object sender, EventArgs e)
        {
              response.write("ddd");
        }
 執行以上代碼,查看源文件__doPostBack()方法存在,點擊按鈕輸出ddd,事件方法也執行了。到此兩種觸發機制瞭解了。
Bug
問題:『使用 __doPostBack 會導致』
回發或回調參數無效。在配置中使用 <pages enableEventValidation="true"/> 或在頁面中使用 <%@ Page EnableEventValidation="true" %> 啓用了事件驗證。出於安全目的,此功能驗證回發或回調事件的參數是否來源於最初呈現這些事件的服務器控件。如果數據有效並且是預期的,則使用 ClientScriptManager.RegisterForEventValidation 方法來註冊回發或回調數據以進行驗證。
問題分析及解決方案:『來源網絡』
這個要具體分析。本來這個措施是 asp.net2.0 用來防止客戶端 欺詐 服務器端的。例如本來輸出到客戶端的一個事件被觸發時需要回發的命令是 “__doPostback('ctl01$abc','user_1')” 的,如果採取採取手段把回發參數由 user_1 改爲 user_5 了,服務器端會重新覈對輸出的是不是 user_5 ,發現和這個頁面上一個輸出的腳本不一致,就會產生這個異常。
但是,很多程序員寫的程序按照過去的習慣(或者按照更加高級靈活的設計例如一些 Ajax 組件)沒有考慮這個問題或者是忽略這個欺詐的可能性,寫的程序可能會修改參數或者修改目標控件。
因此這樣具體問題具體分析。不太可能跟瀏覽器距離服務器的遠近有關,應該還是編程邏輯問題。你應該對出異常的畫面以及所使用的數據進行分析。有時候,經常也需要將這個參數設置爲 false ,放棄安全管理



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章