最近聽了微軟講師邵志東的講座“深入解析ASP.NET架構”的視頻講座,我覺得對ASP.NET架構有一定的認識,現在對講座做一點總結,以及發表我對ASP.NET架構相關知識的自身理解,如有不妥之處,希望各位同仁不吝指出!
一、ASP.NET工作原理:
首先通過一個圖來了解一下ASP.NET工作原理,
圖一 ASP.NET工作原理
通過該圖的順序我們可以看到ASP.NET是如何工作的,可以看到客戶端和服務器端是如何交互的。看到客戶端是如何請求服務器,服務器接受到客戶端請求後,是如何處理請求的,並將處理結果返回給客戶端的。
需要指出的是圖中的aspnet_isapi.dll是用來處理.aspx文件的,其實IIS服務器是隻能識別.html文件的,當瀏覽器對服務器進行http請求時,IIS服務器識別到.aspx文件時,IIS服務器將其交給aspnet_isapi.dll來處理,aspnet_isapi.dll將.aspx文件發送給.NET FrameWork中CLR(公共語言運行時)進行編譯,編譯後將HTML流返回給瀏覽器。
這在IIS服務器上是可以來配置的,配置中默認.aspx文件是由aspnet_isapi.dll來處理的,其實也可以去添加某種特定類型的文件由特定的可執行文件來處理,如:可以添加一個.apx文件來由aspnet_isapi.dll來處理。
具體配置如下圖:
圖二 IIS配置
趁此說一下IIS服務的一些知識:
IIS本事是不支持動態頁面的,也就是說它僅僅支持靜態html頁面的內容,對於如.aspx,.php,.cs等,IIS並不會處理這些標記,它就會把它當作文本,絲毫不做處理髮送到客戶端。爲了解決這個問題。IIS有一種機制,叫做ISAPI的篩選器,它是一個標準組件(COM組件。) ASP.NET服務在註冊到IIS的時候,會把每個擴展可以處理的文件擴展名註冊到IIS裏面(如:*.aspx,*.php等),擴展啓動後,就根據定義好的方式來處理IIS所不能處理的文件,然後把控制權跳轉到專門處理代碼的進程中(如*.aspx由aspnet_isapi.dll來處理)。讓這個進程開始處理代碼,生成標準的HTML代碼,生成後把這些代碼加入到原有的HTML代碼中,最後把完整的HTML返回到IIS,IIS再把內容發送給客戶端。
通過下圖可以看到更具體的ASP.NET頁面的執行過程:
二、ASP.NET請求的處理過程:
上面我們已經瞭解了ASP.NET的工作原理,知道了客戶端是如何和服務器端進行交互的。現在我們可以來看看服務器端到底是怎樣來處理客戶端的請求。
Web服務器提供了很多處理請求的功能,但是爲了滿足開發者的需求,開發者需要擴充或擴展Web服務器的功能,就是向Web服務器插入某些組件來增強Web服務器的功能,微軟公司提供了ISAPI(Internet服務器API)。就比如說aspnet_isapi.dll可以來處理.aspx等文件,我們也可以開發組件(如xxx.dll)來處理某種類型的文件或者去做一些其他增強服務器能力的事情。
簡單的說如何去增強Web服務器的能力呢,我們就可以去開發一些組件,開發組件需要一種技術叫ISAPI。
(ISAPI是一種重要的技術,它允許我們增強與ISAPI兼容的Web服務器的能力,其中IIS就是一種與ISAPI兼容的Web服務器。)
增強Web服務器的能力的組件的類型主要有兩種:
1、ISAPI過濾器:客戶端每次向服務器發出請求的時候,請求要經過過濾器。客戶端不需要在請求中指定過濾器,只需要簡單地把請求發送給Web服務器,接着Web服務器把請求傳遞給相關的過濾器。接下來過濾器可能修改請求,執行某些登錄操作等。也就是說,客戶端每次請求服務器時,必須要經過“過濾”,如需要檢查用戶名和密碼,符合條件時才讓通過請求,“檢查用戶名和密碼”的過程就是ISAPI過濾器所要做的操作。過濾器可以有很多(web.config中指定,後面將講到),並且請求都必須要經過所有的過濾器進行“過濾”。
2、ISAPI擴展:ISAPI擴展是使用Win32動態鏈接庫(xxx.dll)來實現的,也可以把ISAPI擴展看做一個普通的應用程序。它的處理目標是http請求。也就是說Web服務器可以來處理http請求,但是你可以去擴展服務器,去自定義對http的請求,達到更好的效果。
基於上面的內容,我們可以將ASP.NET請求的處理過程總結如下:
ASP.NET請求處理過程是基於“管道模型”的,客戶端向服務器發送http請求時,在模型中ASP.NET把http請求傳遞給管道中的所有模塊(ISAPI過濾器),每個模塊都接受http請求並且有完全控制權限,模塊可以用任何自認爲的方式(通過開發人員來開發,如校驗用戶名密碼等)來處理請求。一旦請求經過了所有Http模塊(如用戶名、密碼符合),就最終被Http處理程序(ISAPI擴展)處理,http處理程序對請求進行一些處理,並且結果將再次經過管道中的http模塊。
圖四 管道模型
我自己舉例來說吧,我建立了一個.apx文件,要求客戶端訪問該頁面,並在客戶端返回"我是apx文件,我是被“ISAPI過濾器”過濾過的,並被"ISAPI擴展"處理過的"。在訪問頁面時必須需要用戶名和密碼。
此時我就需要開發組件來擴充Web服務器,因爲Web服務器沒有校驗指定的用戶名和密碼的能力也沒有處理.apx文件的能力。我就需要開發“ISAPI過濾器”組件(用來校驗用戶名和密碼)和ISAPI擴展組件(處理.apx文件)。
那我們怎麼開發“ISAPI過濾器”組件和“ISAPI擴展”組件呢?
1、開發“ISAPI過濾器”
HttpModule(Http模塊)實現了ISAPI 過濾器的功能,是通過對IhttpModule接口的繼承來處理。HttpModule是實現了System.Web.IhttpModule接口的.NET組件,這些組件通過在某些事件中註冊自身,把自己插入到ASP.NET請求處理管道中(如上圖中的Module1,Module2)。當這些事件發生的時候,ASP.NET調用對請求有興趣的HTTP模塊,這樣該模塊就能處理請求了。
HttpModule的實現:
1、編寫一個類,實現IhttpModule接口,要添加引用System.Web
2、實現Init方法,並且要註冊需要的方法。如:AuthenticateRequest等
3、實現註冊的方法
4、實現Dispose方法,如果需要手工爲類做一些清除工作,可以添加Dispose方法的實現,但這不是必需的,通常可以
不爲Dispose方法添加任何代碼
5、在Web.config文件中,註冊您編寫的類。
下面通過一個實例來實現HttpModule(具體見附件代碼)
1、我編寫了SecurityModules類實現IHttpModule接口
2、實現了Init方法,並且向Application對象註冊事件處理程序(myAuthenticateRequest),當http請求執行到AuthenticateRequest(建立用戶標 識時)時,就執行註冊的方法myAuthenticateRequest
3、實現註冊的方法myAuthenticateRequest,主要來校驗userid和password
4、實現Dispose方法
2using System.Collections.Generic;
3using System.Text;
4using System.Web;
5using System.Security.Principal;
6
7namespace MyModule1
8{
9 public class SecurityModules : IHttpModule
10 {
11 public void Init(HttpApplication objApplication)
12 {
13 // 向Application 對象的事件AuthenticateRequest註冊處理程序
14 objApplication.AuthenticateRequest += new EventHandler(this.myAuthenticateRequest);
15 }
16
17 private void myAuthenticateRequest(object objSender, EventArgs objEventArgs)
18 {
19 // 鑑別用戶的憑證,並找出用戶角色
20 HttpApplication objApp = (HttpApplication)objSender;
21 HttpContext objContext = (HttpContext)objApp.Context;
22 if ((objContext.Request["userid"] == null) && (objContext.Request["password"] == null))
23 {
24 objContext.Response.Write("用戶名和密碼不允許爲空!");
25 objContext.Response.End();
26 }
27
28 string sUserId = "";
29 sUserId = objContext.Request["userid"].ToString();
30 string sPassword = "";
31 sPassword = objContext.Request["password"].ToString();
32 string[] strRoles;
33 strRoles = AuthenticateAndGetRoles(sUserId,sPassword);
34 if ((strRoles == null) || (strRoles.GetLength(0) == 0))
35 {
36 objContext.Response.Write("用戶名和密碼錯誤!");
37 objApp.CompleteRequest();//終止一個http請求
38 }
39 //GenericIdentity類表示具有指定名稱和身份驗證類型的用戶
40 GenericIdentity objIdentity = new GenericIdentity(sUserId, "CustomAuthentication");
41 objContext.User = new GenericPrincipal(objIdentity,strRoles);
42 }
43
44 /// <summary>
45 /// 根據http請求的userid和password來驗證和獲取角色。
46 /// </summary>
47 private string[] AuthenticateAndGetRoles(string sUserId, string sPassword)
48 {
49 string[] strRoles = null;
50 if ((sUserId.Equals("xieex")) && (sPassword.Equals("111")))
51 {
52 strRoles = new String[1];
53 strRoles[0] = "Administrator";
54 }
55 else if ((sUserId.Equals("zhangsan")) && (sPassword.Equals("222")))
56 {
57 strRoles = new String[1];
58 strRoles[0] = "User";
59 }
60 return strRoles;
61 }
62
63 public void Dispose()
64 {
65
66 }
67 }
68}
5、新建一個項目,在Web.config文件中註冊該類,當訪問該項目的頁面時,都要校驗userid和password才能訪問,並且在該項目的引用中添加MyModule1.dll
在Web.config文件中添加:
<httpModules>
<add name="myModule1" type="MyModule1.SecurityModules,MyModule1"/>
</httpModules>
<!--add將HttpModule類添加到應用程序-->
<!--add格式:<add name="modulename(隨便起)" type="命名空間。類名(該類繼承IHttpModule接口), (assemblyname)dll文件名"/> -->
<!--remove從應用程序移除HttpModule類-->
<!--remove格式<remove name="modulename">-->
<!--clear從應用程序移除所有HttpModule映射-->
<!--clear/-->
此時訪問該項目中的頁面時,就要校驗用戶名和密碼了。
此時輸入在URL上增加“?userid=xieex@password=111”時就可以進入頁面了。
這樣我們就開發完了ISAPI過濾器,注意過濾器可以有多個,只要在web.config中的httpModules節點中Add即可,它們都會起作用的不會覆蓋的。
HttpModule通過對HttpAplication對象的一系列事件的處理來對HTTP處理管道施加影響,這些事件在HttpModule的Init方法中進行註冊。具體的事件發生順序如下:
圖五 事件發生順序
關於事件發生順序可以參考例子:MultiHttpModule.csproj,這個例子也反應了過濾器可以有多個,只要在web.config中的httpModules節點中Add即可,它們都會起作用的不會覆蓋的。
2、開發“ISAPI擴展”組件
還是拿下面的例子來說:
“我自己舉例來說吧,我建立了一個.apx文件,要求客戶端訪問該頁面,並在客戶端返回"我是apx文件,我是被“ISAPI過濾器”過濾過的,並被"ISAPI擴展"處理過的"。在訪問頁面時必須需要用戶名和密碼。
此時我就需要開發組件來擴充Web服務器,因爲Web服務器沒有校驗指定的用戶名和密碼的能力也沒有處理.apx文件的能力。我就需要開發“ISAPI過濾器”組件(用來校驗用戶名和密碼)和ISAPI擴展組件(處理.apx文件)。”
上面的ISAPI過濾器可以解決“校驗用戶名和密碼”問題,下面我們利用ISAPI擴展來處理.apx文件。
HttpHandler實現了ISAPI擴展的功能,它處理請求(Request)的信息和發送響應(Response)。HttpHandler功能的實現要繼承IHttpHandler接口。
HTTP處理程序是實現了System.Web.IHttpHandler接口的.NET組件,任何實現了IHttpHandler接口的類都可以用於處理輸入的HTTP請求。
HttpHandler的實現:
1、編寫一個實現IHttpHandler接口的類;
2、在Web.config或machine.config文件中註冊這個處理程序;
3、在Internet服務管理器把文件擴展(你想要的文件擴展名)映射到ASP.NET ISAPI擴展DLL(aspnet_isapi.dll)上。(該步也可不用做)
同樣通過一個實例來實現HttpHandler(具體見附件代碼)
1、編寫了HandlerAPX類,該類繼承了IHttpHandler接口,該類實現了ProcessRequest方法和IsReusable屬性。注意要引用System.Web
2using System.Collections.Generic;
3using System.Text;
4using System.Web;
5
6namespace MyHandler
7{
8 public class HandlerAPX : IHttpHandler
9 {
10 #region Implementation of IHttpHandler
11 /// <summary>
12 /// http處理程序的核心。我們調用這個方法來處理http請求。
13 /// </summary>
14 /// <param name="context"></param>
15 public void ProcessRequest(HttpContext context)
16 {
17 HttpResponse objResponse = context.Response;
18
19 objResponse.Write("<html><body><h1>你好!我是apx文件!");
20 objResponse.Write("</body></html>");
21 }
22
23 /// <summary>
24 /// 我們調用這個屬性來決定http處理程序的實例是否可以用於處理相同其它類型的請求。
25 /// HTTP處理程序可以返回true或false來表明它們是否可以重複使用。
26 /// </summary>
27 public bool IsReusable
28 {
29 get
30 {
31 return true;
32 }
33 }
34 #endregion
35 }
36}
2、新建一個項目,在Web.config文件中註冊該類,並且在該項目的引用中添加MyHandler.dll
在Web.config文件中添加:
<add verb="*" path="*.apx"
type="MyHandler.HandlerAPX,MyHandler" />
</httpHandlers>
<!--add格式:<add verb="*" path="要處理文件" type="命名空間.類名(該類繼承IHttpHandler接口), (assemblyname)dll文件名"/> -->
說明當IIS識別到.apx文件時,就調用HandlerAPX類來處理。
http請求通過httpHandler來處理,本來系統就對其有默認處理,那和自定義的處理是如何協調的呢?看下圖就可以明白,其實兩者是選擇其一的:
在“圖五 事件發生順序”中,我們看到HttpHandler建立後,此後Session就可以用了,下面來看看在HttpHandler中如何訪問Session.
1、不能直接通過HttpContext訪問
2、必須實現IRequiresSessionState接口
3、IRequriresSessionState接口指定目標HTTP處理程序接口具有對會話狀態值的讀寫訪問權限,這是一個標記接口,沒
有任何方法。
同樣通過一個實例來實現在HttpHandler訪問Session(具體見附件代碼)
1、編寫類HandlerSession,該類實現了接口IHttpHandler,IRequiresSessionState
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.SessionState;
namespace MyHandlerSession
{
public class HandlerSession : IHttpHandler,IRequiresSessionState
{
#region Implementation of IHttpHandler
/// <summary>
/// http處理程序的核心。我們調用這個方法來處理http請求。
/// </summary>
/// <param name="context"></param>
public void ProcessRequest(HttpContext context)
{
HttpResponse objResponse = context.Response;
HttpRequest objRequest = context.Request;
HttpSessionState objSession = context.Session;
objResponse.Write("<html><body><h1>歡迎使用自定義的HttpHandler!<br>");
objSession["test"] = "Session測試<br>";
objResponse.Write("Session的值爲:" + objSession["Test"].ToString());
}
/// <summary>
/// 我們調用這個屬性來決定http處理程序的實例是否可以用於處理相同其它類型的請求。
/// HTTP處理程序可以返回true或false來表明它們是否可以重複使用。
/// </summary>
public bool IsReusable
{
get
{
return true;
}
}
#endregion
}
}
2、新建一個項目,在Web.config文件中註冊該類,並且在該項目的引用中添加MyHandlerSession.dll
在Web.config文件中添加:
<add verb="*" path="*.apx"
type="MyHandler.HandlerAPX,MyHandler" />
<add verb="*" path="HttpModule.aspx"
type="MyHandlerSession.HandlerSession,MyHandlerSession" />
</httpHandlers>
說明當IIS識別到HttpModule.aspx文件時,就調用HandlerSession類來處理。但是如果是.apx文件時,就會調用HandlerAPX類來處理。
也就是說註冊多個HttpHandler會覆蓋的,這與HttpModule是不一樣的。如果是對同一類文件註冊了不同的HttpHandler,會執行最後一個。
即:如果Web.config配置爲
<add verb="*" path="*.apx"
type="MyHandler.HandlerAPX,MyHandler" />
<add verb="*" path="*.apx"
type="MyHandlerSession.HandlerSession,MyHandlerSession" />
</httpHandlers>
說明當IIS識別到*.apx文件時,就調用HandlerSession類來處理,而不是調用HandlerAPX類來處理。
三、ASP.NET事件模型機制
1、ASP.NET之所以對於以前的ASP是一個革命性的鉅變,在很大程度上是由於ASP.NET技術是一種基於事件驅動的全新技術。
2、在ASP.NET中時間的觸發和處理是在客戶端和服務端進行的。
3、ASP.NET中,如果頻繁和服務器進行事件信息傳遞,會大大降低服務器的處理效率和性能,因而有些事件如OnMouseOver沒有提供;
4、但提供了Change事件,爲了提高效率它們被緩存在客戶端,等到再一次事件信息被髮送到服務器端時一同發送回去。
如文本框的change事件,下拉框的change事件,
如兩個控件的change事件中:
{
Response.Write("DropDownList控件選擇改變!<br>");
}
protected void TextBox1_TextChanged(object sender, EventArgs e)
{
Response.Write("TextBox文本改變!<br>");
}
如果控件本身的AutoPostBack設置爲false(默認是false)時,文本框和下拉框發生改變時,不會執行change事件的,而是將事件信息緩存在客戶端,當在頁面上點擊一個服務器端控件Button,
{
Response.Write("點擊了Button按鈕!<br>");
}
此時將客戶端中的事件信息發送到服務器端,執行所有的事件,返回到客戶端的信息爲:
TextBox文本改變!
DropDownList控件選擇改變!
點擊了Button按鈕!
具體例子代碼見附件。
HttpModuelandHttpHandler代碼