《ASP.NET 本質論》HttpApplication的處理管道

        HttpApplication對象是ASP.NET中處理請求的重要對象,但是,這種類型的對象實例不是由程序員來創建的,而是由ASP.NET幫助我們創建的。爲了便於擴展處理工作,HttpApplication採用處理管道的方法進行處理,將處理的過程分爲多個步驟,每個步驟通過事件的形式暴露給程序域,這些事件按照固定的處理順序依次觸發,程序員通過編寫事件處理方法就可以自定義每一個請求的擴展處理過程。
      對於HttpApplication來說,到ASP.NET 4.0版本,提供了19個標準事件,如下表:
可參見MSDN:http://msdn.microsoft.com/zh-cn/library/system.web.httpapplication.aspx

序號

名稱

說明

1

BeginRequest

ASP.NET開始處理的第一個時間,表示處理的開始

2

AuthenticateRequest

驗證請求,一般用來取得請求的用戶信息

3

PostAuthenticateRequest

已經獲取請求的用戶信息

4

AuthorizeRequest

授權,一般用來檢查用戶的請求是否獲得權限

5

PostAuthorizeRequest

用戶請求已經得到授權

6

ResolveRequestCache

獲取以前處理緩存的處理結果,如果以前緩存過,那麼不必再進行請求的處理工作,直接返回緩存結果

7

PostResolveRequestCache

已經完成緩存的存取工作(在 PostResolveRequestCache 事件之後和 PostMapRequestHandler 事件之前,會創建一個事件處理程序(一個對應於請求 URL 的頁)。 如果服務器在集成模式下運行 IIS 7.0 並且 .NET Framework 至少爲 3.0 版本,則會引發 MapRequestHandler 事件。 如果服務器在經典模式下運行 IIS 7.0 或者運行的是較早版本的 IIS,則無法處理此事件。)

8

PostMapRequestHandler

已經根據用戶的請求,創建了處理請求的處理器對象

9

AcquireRequestState

取得請求的狀態,一般用於Session

10

PostAcquireRequestState

已經取得了Session

11

PreRequestHandlerExecute

準備執行處理程序(執行事件處理程序。)

12

PostRequestHandlerExecute

已經執行了處理程序

13

ReleaseRequestState

釋放請求的狀態

14

PostReleaseRequestState

已經釋放了請求的狀態(在引發 PostReleaseRequestState 事件之後,現有的所有響應篩選器都將對輸出進行篩選。)

15

UpdateRequestCache

更新緩存

16

PostUpdateRequestCache

已經更新了緩存

17

LogRequest.

請求的日誌操作(僅在 IIS 7.0 處於集成模式並且 .NET Framework 至少爲 3.0 版本的情況下才支持此事件。)

18

PostLogRequest

已經完成了請求的日誌操作(僅在 IIS 7.0 處於集成模式並且 .NET Framework 至少爲 3.0 版本的情況下才支持此事件。)

19

EndRequest

本次請求處理完成


處理過程簡單介紹
        在ASP.NET中,ASP.NET服務器對於每一次請求的的處理過程是相同的,都要經過這個HttpApplication的處理管道。管道內部的處理過程是固定的,在服務器處理請求的各個階段,伴隨着處理的進行,依次觸發對應的事件,以便於程序員在處理的各個階段完成自定義的處理工作。
       首先觸發的事件是BeginRequest,這個事件標誌着ASP.NET服務器處理工作的開始,也是程序員在ASP.NET中針對請求所能夠處理的第一個事件。
      開始處理請求後,第一個重要的工作就是確定請求用戶的身份以實現安全機制,這個工作通過AuthenticateRequest和PostAuthenticateRequest兩個事件提供檢查當前請求用戶身份的機會。顯然,AuthenticateRequest表示開始檢查用戶的身份,而PostAuthentiacteRequest則表示用戶身份已經檢查完成,檢查後的用戶可以通過HttpContext的User屬性獲取到。這個屬性的類型爲System.Security.Principal.IPrincipal,IPrincipal又有一個名爲Identity,類型爲System.Security.Pricipan.IIdentity的屬性,IIdentity有一個類型爲bool型的屬性IsAuthenticated,表示當前請求的用戶是否已經被驗證,或者說確定了用戶是否是匿名用戶,IsAuthenticated如果爲false,那麼,表示是一個匿名用戶,如果爲true,那麼通過IIdentity類型爲string的Name屬性,這就是當前請求的用戶名。
       當ASP.NET獲取用戶的身份之後,根據當前請求的用戶身份,開始請求權限的檢查工作。當第4個事件AuthorizeRequest觸發的時候,表示開始進行用戶的權限檢查,而第5個事件PostAuthorizeRequest則標誌着已經完成了用戶權限的檢查工作。如果用戶沒有通過安全檢查,一般情況下,將跳過剩下的事件,直接觸發EndRequest事件結束請求的處理過程。
      當用戶取得了請求的權限,那麼服務器開始準備用最快的方式來使用戶得到迴應的結果。ResolveRequestcache事件標誌着到從前緩存的結果中進行檢查,看看是否可以直接從以前緩存的結果中直接獲取處理的結果,PostResolveRequestCache表示緩存檢查的結束。
      當不能從緩存中獲取結果的時候,必須通過一次處理來計算出當前請求的結果。在ASP.NET中,用於處理請求以得到結果的對象成爲處理程序Handler,在ASP.NET中提供了許多的處理程序,程序員也可以自定義處理程序。爲了處理這個請求,ASP.NET必須按照匹配規則找到一個處理當前請求的處理程序,PostMapRequestHandler事件表示ASP.NET已經獲取了這個處理程序,HttpContext的Handler屬性就表示這個處理程序對系那個。從上面的分析可以看到,HttpContext的Handler屬性到這裏纔有實際意義。
      得到了處理程序之後,還不能馬上開始進行處理,這是由於處理請求還需要許多與這個請求有關的數據,比如說,這個用戶在上一次向服務器發請求的時候,在服務器保存了一些這個用戶特有的數據。從ASP時代開始,Session這個概念就處在Web開發中,提供基於會話的狀態管理,由於HTTP協議的無狀態性,狀態管理的問題是Web開發的一個核心問題。
      爲了獲取這個用戶在以前保存的專屬數據,AcquireRequestState事件給程序員提供了一個切入點,PostAcquireRequestState事件則表示已經完成了用戶數據的獲取工作,可以在處理中使用了。
     萬事俱備,只欠東風,PreRequestHandlerExecute事件用來通知程序員,處理程序就要開始進行處理工作了,如果在用戶的狀態已經獲取之後,還需要在處理程序之前進行的工作,那麼,就在這個事件中處理吧。
       在PreRequestHandlerExecute事件之後,ASP.NET服務器將通過執行處理程序完成請求的處理工作。這個處理程序可能是一個Web窗體,也可能是一個Web服務。這個工作在第11個事件和第12個事件之間完成。
     處理程序完成之,服務器開始進行掃尾工作,PostRequestHandlerExecute事件通知程序員,ASP.NET服務器的處理程序已經完成。
     在處理完成之後,由於在處理程序在,用戶可能修改了用戶特定的專屬數據,那麼,修改之後的用戶狀態數據可能需要進行序列化或者進行保存處理。ReleaseRequestState事件通知程序員需要釋放這些狀態數據,PostReleaseRequestState則表示已經釋放完成。
      在處理完成之後,如果希望將這次處理的結果緩存起來,以便於在後繼的請求中可以直接使用這個結果,UpdateRequestCache事件提供了處理的機會,PostUpdateRequestCache則表示緩存已經更新完成。
      在ASP.NET 4.0 中,新增加了兩個完成處理的日誌工作,LogRequest表示將這次請求記入日誌中,PostLogRequest表示完成了日誌工作。
     在前述的事件中,請求不一定要經過所有的事件,比如說,用戶沒有經過授權的檢查,那麼將跳過後面的事件,但是,EndRequest事件是所有請求都要經過的最後一個HttpApplication處理管道的事件,也是程序員處理的ASP.NET處理請求中的最後一個機會。這個事件之後,處理的結果將被迴應到瀏覽器,完成ASP.NET服務器的處理工作。


處理HttpApplication的事件
       HttpApplication提供了基於事件的擴展機制,運行程序員藉助於處理管道中的事件進行處理過程擴展。由於HttpApplication對象是由ASP.NET基礎架構來創建和維護的,那麼,如何才能獲取這個對象引用,以便於註冊HttpApplication對象的事件處理,就是程序員首先要解決的問題。在ASP.NET中,提高了兩種方式來解決這個問題:IHttpModule方式和 global.asax方式。這兩種方式的核心都是IHttpModule接口。

通過IHttpModule創建HttpApplication的事件處理程序
       在ASP.NET中,定義在System.Web命名空間下的IHttpModule接口專門用來定義HttpApplication對象的事件處理。
      實現IHttpModule接口的類成爲HttpModule。IHtpModule接口的定義如下,其中僅僅包含兩個成員:

public interface IHttpModule
{

void Dispose()
vodi Init(HttpApplication context)

}


創建和註冊自定義 HTTP 模塊 MSDN:http://msdn.microsoft.com/zh-cn/library/ms227673.aspx
     
      其中,Dispose方法用於回收Module所使用的非託管資源,如果沒有的話,直接返回即可。
      最重要的是Init方法,可以看到,這個方法接受一個HttpApplication類型的參數,在ASP.NET中,每當創建一個HttpApplication對象實例,將遍歷註冊的HttpModule類型,通過反射,依次創建每個註冊HttpModule類型的實例對象,並將這個HttpApplication實例通過Init方法傳遞給各個HttpModule,這樣HttpModule就可以在第一時間完成針對HttpApplication對象的事件註冊了。
       例如,希望寫一個處理PostAuthentiacteRequest事件的HttpModule,那麼,可以如下完成事件的註冊:
       
 public void Init(HttpApplication context)
        {
            context.PostAuthorizeRequest += new EventHandler(context_PostAuthorizeRequest);
        }



註冊 HttpModule
       在APS.NET中,實現IHttpModule接口只是實現HttpModule的第一步,在ASP.NET中所使用的HttpModule還必須在網站配置文件中進行註冊才能真正生效,並在ASP.NET中使用。
      在.NET中,網站的配置文件分爲三個級別,首先在.NET的系統文件夾中,有針對本服務器所有.NET程序的配置文件,配置文件所在的文件夾位於如下位置:
                     操作系統文件夾\Microsoft.NET\Framework\ASP.NET版本\Config
      在這個文件夾中,有兩個重要的配置文件:machine.config   和  web.config。machine.config配置文件中保存有針對此服務器所有.NET程序的基本配置參數。web.config配置文件中保存有針對此服務器所有Web應用程序的基本配置參數。在我們開發的網站項目中的web.config中所做的配置是專門針對這個網站應用程序的配置文件,在網站應用程序中起作用的配置參數來自這三個配置文件的整合。
      在ASP.NET的網站配置文件web.config中,system.web配置元素的子元素httpModules用來配置網站所使用的HttpModule;httpModules的子元素add用來增加一個新的HttpModule;clear將清除前面註冊的所有HttpModule。
     add元素有兩個必選的屬性name 和type,簡介如下:
      name表示這個HttpModule在程序中的名字,在網站應用程序中,可以通過這個名字來找到HttpModule對象的引用。HttpApplication的Modules屬性表示這個對象所管理的所有HttpModule對象,通過這個name作爲索引器,可以找到對應的HttpModule對象。
     type表示HttpModule對象的類型名,ASP.NET網站可以使用這個類型名,通過反射來動態創建HttpModule對象。類型哦寫法就是反射中要求的類型名稱寫法,如果這個類型定義在網站中,那麼,就是一個博阿含命名空間哦類的全名,否則的話,在全名的後面,使用逗號(,)分隔,還需要跟上類型所在程序集的名稱,這個程序集的名稱不需要包含.dll擴展名。

      例如,自定義的HttpModule類位於程序集OnlineUserModule中,類的全名爲Samples.AspNet.CS.CustomHTTPModule,將這個自定義的HttpModule註冊到網站中,那麼配置文件中的定義如下所示:
以下設置適用於 IIS 7.0 經典模式以及較早的 IIS 版本。

<configuration>
  <system.web>
    <httpModules>
      <add type="Samples.AspNet.CS.CustomHTTPModule,OnlineUserModule"
        name="CustomHttpModule" />
      </httpModules>
  </system.web>
</configuration>

以下設置適用於 IIS 7.0 集成模式。

<configuration>
  <system.webServer>
    <modules>
      <add type="Samples.AspNet.CS.CustomHTTPModule,OnlineUserModule"
        name="CustomHttpModule" />
      </modules>
  </system.webServer>
</configuration>


對於IIS 7.0,可以爲MapRequestHandler,LogRequest和PostLogRequest事件添加處理程序。只有在IIS 7.0集成模式下運行並且與 .NET Frameword 3.0或更高版本一起運行的應用程序,纔可以支持這些事件。
       託管代碼模塊也可以在IIS 7.0配置存儲區(ApplicationHost.config文件)的modules元素中註冊。在ApplicationHost.config文件中註冊的模塊具有全局範圍,因爲它們爲所有由IIS 7.0承載的應用程序而註冊。同樣,在ApplicationHost.config文件的globalModules元素中定義的本機代碼模塊也具有全局範圍。如果Web應用程序不需要全局模塊,則可以將其禁用。


不使用配置文件註冊HttpModule
        在APS.NET  4.0 中,還可以不通過修改配置文件來完成Module的註冊。從。NET3.5開始,新提供的PreApplicationStartMethodAttribute特徵可以應用在程序集上,使得自定義的網站初始化代碼可以在Web應用程序的Application_Start初始化環節之前就執行。這個步驟設置在動態編譯和執行Application_Start之前。對於每個程序集,可以定義依次。特徵的定義如下。

[AttributeUsageAttribute(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class PreApplicationStartMethodAttribute : Attribute
{
   public Type Type{get;}
   public string MethorName{get;}
   ......
}

        Type用來指定定義了初始化方法的類型,MethodName用來指定將要執行的初始化的方法。
        這樣,可以不再配置文件中固定配置HttpModule,而是定義一個方法,這個方法可以返回需要動態註冊的HttpModule,將這個方法以委託的形式登記在一個集合中。在網站啓動之後,每當HttpApplicationFactory創建一個HttpAppplication對象,完成正常註冊的HttpModule創建及初始化之後,再來創建我們動態註冊的這些HttpModule。
       對於HttpApplication來說,其Init方法將在網站正常註冊的HttpModule創建及註冊之後被調用,用來完成自定義的HttpApplication初始化。我們可以在這個時間點動態註冊HttpModule。在ASP.NET網站中,Global.asax文件用來生成一個HttpApplication的派生類,這個類用來創建網站中使用的HttpApplication對象,我們可以重寫這個派生類的Init方法,完成動態註冊的HttpModule創建和註冊工作。

Global.asax
private List<IHttpModule> dynamicModules;

        public override void Init()
        {
            base.Init();

            this.dynamicModules
                = DynamicHttpModuleManager.GetModules();
            foreach (IHttpModule module in this.dynamicModules)
            {
                module.Init(this);
            }
        }
在網站初始化之前,將需要註冊的Module類型記錄在 一個集合中。

public delegate IHttpModule CreateDynamicHttpModule();

    public static class DynamicHttpModuleManager
    {
        public static List<CreateDynamicHttpModule> _createModuleHandlerList = 
            new List<CreateDynamicHttpModule>();

        public static void RegisterDynamicModule(
            CreateDynamicHttpModule handler)
        {
            _createModuleHandlerList.Add(handler);
        }

        public static List<IHttpModule> GetModules()
        {
            List<IHttpModule> list = new List<IHttpModule>();
            foreach (CreateDynamicHttpModule handler in _createModuleHandlerList)
            {
                IHttpModule module = handler();
                list.Add(module);
            }
            return list;
        }
    }

在Module中增加一個用戶註冊的方法Register,注意,必須是public 和static

public class Class1:IHttpModule
    {

        public static void Register()
        { 
            DynamicHttpModuleManager.RegisterDynamicModule
            (
                ()=>new Class1()
            );
        }

最後,在項目的AssemblyInfo.cs文件中增加PreApplicationStartMethod特徵完成動態註冊。
[assembly: PreApplicationStartMethod(typeof(WebApplication1.Class1),"Register")]

        在ASP.NEt中,已經與定義了許多HttpModule,甚至已經在服務器的網站配置文件中進行了註冊,在系統文件夾 系統目錄\Microsoft.NET\Framework\v4.0.30319\Config中,web.config文件中已經註冊了14個HttpModule。
 <httpModules>
            <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
            <add name="Session" type="System.Web.SessionState.SessionStateModule" />
            <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
            <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
            <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
            <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
            <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
            <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
            <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" />
            <add name="Profile" type="System.Web.Profile.ProfileModule" />
            <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
            <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
            <add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        </httpModules>

主要的HttpModule的解釋如下:

OutputCacheModule完成ASP.NET的輸出緩存管理工作。
    OutputCacheModule的配置參數通過system.web配置元素的caching子元素的outputCache元素進行定義。當啓用輸出緩存之後,OutputCacheModule將注 冊      HttpApplication的ResolverRequestcache和UpdateRequestcache兩個事件完成輸出緩存管理。

SesionStateModule完成Sesion管理工作。
     這個Module的配置參數通過配置文件中的system.web配置元素的sessionState子元素進行配置。當啓用Session狀態管理之後,SessionStateModule將註冊HttpApplication的AcquireRequestState、ReleaseRequestState、EndRequest三個事件完成Session狀態的管理工作。

ProfileModule在.NET2.0之後,提供個性化數據管理。
    這是一個自定義的類似於Session的迴環狀態管理,但是,個性化數據的讀取和保存可以由程序員完全控制,並且提供了強類型的數據訪問方式。這個Module的配置參數在system.web的子元素profile中進行說明。當啓用了個性化數據管理之後,Module將註冊HttpApplication的AcquireRequestState和EndRequest事件處理。

AnonymousIdentifiactionModule提供匿名用戶標識。
    是否啓用匿名用戶標識在配置文件的system.web配置元素的子元素anonymodusIdentification中定義,還可以配置匿名標識的管理方式。由於在AuthenticateRequest事件中將驗證用戶,獲取用戶名,所以,這個Module註冊了PostAuthenticateRequest的事件處理,當用戶沒有經過驗證的時候,爲用戶分配一個唯一的匿名標識。

WindowsAuthenticationModule、FormsAuthentiactionModule和PassportAuthenticationModule用來完成用戶驗證工作。
    它們通過配置文件中的system.web的子元素authentication子元素定義,mode屬性用來指定網站當前使用的驗證方式,也就是哪一個Module將被用來完成驗證工作。在啓用驗證的情況下,FormsAuthenticationModule和PassprotauthenticationModule將註冊HttpApplication的AuthenticateRequest和EndRqeust事件進行用戶的驗證處理。WindowsAuthenticaitonModule將註冊AuthenticateRequest的事件處理。

RoleManagerModule、UrlAuthorizationModule、FileAuthorizetionModue用來完成用戶的授權管理。
     授權管理的配置參數來自system.web的authorization子元素。UrlAuthorizationModule和FileAuthorizationModule註冊了HttpApplication的AuthorizeRequest事件處理,用來檢查Url和文件的訪問授權。RoleManagerModule在Url和文件訪問授權檢查通過之後,通過用戶的標識和角色來完成用戶的授權檢查,RoleManagerModule註冊了HttpApplication的PostAuthenticateRequest和EndRequest事件處理。

HttpModule的事件

    每個HttpModule也可以觸發自定義的事件,但是,處理這些HttpModule事件更加麻煩一些,因爲這些HttpModule對象實例也不是我們自己創建的。
    一般來說,可以通過HttpApplication的Modules屬性獲取特定的HttpModule,這個屬性的定義如下
public HttpModuleCollection Modules { get; }

    可以使用定義HttpModule時候哦name作爲索引器來獲取對應的HttpModule。例如獲取前面定義的HttpModule對象的引用,可以如下進行:

application,Modules["CustomHttpModule"];

然後,就可以定義這個HttpModule的事件處理了。
不過這樣比較麻煩,更簡單的方式是在global.asax中進行事件處理。

global.asax中HttpApplication事件的自動註冊

      在global.asax中,針對HttpApplication的事件處理,可以通過定義特殊命令的方法來實現。首先,這些方法必須符合System.EventHandler,因爲所有的HttpApplication管道事件都是用這個委託定義。第二,方法的作用於必須是public。第三,方法的命名格式必須如下:Application_註冊的事件名稱。按照這種命名方法定義在global.asax中的方法將被自動註冊到對應事件中。
     例如,希望在global.asax中註冊PostAuthentiacteRequest事件處理,按麼global.asax中應該定義一個如下的方法:

void Appliaction_PostAuthenticateRequest(object sender, EventArgs e)
        { 
            //....
        }



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