【轉(zhuǎn)載】ASP.NETPage那點(diǎn)事-創(chuàng)新互聯(lián)

Page,我想每個(gè)ASP.NET開發(fā)人員對(duì)它應(yīng)該都是比較熟悉的。
這次的博客我就打算專門談?wù)勊?。不過呢,我不打算說 在Page中使用控件的一些話題,也不會(huì)說Page的生命周期的相關(guān)話題,因?yàn)槲艺J(rèn)為這些話題被人談?wù)摰拇螖?shù)實(shí)在是太多了,尤其是市面上的ASP.NET的書籍,都會(huì)比較喜歡這些話題。

成都創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括北塔網(wǎng)站建設(shè)、北塔網(wǎng)站制作、北塔網(wǎng)頁制作以及北塔網(wǎng)絡(luò)營(yíng)銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,北塔網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到北塔省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

我不喜歡重復(fù),因此今天我只想談些人家不談的那點(diǎn)事,但我認(rèn)為它們?nèi)匀缓苤匾?/p>回到頂部 一些重要的Page指令

雖然Page公開了很多屬性,讓我們可以在運(yùn)行時(shí)調(diào)整它的狀態(tài)與行為,但是,還有些重要的參數(shù)卻是以“指令”方式提供的,需要在設(shè)計(jì)時(shí)就指定。
下面是我整理的一些我認(rèn)為 比較重要并且經(jīng)常需要使用 的指令:

@ Page 指令
Async指示頁面要不要以異步頁的方式運(yùn)行。默認(rèn)值為 false。
注意:如果需要開發(fā)一個(gè)異步頁,必須要設(shè)置這個(gè)指令屬性,以便在編譯頁面時(shí)實(shí)現(xiàn)IHttpAsyncHandler接口。
AsyncTimeOut定義在處理異步任務(wù)時(shí)使用的超時(shí)時(shí)間間隔(以秒為單位)。默認(rèn)值為 45 秒。 該值必須是整數(shù)。
此屬性僅對(duì)Page.RegisterAsyncTask()方法有效。
AutoEventWireup指示頁的事件是否自動(dòng)綁定。如果啟用了事件自動(dòng)綁定,則為 true;否則為 false。默認(rèn)值為 true。
所謂的事件自動(dòng)綁定就是識(shí)別Page_Load這些類型的頁面事件處理程序。
事件自動(dòng)綁定發(fā)生在頁面第一次請(qǐng)求時(shí),在后續(xù)請(qǐng)求中會(huì)使用緩存的委托調(diào)用列表,因此對(duì)性能的輕微影響也僅發(fā)生在第一次請(qǐng)求時(shí)。
EnableSessionState定義頁的會(huì)話狀態(tài)要求。如果啟用了會(huì)話狀態(tài),則為 true;如果可以讀取會(huì)話狀態(tài)但不能進(jìn)行更改,則為 ReadOnly;否則為 false。默認(rèn)值為 true。
建議不要保留默認(rèn)值:
1. 如果只是部分頁面需要使用Session,那么不需要使用Session的頁面請(qǐng)顯式設(shè)置為關(guān)閉狀態(tài),
2. 對(duì)于不需要修改Session的頁面請(qǐng)?jiān)O(shè)置為ReadOnly。
頁面編譯時(shí)就是根據(jù)這個(gè)指令來決定生成對(duì)IRequiresSessionState,IReadOnlySessionState接口的實(shí)現(xiàn)關(guān)系。
EnableViewState指示是否在頁請(qǐng)求之間保持視圖狀態(tài)。如果要保持視圖狀態(tài),則為 true;否則為 false。默認(rèn)值為 true。
強(qiáng)烈建議不要使用視圖狀態(tài),因?yàn)樗鉀Q的問題比引用的問題更多。
MasterPageFile設(shè)置內(nèi)容頁的母版頁或嵌套母版頁的路徑。支持相對(duì)路徑和絕對(duì)路徑。
可以在頁面的PreInit事件中動(dòng)態(tài)設(shè)置同名的屬性實(shí)現(xiàn)動(dòng)態(tài)切換母版頁的功能。
Trace指示是否啟用跟蹤。如果啟用了跟蹤,則為 true;否則為 false。默認(rèn)值為 false。
啟用跟蹤對(duì)頁面調(diào)試非常有用,我們可以調(diào)用Page.Trace對(duì)象的方法輸出一些有價(jià)值的診斷信息。
Page.Trace.Write()采用默認(rèn)字體輸出,Page.Trace.Warn()采用紅色字體輸出。
如果你總是記不住頁面生命周期,啟用跟蹤后,一切就擺在你眼前。
ValidateRequest指示是否應(yīng)發(fā)生請(qǐng)求驗(yàn)證。如果為true,請(qǐng)求驗(yàn)證將根據(jù)具有潛在危險(xiǎn)的值的硬編碼列表檢查所有輸入數(shù)據(jù)。默認(rèn)值為 true。
如果希望用戶輸入HTML代碼,請(qǐng)?jiān)O(shè)置為false 。
注意:在ASP.NET 2.0中不驗(yàn)證ashx請(qǐng)求,但是ASP.NET 4.0默認(rèn)會(huì)驗(yàn)證,
如果希望在4.0中兼容2.0的行為,請(qǐng)?jiān)趙eb.config中配置<httpRuntime requestValidationMode="2.0"/>
其它指令
@ Import導(dǎo)入允許您指定要在代碼中引用的命名空間。
@ OutputCache設(shè)置頁面或者用戶控件的輸出緩存。
@ Register聲明控件的標(biāo)記前綴和控件程序集的位置。如果要向頁面添加用戶控件或自定義 ASP.NET 控件,可以使用此指令。
回到頂部 web.config的全局設(shè)置

前面我介紹了一些常用的Page指令,考慮到方便性,ASP.NET還允許我們?cè)趙eb.config中為一些常用的指令配置默認(rèn)值。下面我就一些常用的場(chǎng)景來說明這些全局配置的方便性。

1. 通常,我在創(chuàng)建一個(gè)網(wǎng)站項(xiàng)目時(shí),肯定會(huì)決定不使用ViewState和Session的。那么如果為每個(gè)頁面設(shè)置EnableViewState,EnableSessionState指令屬性,那就顯得太麻煩了,而且還容易遺漏。此時(shí),我們可以直接在web.config中為這些參數(shù)指定一個(gè)全局的默認(rèn)值:

<pagesenableViewState="false" enableSessionState="false"></pages>

補(bǔ)充說明一下:全局禁用Session的徹底方法是把Session對(duì)應(yīng)的HttpModule從httpModules列表中移除。

web.config允許我們?cè)O(shè)置Page默認(rèn)參數(shù)的具體配置節(jié)如下:

<pages buffer="[True|False]" enableEventValidation="[True|False]" enableSessionState="[True|False|ReadOnly]" enableViewState="[True|False]" enableViewStateMac="[True|False]" smartNavigation="[True|False]" autoEventWireup="[True|False]" pageBaseType="typename, assembly" userControlBaseType="typename" validateRequest="[True|False]" masterPageFile="file path" theme="string" styleSheetTheme="string" maxPageStateFieldLength="number" compilationMode="[Always|Auto|Never]" pageParserFilterType="string" viewStateEncryptionMode="[Always|Auto|Never]" maintainScrollPositionOnPostBack="[True|False]" asyncTimeout="number" > <controls>...</controls> <namespaces>...</namespaces> <tagMapping>...</tagMapping> </pages>

2. 為了代碼重用,設(shè)計(jì)用戶控件也是很常用的方法。
我們可以使用 @ Register指令 在頁面注冊(cè)需要使用的UserControl或者WebControl。然而,有些控件比較通用,許多頁面都會(huì)使用它,那么就不要再使用 @ Register指令了,可以在web.config中統(tǒng)一注冊(cè)。例如:

<pages> <controls> <addtagPrefix="fish" tagName="MainMenu" src="~/Controls/MainMenu.ascx" /> <addtagPrefix="fish" tagName="PageHeader" src="~/Controls/PageHeader.ascx" /> </controls> </pages>

有了這個(gè)定義后,我就可以在任何頁面中直接使用:

<fish:PageHeaderrunat="server"ID="PageHeader1" />

3. 對(duì)于喜歡使用頁面內(nèi)聯(lián)代碼的人來說,可能經(jīng)常需要使用自己定義的類型。如果這些類型定義在某個(gè)命名空間中,那么就需要在內(nèi)聯(lián)代碼中采用完整命名空間的寫法。雖然這樣做沒有什么問題,但就是麻煩,于是,我們可以在頁面中使用 @ Import指令 來導(dǎo)入我們需要使用的命名空間,但是這個(gè)指令每次只能導(dǎo)入一個(gè)命名空間,而且每個(gè)頁面還得重復(fù)導(dǎo)入,顯然不夠方便。

為了方便使用一些常用的命名空間,我們可以在web.config中統(tǒng)一指定,例如:

<pages> <namespaces> <addnamespace="MyMVC" /> <addnamespace="WebSiteCommonLib" /> <addnamespace="WebSiteModel" /> </namespaces> </pages>

這樣設(shè)置后,所有頁面就可以直接使用這些命名空間下的類型了。

不知道有些人想過:為什么在頁面中使用某些微軟提供的類型就不需要導(dǎo)入命名空間?
答案是:其實(shí)ASP.NET已經(jīng)將一些微軟認(rèn)為常用的命名空間在web.config中配置好了:

<pages> <namespaces> <addnamespace="System"/> <addnamespace="System.Collections"/> <addnamespace="System.Collections.Specialized"/> <addnamespace="System.Configuration"/> <addnamespace="System.Text"/> <addnamespace="System.Text.RegularExpressions"/> <addnamespace="System.Web"/> <addnamespace="System.Web.Caching"/> <addnamespace="System.Web.SessionState"/> <addnamespace="System.Web.Security"/> <addnamespace="System.Web.Profile"/> <addnamespace="System.Web.UI"/> <addnamespace="System.Web.UI.WebControls"/> <addnamespace="System.Web.UI.WebControls.WebParts"/> <addnamespace="System.Web.UI.HtmlControls"/> </namespaces> </pages>

4. 現(xiàn)在,有越來越多的人為了方便而使用擴(kuò)展方法。使用擴(kuò)展方法的好處是:可以讓我們不去關(guān)心這些擴(kuò)展方法定義在那個(gè)類中,只要在支持?jǐn)U展方法的對(duì)象上調(diào)用就可以了,就像下面的代碼這樣:

當(dāng)前用戶已登錄,登錄名:<%=Context.User.Identity.Name.HtmlEncode() %>

然而,在頁面中使用擴(kuò)展方法時(shí),也必須先導(dǎo)入擴(kuò)展方法的定義類的命名空間。
因此,為了方便,我們可以在web.config中為我們定義的擴(kuò)展方法導(dǎo)入相應(yīng)的命名空間:

<pages> <namespaces> <addnamespace="FishDemoCodeLib" /> </namespaces> </pages>回到頂部 換個(gè)方式使用 Page

在傳統(tǒng)的WEB開發(fā)模式下,我們通常會(huì)設(shè)計(jì)一些頁面(Page)響應(yīng)來自用戶瀏覽器的請(qǐng)求,在這種模式下,Page會(huì)將最后生成的整頁HTML代碼直接發(fā)送給用戶瀏覽器。然而,在某些時(shí)候,我們只需要生成一個(gè)HTML片段:
1. 在AJAX請(qǐng)求中,客戶端為了局部刷新,只要求服務(wù)端返回一個(gè)HTML片段。
2. BigPipe方式下,為了能分塊輸出,每次也只需要輸出一個(gè)HTML片段。

如果只是為了得到一段簡(jiǎn)單的HTML代碼,可能有些人會(huì)選擇采用代碼來拼接,但是如果那段HTML還有些復(fù)雜呢?顯然拼接方法肯定是不行的。

對(duì)于第一個(gè)問題,可能有人說:我可以創(chuàng)建一個(gè)頁面,只放部分代碼到頁面上。
的確,這種方法可以勉強(qiáng)解決第一個(gè)問題,但是,很有可能那部分代碼在整頁輸出時(shí)也會(huì)用到,怎么辦?
做成UserControl,然后放在一個(gè)單獨(dú)的頁面中!
其實(shí)這種做法很無奈,因?yàn)槟莻€(gè)容器頁面的意義不大(僅僅是個(gè)容器),最后搞得項(xiàng)目中一大堆頁面文件!
事實(shí)上,這種方法僅適用于使用簡(jiǎn)單服務(wù)端控件的場(chǎng)合,如果想使用一些高級(jí)的服務(wù)端控件,它根本就不行。

為了能實(shí)現(xiàn)前面說到的二個(gè)需求,我們就不能再按照傳統(tǒng)的方式來使用Page了。因?yàn)槲覀兿M艿玫剑ǚ祷兀┮欢蜨TML。

有二種方法可以讓我們繼續(xù)使用頁面模板代碼的方式生成HTML代碼:
1. Server.Execute()方法。
2. Page.RenderControl()方法。

下面這段代碼來源于 MyMVC框架,這個(gè)方法可以根據(jù)指定的用戶控件以及控件顯示所需的數(shù)據(jù)得到控件的輸出結(jié)果(一段HTML代碼)。

/// <summary> ///用指定的用戶控件以及視圖數(shù)據(jù)呈現(xiàn)結(jié)果,最后返回生成的HTML代碼。 ///用戶控件應(yīng)從MyUserControlView<T>繼承 /// </summary> /// <param name="ucVirtualPath">用戶控件的虛擬路徑</param> /// <param name="model">視圖數(shù)據(jù)</param> /// <returns>生成的HTML代碼</returns> public static stringRender(stringucVirtualPath, objectmodel) { if( string.IsNullOrEmpty(ucVirtualPath) ) throw newArgumentNullException("ucVirtualPath"); Pagepage =newPage(); Controlctl =page.LoadControl(ucVirtualPath); if( ctl ==null) throw newInvalidOperationException( string.Format("指定的用戶控件 {0} 沒有找到。", ucVirtualPath)); if( model !=null) { MyBaseUserControlmyctl =ctl asMyBaseUserControl; if( myctl !=null) myctl.SetModel(model); } // 將用戶控件放在Page容器中。 page.Controls.Add(ctl); StringWriteroutput =newStringWriter(); HtmlTextWriterwrite =newHtmlTextWriter(output, string.Empty); page.RenderControl(write); // 用下面的方法也可以的。 //HttpContext.Current.Server.Execute(page, output, false); returnoutput.ToString(); }

整段代碼分為以下幾個(gè)步驟(我已用空行分隔開了):
1. 檢查參數(shù)。
2. 創(chuàng)建頁面容器并加載用戶控件。
3. 設(shè)置頁面(視圖)所需的顯示數(shù)據(jù)。
4. 將用戶控件添加到Page的Controls集合中。
5. 調(diào)用RenderControl或者Execute讓Page輸出HTML代碼。
6. 返回結(jié)果。

這段代碼很簡(jiǎn)單,唯獨(dú)值得介紹的就是第5步,調(diào)用它們就可以得到控件輸出的HTML代碼。
RenderControl或者Execute的差別在于:
RenderControl不支持服務(wù)器控件,原因在于它利用了頁面的一種獨(dú)特編譯方式,我已在以前的博客中分析過了。
Execute可以支持服務(wù)器控件,因?yàn)樗鼤?huì)執(zhí)行一次完整的頁面生命周期。

注意:上面這段代碼就算使用Execute,也只能支持部分簡(jiǎn)單的服務(wù)器控件,因?yàn)橐恍?fù)雜的服務(wù)器控件需要在HtmlForm中才能運(yùn)行。因此,如果需要支持所有的服務(wù)器控件,那么還必須創(chuàng)建HtmlForm對(duì)象,并調(diào)整包含關(guān)系,還有就是還需要去掉產(chǎn)生的多余HTML代碼。

如果你需要生成整個(gè)頁面生成的HTML代碼,可以參考 MyMVC框架,那里有實(shí)現(xiàn)這個(gè)功能的完整代碼。

回到頂部 重新認(rèn)識(shí)Eval()方法

我想很多人都寫過類似下面的代碼:

<asp:RepeaterID="repeater1"runat="server"> <HeaderTemplate><ul></HeaderTemplate> <FooterTemplate></ul></FooterTemplate> <ItemTemplate> <li><%#Eval("OrderID")%>,<%#Eval("OrderDate")%>,<%#Eval("SumMoney")%> </li> </ItemTemplate> </asp:Repeater>

在這里我要說的是 Eval() 的調(diào)用,還不是Repeater控件。

Eval()不僅僅可以讀取一個(gè)綁定數(shù)據(jù)項(xiàng)的屬性,還可以去讀取DataTable中的一個(gè)數(shù)據(jù)列。而且還能完成更復(fù)雜的綁定計(jì)算:

<li><%#Eval("OrderID")%>,<%#Eval("OrderDate")%>,<%#Eval("SumMoney")%> ,訂單中的第一個(gè)商品:<%#Eval("Detail[0].ProductName") %> </li>

當(dāng)然了,對(duì)于頁面上的數(shù)據(jù)綁定,用Eval()的確不是性能最好的方法,建議還是使用強(qiáng)類型轉(zhuǎn)換的方法。

有時(shí)候,尤其是在寫反射應(yīng)用時(shí),時(shí)常會(huì)有從字符串解析并實(shí)現(xiàn)求值計(jì)算的需求。那么,前面這個(gè)示例中,Eval()的功能是不是值得挖掘呢?我認(rèn)為答案是肯定的。

通過分析ASP.NET的綁定代碼,我發(fā)現(xiàn)Eval在內(nèi)部會(huì)調(diào)用DataBinder.Eval這個(gè)靜態(tài)方法,這個(gè)方法的簽名如下:

// 在運(yùn)行時(shí)計(jì)算數(shù)據(jù)綁定表達(dá)式。 // // 參數(shù): // container: // 表達(dá)式根據(jù)其進(jìn)行計(jì)算的對(duì)象引用。此標(biāo)識(shí)符必須是以頁的指定語言表示的有效對(duì)象標(biāo)識(shí)符。 // // expression: // 從 container 到要放置在綁定控件屬性中的公共屬性值的導(dǎo)航路徑。 // 此路徑必須是以點(diǎn)分隔的屬性或字段名稱字符串,如 C# 中的 "Tables[0].DefaultView.[0].Price" // 或 Visual Basic 中的 "Tables(0).DefaultView.(0).Price"。 // // 返回結(jié)果: // System.Object,它是數(shù)據(jù)綁定表達(dá)式的計(jì)算結(jié)果。 public static objectEval(objectcontainer, stringexpression);

通過這個(gè)簽名的注釋,我們可以很容易地看出它的用法。
下面我來舉個(gè)例子把它應(yīng)用在非綁定的應(yīng)用中:

我有一個(gè)類:

public classTestEvalClass { publicList<Order>Orders { get; set; } // Order以及OrderDetail的定義就省略了,我想大家能想像得出來。 }

那么下面的代碼是可以運(yùn)行的:

static voidMain() { TestEvalClasstestObject =GetTestEvalClassInstance(); stringproductName =(string)System.Web.UI.DataBinder.Eval(testObject, "Orders[0].Detail[0].ProductName"); Console.WriteLine(productName); }

對(duì)于這個(gè)示例,我想輸出什么結(jié)果,并不重要。
我只想說:如果讓你去解析那個(gè)表達(dá)式,會(huì)不會(huì)比較麻煩,現(xiàn)在有現(xiàn)成的,用起來是不是很方便?

回到頂部 不用基類也能擴(kuò)展

在一個(gè)ASP.NET網(wǎng)站中,如果想為所有的頁面添加某個(gè)功能,我們通常會(huì)想到使用基類的方式去實(shí)現(xiàn)。這的確是一種很有效的方法,但不并唯一的方法,還有一種方法也能容易實(shí)現(xiàn)這個(gè)需求,那就是使用PageAdapter的方式。

在我寫博客的過程中,我寫了很多示例頁面,頁面中包含一些提交按鈕是少不了的事情,然而,為了能讓示例代碼看起來比較原始(簡(jiǎn)單),我盡量不使用服務(wù)器控件,因此就要面臨提交按鈕的事件處理問題。在博客【細(xì)說 ASP.NET Cache 及其高級(jí)用法】的示例代碼中,我開始采用PageAdapter這種方法,它可以讓代碼很簡(jiǎn)單,而且以后也方便以后重用(只需要復(fù)制幾個(gè)文件即可)。

或許有些人認(rèn)為:擴(kuò)展所有頁面的功能,還是使用基類比較好。
對(duì)于這個(gè)觀點(diǎn),我完全不反對(duì)。
但是,PageAdapter的好處在于它的可插拔性(類似HttpModule的優(yōu)點(diǎn))。
不過,我當(dāng)時(shí)設(shè)計(jì)這種擴(kuò)展方式只是想再換個(gè)方法嘗試一下而已。

其實(shí)微軟設(shè)計(jì)PageAdapter的本意是為了處理各種瀏覽器的兼容問題,但是我把這個(gè)功能用到擴(kuò)展Page的功能上去了。 HttpModule可以進(jìn)入到ASP.NET請(qǐng)求管線的任何階段,但它就是進(jìn)入不了頁面的生命周期中,有了這個(gè)方法,我們就可以采用HttpModule這種【外掛】式的方法進(jìn)入到頁面生命周期中,我認(rèn)為是很有意義的。

方法多了,我想不是件壞事。每種方法都有適合它們的應(yīng)用場(chǎng)合,了解更多的方法,以后就能做出更優(yōu)秀的設(shè)計(jì)。

這次想到這個(gè)話題是因?yàn)榍懊娴牟┛汀炯?xì)說ASP.NET Forms 身份認(rèn)證】中的示例代碼。有些人看到那些代碼,發(fā)現(xiàn)代碼的運(yùn)行方式比較特別,所以,今天我就打算著重介紹這種方法。

我們?cè)賮砘仡櫼幌乱郧安┛椭械氖纠a,首先從頁面代碼開始:

<fieldset><legend>普通登錄</legend><formaction="<%= Request.RawUrl %>"method="post"> 登錄名:<inputtype="text"name="loginName"style="width: 200px"value="Fish" /> <inputtype="submit"name="NormalLogin"value="登錄" /> </form></fieldset> <fieldset><legend>包含【用戶信息】的自定義登錄</legend> <formaction="<%= Request.RawUrl %>"method="post"> <tableborder="0"> <tr><td>登錄名:</td> <td><inputtype="text"name="loginName"style="width: 200px"value="Fish" /></td></tr> <tr><td>UserId:</td> <td><inputtype="text"name="UserId"style="width: 200px"value="78" /></td></tr> <tr><td>GroupId:</td> <td><inputtype="text"name="GroupId"style="width: 200px" /> 1表示管理員用戶 </td></tr> <tr><td>用戶全名:</td> <td><inputtype="text"name="UserName"style="width: 200px"value="Fish Li" /></td></tr> </table> <inputtype="submit"name="CustomizeLogin"value="登錄" /> </form></fieldset>

在這段頁面代碼中,我定義了二個(gè)表單,它們包含各自的提交按鈕(其實(shí)這也只是部分代碼)。
再來看后臺(tái)處理代碼是如何響應(yīng)提交請(qǐng)求的:

public partial class_Default: System.Web.UI.Page { [SubmitMethod(AutoRedirect =true)] public voidNormalLogin() { // 省略登錄處理代碼。 // 如果需要知道這段代碼可以瀏覽下面的網(wǎng)址: // http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.html } [SubmitMethod(AutoRedirect =true)] public voidCustomizeLogin() { // 省略登錄處理代碼。 // 如果需要知道這段代碼可以瀏覽下面的網(wǎng)址: // http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.html }

注意觀察,這二個(gè)C#方法的名稱與頁面二個(gè)submit按鈕的name屬性相同,因此可以猜測(cè)到這二個(gè)C#方法可以處理那二個(gè)submit按鈕的提交請(qǐng)求。那么這二段代碼是如何運(yùn)行起來的呢?有些人或許看到了[SubmitMethod]的使用,認(rèn)為與它們有關(guān)。其實(shí)這種說法并不正確,我也可以完全不使用它們。請(qǐng)記住:Attribute永遠(yuǎn)只是一個(gè)標(biāo)記,它不可能讓代碼自動(dòng)運(yùn)行起來。

前面的代碼能運(yùn)行起來,與App_Browsers目錄下的Page.browser文件有關(guān),此文件的代碼如下:

<browsers> <browserrefID="Default"> <controlAdapters> <adaptercontrolType="System.Web.UI.Page" adapterType="FishDemoCodeLib.MyPageAdapter, FishDemoCodeLib" /> </controlAdapters> </browser> </browsers>

這里定義了一個(gè)MyPageAdapter,它用于Page控件的請(qǐng)求過程。 refID="Default" 表示是對(duì)ASP.NET定義的Default.browser文件補(bǔ)充一些配置,它將能匹配來自所有瀏覽器的請(qǐng)求。
我再來看一下MyPageAdapter的代碼:

[AttributeUsage(AttributeTargets.Method, AllowMultiple =false)] public classSubmitMethodAttribute: Attribute { public boolAutoRedirect { get; set; } } internal sealed classMethodInvokeInfo { publicMethodInfoMethodInfo; publicSubmitMethodAttributeMethodAttribute; } public classMyPageAdapter: System.Web.UI.Adapters.PageAdapter { private static readonlyHashtables_table =Hashtable.Synchronized(newHashtable()); private staticMethodInvokeInfo[] GetMethodInfo(Typetype) { MethodInvokeInfo[] array =s_table[type.AssemblyQualifiedName] asMethodInvokeInfo[]; if( array ==null) { array =(fromm intype.GetMethods(BindingFlags.Instance |BindingFlags.Public) leta =m.GetCustomAttributes( typeof(SubmitMethodAttribute), false) asSubmitMethodAttribute[] wherea.Length >0 select newMethodInvokeInfo{ MethodInfo =m, MethodAttribute =a[0] }).ToArray(); s_table[type.AssemblyQualifiedName] =array; } returnarray; } protected override voidOnLoad(EventArgse) { base.OnLoad(e); if( Page.Request.Form.AllKeys.Length ==0) return; // 沒有提交表單 MethodInvokeInfo[] array =GetMethodInfo(Page.GetType().BaseType); if( array.Length ==0) return; foreach( MethodInvokeInfom inarray ) { if( string.IsNullOrEmpty(Page.Request.Form[m.MethodInfo.Name]) ==false) { m.MethodInfo.Invoke(Page, null); if( m.MethodAttribute.AutoRedirect &&Page.Response.IsRequestBeingRedirected ==false) Page.Response.Redirect(Page.Request.RawUrl); return; } } } }

這段代碼并不長(zhǎng),核心代碼更是比較少。
代碼中,最重要的一塊是MyPageAdapter的實(shí)現(xiàn),它繼承了System.Web.UI.Adapters.PageAdapter,并重寫了OnLoad方法(相當(dāng)是在重寫Page的OnLoad方法),也正是由于這個(gè)重寫,代碼才有機(jī)會(huì)在頁面的生命周期中被執(zhí)行,這一點(diǎn)是HttpModule做不到的。
在OnLoad方法中做了以下事情:
1. 檢查是不是發(fā)生了表單提交的操作。
2. 獲取當(dāng)前頁面類型的所有[SubmitMethod]修飾過的方法。
3. 檢查提交的表單數(shù)據(jù)中,是否存在與name對(duì)應(yīng)的C#方法名。
4. 如果找到一個(gè)匹配的方法名,則調(diào)用。
5. 如果在[SubmitMethod]中設(shè)置了AutoRedirect=true,則引發(fā)重定向。

注意:如果不調(diào)用base.OnLoad(e); 那么頁面的Load事件根本不會(huì)發(fā)生。
也就是說:PageAdapter.OnLoad的調(diào)用時(shí)間要早于Page.Onload方法。

由于這段代碼僅供我寫示例代碼時(shí)使用,因此并沒有檢查要調(diào)用的方法的參數(shù)是否滿足條件,也沒有優(yōu)化刻意去優(yōu)化它的性能。在我的設(shè)計(jì)中,被調(diào)用的方法應(yīng)該是無參的,因此是容易判斷的,而且可以使用一個(gè)固定簽名的委托去優(yōu)化它的,這些細(xì)節(jié)留著以后再去完善它吧!

不登高山,怎知天高;不臨深溪,焉知地厚!站在堅(jiān)實(shí)的土地上,做著生命中最真實(shí)的事情;像一棵挺拔的大樹,認(rèn)可自己的命運(yùn)并敢于迎接屬于這一方天空的風(fēng)風(fēng)雨雨。

新聞名稱:【轉(zhuǎn)載】ASP.NETPage那點(diǎn)事-創(chuàng)新互聯(lián)
標(biāo)題鏈接:http://www.bm7419.com/article44/ceeohe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、網(wǎng)站改版、云服務(wù)器全網(wǎng)營(yíng)銷推廣、動(dòng)態(tài)網(wǎng)站、網(wǎng)站維護(hù)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作