深入解析ASP.NET架構

最近聽了微軟講師邵志東的講座“深入解析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請求的處理過程:

上面我們已經瞭解了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方法

 1using System;
 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文件中添加:

<!--HttpModule通過對HttpApplication對象的一系列事件的處理來對HTTP處理管道施加影響,這些事件在HttpModule的Init方法中進行註冊包括BeginRequest等。-->
      
<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

 1using System;
 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文件中添加:

      <httpHandlers>
        
<add verb="*" path="*.apx" 
               type
="MyHandler.HandlerAPX,MyHandler" />

      
</httpHandlers>
      
<!--add格式:<add verb="*" path="要處理文件"  type="命名空間.類名(該類繼承IHttpHandler接口), (assemblyname)dll文件名"/>          -->

 

說明當IIS識別到.apx文件時,就調用HandlerAPX類來處理。

http請求通過httpHandler來處理,本來系統就對其有默認處理,那和自定義的處理是如何協調的呢?看下圖就可以明白,其實兩者是選擇其一的:


                                     圖六  HttpHandler之間的關係

在“圖五 事件發生順序”中,我們看到HttpHandler建立後,此後Session就可以用了,下面來看看在HttpHandler中如何訪問Session.
1、不能直接通過HttpContext訪問
2、必須實現IRequiresSessionState接口
3、IRequriresSessionState接口指定目標HTTP處理程序接口具有對會話狀態值的讀寫訪問權限,這是一個標記接口,沒
   有任何方法。

同樣通過一個實例來實現在HttpHandler訪問Session(具體見附件代碼)
1、編寫類HandlerSession,該類實現了接口IHttpHandler,IRequiresSessionState

 

using System;
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文件中添加:

      <httpHandlers>
        
<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配置爲

      <httpHandlers>
        
<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事件中:

 

        protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
        
{
            Response.Write(
"DropDownList控件選擇改變!<br>"
);
        }


        
protected void TextBox1_TextChanged(object sender, EventArgs e)
        
{
            Response.Write(
"TextBox文本改變!<br>"
);
        }

如果控件本身的AutoPostBack設置爲false(默認是false)時,文本框和下拉框發生改變時,不會執行change事件的,而是將事件信息緩存在客戶端,當在頁面上點擊一個服務器端控件Button,

        protected void Button1_Click(object sender, System.EventArgs e)
        
{
           Response.Write(
"點擊了Button按鈕!<br>"
);
        }

此時將客戶端中的事件信息發送到服務器端,執行所有的事件,返回到客戶端的信息爲:
TextBox文本改變!
DropDownList控件選擇改變!
點擊了Button按鈕!

具體例子代碼見附件。
HttpModuelandHttpHandler代碼

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