ASP.Net Web Page深入探討一
發表時間:2024-06-02 來源:明輝站整理相關軟件相關文章人氣:
[摘要]一、服務器腳本基礎介紹 首先,我們先復習一下Web服務器頁面的基本執行方式:1、 客戶端通過在瀏覽器的地址欄敲入地址來發送請求到服務器端2、 服務器接收到請求之后,發給相應的服務器端頁面(也就是腳本)來執行,腳本產生客戶端的響應,發送回客戶端3、 客戶端瀏覽器接收到服務器傳回的響應,對Html進行...
一、服務器腳本基礎介紹
首先,我們先復習一下Web服務器頁面的基本執行方式:
1、 客戶端通過在瀏覽器的地址欄敲入地址來發送請求到服務器端
2、 服務器接收到請求之后,發給相應的服務器端頁面(也就是腳本)來執行,腳本產生客戶端的響應,發送回客戶端
3、 客戶端瀏覽器接收到服務器傳回的響應,對Html進行解析,將圖形化的網頁呈現在用戶面前
對于服務器和客戶端的交互,通常通過下面幾種主要方式:
1、 Form:這是最主要的方式,標準化的控件來獲取用戶的輸入,Form的提交將數據發送給服務器端處理
2、 QueryString:通過在Url后面帶參數達到將參數傳送給服務器,這種方式其實跟Get方式的Form是一樣的
3、 Cookies:這是一種比較特殊的方式,通常用于用戶身份的確認
二、ASP.Net簡介
傳統的服務器腳本語言,如ASP、JSP等,編寫服務器腳本的方式大同小異,都是在Html中嵌入解釋或編譯執行的代碼,由服務器平臺執行這些代碼來生成Html;對于這類似的腳本,頁面的生存周期實際上很簡單,就是從開頭至末尾,執行完所有的代碼,當然用Java編寫的Servlet可以編寫更復雜的代碼,但是從結構上看,和JSP沒什么區別。
ASP.Net的出現,打破了這種傳統;ASP.Net采用了CodeBehind技術和服務器端控件,加入了服務器端的事件的概念,改變了腳本語言編寫的模式,更加貼近Window編程,使Web編程更加簡單、直觀;但是我們要看到,ASP.Net本身并沒有改變Web編程的基本模式,只是封裝了一些細節、提供了一些易用的功能,使代碼更容易編寫和維護;從某種程度上來說,將服務器端執行的方式復雜化了,這就是我們今天要討論的主體:ASP.Net Web Page的生存周期。
三、ASP.Net請求處理模式
我們說,ASP.Net的Web Page并沒有脫離Web編程的模式,所以它仍然是以 請求->接收請求->處理請求->發送響應 這樣的模式在工作,每一次與客戶端的交互都會引發一次新的請求,所以一個Web Page的生命周期是以一次請求為基礎的。
當IIS收到客戶端的請求的時候,會將請求交給aspnet_wp這個進程來處理,這個進程會查看請求的應用程序域是否存在,如果不存在則會創建一個,然后會創建一個Http運行時(HttpRuntime)來處理請求,這個運行時“為當前應用程序提供一組 ASP.NET 運行時服務”(摘自MSDN)。
HttpRuntime在處理請求的時候,會維護一系列的應用程序實例,也就是應用程序的Global類(global.asax)的實例,這些實例在沒有請求的時候,會存放在一個應用程序池中(實際上應用程序池由另一個類來維護,HttpRuntime只是簡單的調用),每接收到一個請求,HttpRuntime都會獲取一個閑置的實例來處理請求,這個實例在請求結束前不會處理其他的請求,處理完畢之后,它又會回到池中,“一個實例在其生存期內被用于處理多個請求,但它一次只能處理一個請求。”(摘自MSDN)
當應用程序實例處理請求的時候,它會創建請求頁面類的實例,執行它的ProcessRequest方法來處理請求,這個方法也就是Web Page生命周期的開始。
四、Aspx頁面與CodeBehind
在深入了解頁面的生命周期之前,我們先來探討一些Aspx與CodeBehind之間的關系。
<%@ Page language="c#" Codebehind="WebForm.aspx.cs" Inherits="MyNamespace.WebForm" %>
相信使用過CodeBehind技術的朋友,對ASPX頂部的這句話應該是非常熟悉了,我們來一項一項的分析它:
Page language="c#" 這個就不用多說了吧
Codebehind="WebForm.aspx.cs" 這一句表示綁定的代碼文件
Inherits="MyNamespace.WebForm" 這句非常重要,它表示頁面繼承的類名稱,也就是CodeBehind的代碼文件中的類,這個類必須從System.Web.WebControls.Page派生
從上面我們可以分析出,實際上CodeBehind中的類就是頁面(ASPX)的基類,到這里,可能有些朋友要問了,在編寫ASPX的時候,完全是按照ASP的方式,在Html中嵌入代碼或者嵌入服務器控件,沒有看到所謂“類”的影子啊?
這個問題實際上并不復雜,各位使用ASP.Net編程的朋友可以到你們的系統盤:\WINDOWS\Microsoft.NET\Framework\<版本號>\Temporary ASP.NET Files這個目錄下,這個下面就放了所有本機上存在的ASP.Net應用程序的臨時文件,子目錄的名稱就是應用程序的名稱,然后再下去兩層(為了保證唯一,ASP.Net自動產生了兩層子目錄,并且子目錄名稱是隨機的),然后我們會發現有很多類似:“yfy1gjhc.dll”、“xeunj5u3.dll”這樣的鏈接庫以及“komee-bp.0.cs”、“9falckav.0.cs”這樣的源文件,實際上這就是ASPX被ASP.Net動態編譯后的結果,打開這些源文件我們可以發現:
public class WebForm_aspx : MyNamespace.WebForm, System.Web.SessionState.IRequiresSessionState
這就印證了我們前面的說法,ASPX是代碼綁定類的子類,它的名稱是ASPX文件名加上“_aspx”后綴,通過研究這些代碼我們可以發現,實際上所有aspx中定義的服務器控件都是在這些代碼中生成的,然后動態產生這些代碼的時候,把原來在ASPX中嵌入的代碼寫在了相應的位置。
當某個頁面第一次被訪問的時候,Http運行時就會使用一個代碼生成器去解析ASPX文件并生成源代碼并編譯,然后以后的訪問就直接調用編譯后的dll,這也是為什么ASPX第一次訪問的時候非常慢的原因。
解釋了這個問題,我們再來看另一個問題。我們在使用代碼綁定的時候,在設計頁面拖一個控件,然后切換到代碼視圖,就可以直接在Page_Load中使用這個控件了,既然控件是在子類中產生的,那為什么在父類中可以直接使用呢?
實際上我們可以發現,每當用VS.Net拖一個控件到頁面上,代碼綁定文件中總是會類似這樣的添加一個聲明:
protected System.Web.WebControls.Button Button1;
我們可以發現這個字段被聲明成protected,而且名字與ASPX中控件的ID一致,仔細想一想,這個問題就迎刃而解了。我們前面提到ASPX的源代碼是被生成器動態生成和編譯的,生成器會產生動態生成每一個服務器控件的代碼,在生成的時候,它會檢查父類有沒有聲明這個控件,如果聲明了,它會添加類似下面的一句代碼:
this.DataGrid1 = __ctrl;
這個__ctrl就是生成該控件的變量,這時候它就把控件的引用賦給了父類中相應的變量,這也是為什么父類中的聲明必須為protected(實際上也可以為public),因為要保證子類能夠調用。
然后在執行Page_Load的時候,因為這時候父類的聲明已經被子類中的初始化代碼賦了值,所以我們就可以使用這個字段來訪問對應的控件,了解了這些,我們就不會犯在代碼綁定文件中的構造器里使用控件,造成空引用的異常的錯誤了,因為構造器是最先執行的,這時候子類的初始化還沒有開始,所以父類中的字段是空值,至于子類是什么時候初始化我們放到后面討論。