摘 要 在有線及無線網絡日益普及的今天,基于B/S模式的開發越來越成為應用開發的主流,其中微軟公司的asp.net以強大、易適應、簡單、易學而著稱。ASP.NET2.0(ASP:ActiveServerPage)是微軟的.NET框架更新版本.NET2.0中的一部分,是一種重要的,流行的動態WEB開發技術。了解和理解ASP.NET2.0頁面的初始化過程對于幫助開發人員對.net技術的整體認識和提高開發效率有很大的幫助。
關鍵詞 B/S開發 ASP.NET 生命周期 IIS aspnet_isapi
一、aspx頁面的處理準備
任何B/S結構的軟件系統必須部署和發布在一定的應用程序容器當中,IIS即Internet Information Services(互聯網信息服務)就是由微軟公司開發的可安裝于windows系列操作系統上的、支持asp.net發布應用程序站點的web服務器。當然IIS的功能強大而豐富,還包括FTP服務器、NNTP服務器和SMTP服務器,分別用于文件傳輸、新聞服務和郵件發送等方面的功能,它使得在網絡(包括互聯網和局域網)上發布信息成了一件很容易的事。
IIS作為互聯網信息發布組件其最大作用是作為容器和分發機制對來自互聯網的請求信息進行識別和轉發,而對于來自網絡的aspx頁面(aspx是ASP.NET2.0的動態頁面文件的擴展名)IIS就是在識別后交付給ISAPI(Internet Server ApplicationPrograme Interface,互聯網服務器應用程序接口)應用程序處理,而ISAPI作為一個應用接口則根據擴展名進一步識別后將真正的處理和解析工作交給aspnet_isapi.dll程序來處理,aspnet_isapi.dll程序處理完請求后將響應結果通過ISAPI和IIS發送給客戶端(即具體的請求用戶),完成信息的交互。
這一過程實際上就是為用戶發送的aspx頁面請求創建處理請求的環境,這個環境順序的調用iis、ISAPI和aspnet_isapi.dll程序,并創建應用程序對象、request、response和context對象去處理請求。這個準備過程具體如下:
1.用戶發送一個請求到IIS。IIS首先檢查哪個ISAPI擴展可以處理這個請求,這取決于請求的文件擴展名。舉例來說,如果請求頁面是‘.ASPX’,它將被傳送到‘aspnet_isapi.dll’來處理。
2.如果這是www站點的第一個請求,Application Manager類將創建一個應用程序域,www站點運行于其中。我們都知道在同一個IIS上,兩個web應用程序的應用程序域是獨立的(隔離的)。因此,一個應用程序域中問題不會影響到其他應用程序域。
3.新建的應用程序域創建宿主環境,如HttpRuntime對象。一旦宿主環境被創建,必要的ASP.NET核心對象如HttpContext、HttpRequest和HttpRespone對象也被創建。
4.一旦所有的ASP.NET核心對象被創建,HttpApplication對象將被創建去處理請求。如果系統中有global.asax文件,global.asax文件對象將被創建。global.asax文件繼承自HttpApplication類;第一次ASP.NET頁面連接到應用程序,一個HttpApplication新實例將被創建。為了最大化性能,HttpApplication實例可能被多個請求重用。
5.HttpApplication對象分配給核心ASP.NET對象來處理頁面。
6.HttpApplication通過HttpContext、HttpRequest和HttpRespone事件開始處理請求。它觸發MHPM事件處理請求。
二、aspx頁面的處理過程
待aspx頁面請求的處理環境準備就緒(以創建了HttpApplication為準)之后,便進入了實際的aspx頁面請求處理過程:
1.客戶端請求處理開始。ASP.NET引擎開始和創建HttpModule發出事件(你可以注入定制邏輯)之前,有6個重要事件你可以使用:BeginRequest、AuthenticateRequest、AuthorizeRequest、ResolveRequestCache、AcquireRequestState和PreRequestHandlerExecute。
2.創建頁面句柄過程,即執行HttpHandler:一旦上面6個事件觸發,ASP.NET引擎將調用ProcessRequest事件完成HttpHandler的執行。
3.創建頁面對象過程:一旦HttpHandler邏輯執行,ASP.NET頁面對象被創建。ASP.NET頁面對象被創建,許多事件被觸發,主要包括Init、Load、Validate、Event、Render、Unload等。
4.頁面對象執行完畢后的清理工作及請求處理結果的反饋工作:一旦頁面對象執行了且從內存中卸載,HttpModule提供發送頁面執行事件,它們可用于注入自定義post-處理邏輯。有4個重要的post-處理事件,PostRequestHandlerExecute、PostRequestState、UpdateRequestCache、EndRequest。
三、主要事件及功用
下面介紹asp.net從請求到完成應答全生命周期過程中所涉及的主要事件,這些事件覆蓋了頁面請求、頁面初始化、恢復與加載事件處理、保存與現實等各個環節。
1.初始化。(1)構造函數,當頁面被提交請求的第一個方法永遠是構造函數。可以在構造函數里面初始一些自定義屬性或對象,不過這時候因為頁面還沒有被完全初始化所以多少會有些限制。特別需要使用HttpContext對象。當前可以使用的對象包括QueryString,Form以及Cookies集合,還有Cache對象。注意:在構造函數里是不允許使用Session的。
(2)下一個將執行的方法是AddParsedSubObject方法。這個方法將添加所有獨立的控件并把頁面組成一個控件集合樹,這個方法經常被一些高級的頁面模板解決方案(PageTemplateSolutions)重寫以便添加頁面內容到頁面模板(PageTemplate)中一些特殊的控件中。這個方法遞歸應用到所有的頁面控件及相應的的每個子控件,所有的控件都是在這個方法中開始最早的初始化。
(3)頁面類中下一個將執行的方法是DeterminePostBackMode。這個方法允許修改IsPostBack的值及相關的事件。如果需要從數據庫中加載ViewState,這個方法將特別有用,因為ViewState只有在IsPostBack為真的情況下才會進行恢復。返回空將會導致強制執行非回傳,返回Request.Form則強制執行一個回傳。除非在特殊情況下,否則并不建議去操作這個,因為會影響其他的事件。
(3)下一個將要執行的方法是OnInit方法。一般這是第一個真正被使用的方法。這個方法觸發時,所有頁面定義中的控件執行初始化,這意味著所有在頁面中定義的值應用到相應的控件上。不過,ViewState和傳回的值還不會應用到控件上,因此,任何被代碼或用戶改變的值還沒有被恢復到控件上。這個方法通常是最好的創建、重創建動態控件的好地方。
2 恢復及加載。(1)下一個方法,LoadPageStateFromPersistenceMedium只會在頁面被回傳的時候才會被執行。如果因為使用Session或自定義存儲方式,您修改了后面將要提到的影響ViewState保存方式的方法SavePageStateToPersistenceMedium,則這個方法需要被重寫。默認的實現中ViewState是一種Base64格式編碼,并且被保存在頁面的隱藏域中,您可以使用這篇文章中提及的方法修改ViewState并按以上兩種方式保存。注意:這個方法并沒有真正加載ViewState到頁面或頁面控件中。
(2)當得到ViewState后,下一個方法LoadViewSate。它將以遞歸的方式恢復ViewState到頁面及各個頁面控件或子控件中。這個方法執行后,每個控件都將恢復到上一次的狀態,但是用戶提交的數據還沒有應用到控件上,因為他們不是ViewState的一部分。這個方法主要用于恢復您在其他事件中動態生成的控件的值,他們的值是您手動保存在ViewSate中,并且現在已經失效。
(3)下一個方法是ProcessPostData。這個方法也同樣是回傳的時候才會被執行,并且不允許被重寫,這個是頁面基類的私有方法。這個方法通過匹配控件的名稱恢復相應的用戶提交的控件的值,到這一步意味著整個頁面都已經被完全恢復了。唯一要記住的是所有動態控件的創建必須在這個方法之前。這個方法也是記錄后面的改變事件的方法。
(4)下一個方法是OnLoad方法。通常這是用得最多的方法,因為這個方法是頁面生存期第一個恢復了所有值的地方。大多數代碼根據判斷IsPostBack來決定是否重新設置控件狀態。也可以在這個方法中調用Validate并且檢查IsValid的值,也可以在這個方法中創建動態控件,并且該控件的所有的方法都會被執行以追上當前頁面的狀態,包括ViewSate,不過不包括回傳的值。
3.事件處理。(1)下一個方法還是ProcessPostData。實際上就是前一個方法的另一次調用,它仍然是只在回傳的時候執行并且由于是私有方法不可以被重寫。如果您是第一次看頁面的運行軌跡也許會覺得這個方法有些多余。但實際上這個方法是必要的,因為在OnLoad中創建的動態控件也需要他們回傳的值。任何在這以后創建的控件將可以得到他們的ViewState,但是不能再得到他們的回傳的值,并且不會觸發任何值改變事件(ChangeEvent)。
(2)下一個方法,RaiseChangedEvents。也是只在回傳頁面中執行,并且也因為是基類的私有方法所有不能被繼承。在整個頁面生存期中,是根據之前的ProcessPostData記錄的控件的值和提交的值是否不同來觸發值改變事件。您也許需要調用Validate或者檢查IsValid的值。這里并沒有特別的說明多個值改變事件的執行先后順序。
(3)下一個方法,RaisePostBackEvent。同樣是因為是基類的私有方法不能被繼承,同樣也是只在回傳頁面中執行。除非使用了AutoPostBack,不然這是實際提交表單事件執行的地方,特別是按鈕或者其實使用javascript提交表單等。如果還沒有被手動調用過并且使用了驗證控件,那么Validate會被調用。注意IE中有個BUG有時會允許提交但卻不觸發任何事件。
(4)下一個方法是OnPreRender。一般這是在客戶端展現頁面之前改變頁面及其控件的最后一次機會。您也可以在這個方法里面創建動態控件,并且所有的方法都會被執行以追上當前頁面的狀態,包括ViewSate,但是私有方法將不會被執行,這意味著不會有回傳的值并且不會有事件觸發。由于IE中的BUG,這是一個沒有事件趕上PostBack的好地方。
4.保存及顯示。(1)下一個方法是SaveViewState。不論是否是回傳頁面,均會遞歸的執行以保存頁面及其所有控件的ViewState。ViewState基本上保存所有與定義在aspx中的原始值不同的值,不管是被代碼還是用戶所改變。注意控件值是根據他們在頁面的控件樹中的位置來保存的,所以,如果動態控件后來加到了錯誤的位置將會導致混亂。
(2)下一個方法是SavePageStateToPersistenceMedium真正的保存頁面的ViewSate。這個方法隨同LoadPageStateFromPersistenceMediumg一起被重寫以便保存ViewState到Session或其他自定義數據,而不是用隱藏域。這對于低帶寬的用戶來說是很有幫助的,并且對于移動設備來說,Session是默認設置。
(3)下一個方法是Render方法。該方法遞歸的創建并發送相應控件的html給瀏覽器。這個方法被一些頁面模板方案重寫以添加一些通用的頁面頭與腳而不使用服務器控件,他們總是有一些額外的東西。注意這兒的修改只能使用純HTML,因為控件在這兒已經被生成了。您可以用StringBuilder,StringWriter,HtmlTextWriter捕獲相應的HTML輸出。
(4)最后的方法是OnUnload。這個方法會調用相應的Dispose方法。這個方法提供機會以便清空該頁面中使用的非托管資源,如關閉打開的文件句柄,以前打開的數據庫連接等。注意這個方法是在頁面已經發送到客戶端以后執行的,所以它只有影響服務器對象,并且它不會顯示在頁面的顯示軌跡中。這就是頁面的生存期,對于每一次請求都是這么運行的。
四、結束語
ASP.NET頁面生命周期因其事件機制而顯得格外新穎獨特。Web頁面由控件組成,這些控件既可以產生豐富的基于HTML的用戶界面,又可以通過事件與用戶交互。以前,在Web應用程序的上下文中,設置事件模型是件有挑戰性的工作。可我們驚奇地看到,客戶端生成的事件可以由服務器端的代碼來解決,而且只進行一些相應的修改后,此過程仍可以輸出相同的HTML頁面。
了解頁面生命周期的各個階段,對開發人員從整體上理解和把握asp.net技術具有非常重要的意義,特別是了解頁面生命周期中的主要事件及其作用對于開發人員更加靈活地實現用戶的各類需求具有特殊的作用。