ASP.NET那點鮮爲人知的事(一)

http://www.cnblogs.com/OceanEyes/archive/2012/08/13/aspnetEssential-1.html#_label0css

咱們上網時,在瀏覽器地址輸入網址:Http://www.cnblogs.com,按下回車,一張網頁就呈如今咱們眼前。這究竟發生了什麼?對於一名優秀的Programmer來講,我想有必要一下熟悉瀏覽器--->服務器請求的過程。html

ASP.NET

ASP.NET是運行在公共語言運行時刻時(CLR)上的應用程序框架。他用來在服務器端構建功能強大的web應用程序。當瀏覽器請求 ASP.NET 文件時,IIS 會把該請求傳遞給服務器上的 ASP.NET 引擎,ASP.NET 引擎會逐行地讀取該文件,並執行文件中的腳本,最後,ASP.NET 文件會以純 HTML 的形式返回瀏覽器。程序員

客戶端瀏覽器和服務器之間的請求響應是經過Socket進行通訊,基於HTTP協議,客戶端發送一次HTTP請求,服務器接收到請求,處理以後向瀏覽器迴應響應報文。那麼什麼是HTTP協議呢?web

HTTP協議:

當瀏覽器尋找到Web服務器地址後,瀏覽器將幫助咱們把對服務器的請求轉換爲一系列參數(消息)發給Web服務器,瀏覽器和Web服務器的對話中,須要使用雙方都能理解語法規範進行通訊,這種程序之間進行通訊的語法規定,咱們稱之爲協議。瀏覽器與服務器之間的協議是應用層協議,當前遵循的協議是HTTP/1.1。HTTP/1.1協議時Web開發的基礎,這是一個無狀態協議,客戶端瀏覽器和服務器經過Socket通訊進行請求和響應完成一次會話。每次會話中,通訊雙方發送的數據稱爲消息,分爲兩種:請求消息和響應消息。編程

對於消息而言,通常他有三部分組成,而且消息的頭和消息體之間用一個空行進行分隔:api

咱們經過瀏覽器插件HttpWatch Professional能夠清晰看到瀏覽器和服務器之間的通訊內容:數組

瞭解了什麼是HTTP協議以後,咱們在回到先前提出的那個問題,瀏覽器的請求怎樣到達服務器?瀏覽器

HTTP.SYS組件

咱們知道要訪問一個網站,必需要其部署在相應服務器軟件上(如IIS),瀏覽器向服務器發送請求以後,當請求經過Socket到達服務器時,首先服務器Windows內核中的HTTP.SYS組件捕獲請求,根據URL的請求地址將其轉發到應用程序池(Application Pool,ASP.NET應用程序必須運行在一個應用程序池中),再由運行在應用程序池裏的工做者進程(Worker Process,用於裝載專門處理ASP.NET頁面的一個ISAPI擴展程序:aspnet_isapi.dll)響應請求,當請求處理完成時,HTTP.SYS又將結果發送出去(HTTP.SYS會在內部創建一個緩存區,用於緩存近期的處理結果)。當HTTP.SYS請求分析這是一個須要交給IIS服務器處理的HTTP請求時,HTTP.SYS組件就會把此次請求交給IISl處理,服務器軟件(IIS)會判斷用戶請求的是靜態頁面(Html)仍是動態頁面(Aspx.Ashx),若是請求的是Html靜態頁面或者js,css,xml以及圖片等,IIS直接返回請求的Html靜態頁面和js等相應文件。那麼若是請求的是動態頁面呢?仍是向處理靜態頁面同樣嗎?顯然是不可能的。IIS服務器會分析請求的類型,而後從處理程序映射(即下文IIS服務器擴展)表中去匹配,當在處理程序映射表中可以匹配到請求的類型時,那麼IIS服務器就將請求交給處理程序映射表中所對應的程序來處理。當IIS發現,在處理程序映射表中沒有能匹配的項的時候,就直接返回請求所對應物理路徑下的文件,如Html,JS,CSS,JPG,PNG等。緩存

 

IIS服務器擴展

因爲IIS服務器在設計時引入了開放的ISAPI接口標準,具有極高的可擴展性。在覈心組件不變的狀況下可靈活支持不一樣類型不一樣版本的ASP.NET應用程序。安全

ISAPI(Internet Server Application Programming Interface)

ISAPI(服務器應用編程接口),它爲開發人員提供了強大的可編程能力,只要按照標準接口開發不一樣類型的Web應用程序的ISAPI擴展程序,就能實現對IIS功能上的擴展,從而使IIS能夠處理不一樣類型的客戶端請求。IIS管理器提供了應用程序配置功能,能夠對不一樣的客戶端請求配置不一樣的ISAPI擴展程序ISAPI擴展程序一般以DLL形式存在,能夠被IIS加載並調用。有了基於ISAPI的擴展擴展程序,IIS服務器就能夠根據客戶端請求的資源擴展名,來決定應由哪一個ISAPI擴展程序來處理客戶端請求,而後就能夠將請求轉發給合適的ISAPI擴展程序。

IIS7處理程序映射

ASP.NET的後臺輔助進程aspnet_wp.exe

實際上客戶發起的請求最終要由aspnet_isapi.dll(被工做者進程Worker Process裝載)傳遞給aspnet_wp.exe去處理,.NET平臺下稱其爲ASP.NET Process(簡稱爲WP),該文件位於.Net Framework安裝目錄下,與aspnet_isapi.dll所在位置相同。當aspnet_isapi接收到IIS轉發的ASP.NET請求後,會將請求放入隊列,並根據實際狀況分配請求處理任務給WP進程。一旦請求被轉送給WP進程,WP進程便會通知aspnet_isapi請求正在被處理。這個通知的過程是經過同步I/O完成的,這麼實現目的是爲了保證處理過程的完整性,由於只有當請求在aspnet_isapi內部被標記爲"executing"後,WP纔會真正開始處理該請求。此後請求便在WP的上下文環境中執行。當執行結束後處理結果會經過一個異步的開放管道回送給aspnet_isapi,這時請求的狀態會被更新爲「Done」。接着請求就會從隊列中清除。若是WP進程崩潰,全部正在處理中的請求都將維持「executing」狀態一段時間,等到aspnet_isapi檢測到WP進程死掉後,會自動丟棄全部的請求並釋放已經分配的資源。

WP會分析每個請求的信息解析出其中的虛擬目錄信息,並檢查該虛擬目錄對應的AppDomain(應用程序域)是否已經存在,若是不存在,則建立一個新的AppDomain(ApplicationManager建立應用程序域),而後使用它。不然直接重用已經創建的AppDomain對象。這裏的AppDomain指的是.NET中引入的應用程序域的概念,程序集管理的最小邏輯單位爲應用程序域,包括四個重要的機制,隔離、卸載、安全、配置,它能夠理解爲一個進程或一個邊界或一個容器,它是應用程序的執行環境.NET下全部的應用程序都運行在AppDomain中,每個ASP.NET應用程序IIS中的站點或者虛擬目錄都會有一個AppDomain與之對應,它保存了Applcation對象、Cache等全局變量。

由一張流程圖回顧上述瀏覽器到達服務器的過程

 

 

 

 ISAPIRuntme.ProcessRequest方法第一個進入ASP.NET

當aspnet_wp.exe接受到aspnet_isapi.dll的請求後,就將請求轉給指定虛擬目錄對應的AppDomain中的ISAPIRuntime對象,ISAPIRuntime.ProcessRequest()開始進入ASP.NET,並將瀏覽器發送請求消息封裝成HttpWorkerRequest類(抽象類,開發環境中對應SimpleWorkRequest)。以後再執行HttpRuntime的靜態方法:ProcessRequestNoDemand(參數爲封裝了瀏覽器請求的信息:HttpWorkerRequest)

 

 補充:默默無聞的工做者對象HttpWorkerRequest

在Asp.Net中,準備用於處理的請求,必須封裝爲HttpWorkerRequest類型的對象,這是一個抽象類:

 

[ComVisibleAttribute(false)]
public abstract class HttpWorkerRequest

 

客戶的請求首先會被ISAPIRuntme對象ProcessRequest方法處理

建立了HttpWorkerRequest 類型的wr對象,由於ISAPIWorkerRequest 繼承於HttpWorkerRequest

 

[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public int ProcessRequest(IntPtr ecb, int iWRType)
{
    IntPtr zero = IntPtr.Zero;
    if (iWRType == 2)
    {
        zero = ecb;
        ecb = UnsafeNativeMethods.GetEcb(zero);
    }
    ISAPIWorkerRequest wr = null;
    try
    {
        bool useOOP = iWRType == 1;
        wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
        wr.Initialize();
        ......
        if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
        {
            HttpRuntime.ProcessRequestNoDemand(wr);
            return 0;
        }
        HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
        return 1;
    }
  ......
}

HttpRuntime調用ProcessRequestNoDemand方法:

 

internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)
{
    RequestQueue queue = _theRuntime._requestQueue;
    wr.UpdateInitialCounters();
    if (queue != null)
    {
        wr = queue.GetRequestToExecute(wr);
    }
    if (wr != null)
    {
        CalculateWaitTimeAndUpdatePerfCounter(wr);
        wr.ResetStartTime();
        ProcessRequestNow(wr);
    }
}

該方法先從請求隊列中取出一個請求,而後更新請求的引用計數器的信息,而後ProcessRequestNow方法處理請求。

 

 在這兒終於找到了HttpRuntime這個對象了:

internal static void ProcessRequestNow(HttpWorkerRequest wr)
{
    _theRuntime.ProcessRequestInternal(wr);
}
 _theRuntime就是HttpRuntime類型的對象,他在HttpRuntime的靜態構造函數初始化。
static HttpRuntime()
{
   ......
    _theRuntime = new HttpRuntime();
    _theRuntime.Init();
    AddAppDomainTraceMessage("HttpRuntime::cctor*");
}

點擊進入ProcessRequsetNow(Wr)方法,Wr即封裝了HTTP Message的HttpWorkRequest對象

在HttpRuntime接受到請求後,馬上經過HttpWorkerRequest傳遞的參數進行分析和分解,建立方便用戶網站應用程序處理用的對象。HttpRequest,HttpResponse

終於發現了HttpContext,根據HttpWorkerRequest初始化HttpContext

private void ProcessRequestInternal(HttpWorkerRequest wr)
{
    ......
    else
    {
        HttpContext context;
        try
        {
            context = new HttpContext(wr, false);
        }
        catch
        {
            try
            {
                wr.SendStatus(400, "Bad Request");
                wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");
                byte[] data = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
                wr.SendResponseFromMemory(data, data.Length);
                wr.FlushResponse(true);
                wr.EndOfRequest();
                return;
            }
            finally
            {
                Interlocked.Decrement(ref this._activeRequestCount);
            }
        }
        wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context);
        HostingEnvironment.IncrementBusyCount();
        try
        {
            try
            {
                this.EnsureFirstRequestInit(context);
            }
            catch
            {
                if (!context.Request.IsDebuggingRequest)
                {
                    throw;
                }
            }
   ......
    }
}

 

在進入看看:根據WR,初始化了請求參數的類型HttpRequest對象和處理迴應類型HttpReponse對象

internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter)
{
    this._timeoutStartTimeUtcTicks = -1;
    this._timeoutTicks = -1;
    this._threadAbortOnTimeout = true;
    this.ThreadContextId = new object();
    this._wr = wr;
    this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
    if (initResponseWriter)
    {
        this._response.InitResponseWriter();
    }
    PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
}

privatevoid ProcessRequestInternal(HttpWorkerRequest wr) ProcessRequestInternal這個方法很重要,前面分析了它建立了上下文對象HttpContext,接下來分析HttpApplication的建立。

複製代碼
{
            .....
            IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
            ......
try
{
this.EnsureFirstRequestInit(context);
}
......
context.Response.InitResponseWriter();
......if (applicationInstance is IHttpAsyncHandler) { IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; context.AsyncAppHandler = handler2; handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context); } ...... } } }
複製代碼
  • EnsureFirstRequestInit()方法完成第一次請求初始化工做,該方法鎖定全局變量_beforeRequestFirst,而後調用FirstRequestInit(context)完成配置文件的加載,初始化請求隊列,裝載Bin目錄下全部程序集工做,而後更新_beforeRequestFirst=false;context.FirstRequest=true;
複製代碼
private void EnsureFirstRequestInit(HttpContext context)
{
    if (this._beforeFirstRequest)
    {
        lock (this)
        {
            if (this._beforeFirstRequest)
            {
                this._firstRequestStartTime = DateTime.UtcNow;
                this.FirstRequestInit(context);
                this._beforeFirstRequest = false;
                context.FirstRequest = true;
            }
        }
    }
}
 
複製代碼
  • 執行InitResponseWrite建立HttpWrite對象,用於寫入結果返回信息。
  • 建立HttpApplication實例,HttpApplicationFactory.GetApplicationInstance(注意其實不是這個方法直接建立,而是經過這個方法裏面又調用了GetNormalApplicationInstance方法來建立默認的HttpApplication實例
  • 那什麼是HttpApplicationFactotry?
  • HttpApplicationFactotry用於負責管理一個HttpApplication的對象池。

看一下HttpApplication這個類的申明:

[ToolboxItem(false)]
public class HttpApplication : IComponent, IDisposable, IHttpAsyncHandler, IHttpHandler, IRequestCompletedNotifier, ISyncContext
{

}


調用HttpApplicationFactory對象的GetNormalApplicationInstance獲得一個HttpApplication實例:

 

internal static IHttpHandler GetApplicationInstance(HttpContext context)
{
    ......return _theApplicationFactory.GetNormalApplicationInstance(context);
}

GetApplicationInstance方法生成一個默認的HttpApplication對象,HttpApplication實現了IHttpAsyncHandler接口。

調用HttpApplication對象(實現了IHttpAsyncHandler接口)的BeginProcessRequest方法執行客戶請求。

 if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);
}

OK,回到前一步,再深刻一步,進入GetNormalApplicationInstance方法以後,咱們看到了HttpApplication對象是如何被建立和初始化:

 

複製代碼
private HttpApplication GetNormalApplicationInstance(HttpContext context)
{
    HttpApplication state = null;
  ......
    if (state == null)
    {
        state = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
        using (new ApplicationImpersonationContext())
        {
            state.InitInternal(context, this._state, this._eventHandlerMethods);
        }
    }
 ......
}
複製代碼

 

internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)咱們發現HttpApplication類提供了一個名爲InitInternal的方法,調用它來完成HttpApplication實例的初始化工做,點擊進入InitInternal方法內部:

 

複製代碼
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
{

    this._state = state;
    PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);

......
                this.InitModules();
            Label_006B:
                if (handlers != null)
                {
                    this.HookupEventHandlersForApplicationAndModules(handlers);
                }
             ......
          .....
            if (HttpRuntime.UseIntegratedPipeline)
            {
                this._stepManager = new PipelineStepManager(this);
            }
            else
            {
                this._stepManager = new ApplicationStepManager(this);
            }
            this._stepManager.BuildSteps(this._resumeStepsWaitCallback);
        }
 ......
}
複製代碼

首先初始化Modules(InitModules)

 

複製代碼
private void InitModules()
{
    HttpModuleCollection modules = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();
    HttpModuleCollection other = this.CreateDynamicModules();
    modules.AppendCollection(other);
    this._moduleCollection = modules;
    this.InitModulesCommon();
}
複製代碼

接下來完成事件的綁定(19個管道事件):BuildSteps: 

 

複製代碼
          if (HttpRuntime.UseIntegratedPipeline)
            {
                this._stepManager = new PipelineStepManager(this);
            }
            else
            {
                this._stepManager = new ApplicationStepManager(this);
            }
            this._stepManager.BuildSteps(this._resumeStepsWaitCallback);
        }
 ......
複製代碼

 BuildSteps完成HttpApplication19個管道事件的註冊:

複製代碼
internal override void BuildSteps(WaitCallback stepCallback)
{
    ArrayList steps = new ArrayList();
    HttpApplication app = base._application;
    bool flag = false;
    UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
    flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);
    steps.Add(new HttpApplication.ValidateRequestExecutionStep(app));
    steps.Add(new HttpApplication.ValidatePathExecutionStep(app));
    if (flag)
    {
        steps.Add(new HttpApplication.UrlMappingsExecutionStep(app));
    }
    app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
    steps.Add(new HttpApplication.MapHandlerExecutionStep(app));//---------------------->
    app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
    app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
    steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());
    steps.Add(newHttpApplication.CallHandlerExecutionStep(app));//---------------------->用於建立處理用戶請求的對象(Handler)
private void CreateEventExecutionSteps(object eventIndex, ArrayList steps)
{
    AsyncAppEventHandler handler = this.AsyncEvents[eventIndex];
    if (handler != null)
    {
        handler.CreateExecutionSteps(this, steps);
    }
    EventHandler handler2 = (EventHandler) this.Events[eventIndex];
    if (handler2 != null)
    {
        Delegate[] invocationList = handler2.GetInvocationList();
        for (int i = 0; i < invocationList.Length; i++)
        {
            steps.Add(new SyncEventExecutionStep(this, (EventHandler) invocationList[i]));
        }
    }
}
 
     
複製代碼

在HttpApplication對象初始化時,首先會調用InitModules方法來加載在web.config文件中配置的全部HttpModule模塊。

接着HookupEventHandlersForApplicationAndModules方法被調用,這個方法完成global.asax文件中配置的HttpModule或HttpApplication事件的綁定

最後ApplicationStopManager對象的BuildSteps方法被調用,完成HttpApplication19個管道事件的註冊。這個方法很重要,它將建立各類HttpApplication.IExecutionStep保存到一個數組列表:

 

複製代碼
internal override void BuildSteps(WaitCallback stepCallback)
{
.....
this._execSteps = new HttpApplication.IExecutionStep[steps.Count];
steps.CopyTo(this._execSteps);
.....
}
複製代碼

以便在BeginProcessRequest方法內部調用ResumeSteps方法依次執行這些對象的Execute()方法,完成各類處置。 

 

 調用BeginProcessRequest方法來實現IHttpAsyncHandler接口中定義的方法處理請求:

複製代碼
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
    this._context = context;
    this._context.ApplicationInstance = this;
    this._stepManager.InitRequest();
    this._context.Root();
    HttpAsyncResult result = new HttpAsyncResult(cb, extraData);
    this.AsyncResult = result;
    if (this._context.TraceIsEnabled)
    {
        HttpRuntime.Profile.StartRequest(this._context);
    }
    this.ResumeSteps(null);//---------->依次執行管道事件
    return result;
}
複製代碼

 

BeginProcessRequest執行過程

  • 在取得HttpApplication對象實例以後HttpRuntime對象開始調用其的BeginProcessRequest方法來實現IHttpAsyncHandler接口中定義的方法處理請求:
  • 該方法首先調用ApplicationStepManager對象的InitRequest方法完成一些初始化工做例如將記錄當前執行步驟的變量清0、置請求處理完成標誌爲false等。
  • 而後根據上下文建立HttpAsyncResult對象記錄執行結果最後ResumeSteps方法被調用這個方法會依次取出在數組列表中的HttpApplication.IExecutionStep對象傳遞給HttpApplication的ExecuteStep方法由它調用執行IExecutionStep對象的Execute方法。
  • 當執行到MapHandlerExecutionStep時會執行以下代碼獲取最終執行請求:context.Handler = this._application.MapHttpHandler()。HttpApplication對象的MapHttpHandler方法將根據配置文件結合請求類型和URL以調用相應的IHttpHandlerFactory來獲取HttpHandler對象。例如與.aspx頁面對應的Page類就是一種HttpHandler。此後請求處理的執行權被轉交至對應的HttpHandler對象上。下面代碼演示了過程:
複製代碼
    void HttpApplication.IExecutionStep.Execute()
    {
        HttpContext context = this._application.Context;
        HttpRequest request = context.Request;
        if (EtwTrace.IsTraceEnabled(5, 1))
        {
            EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_ENTER, context.WorkerRequest);
        }
        context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);
        if (EtwTrace.IsTraceEnabled(5, 1))
        {
            EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_LEAVE, context.WorkerRequest);
        }
    }
複製代碼

 這兒調用了一個很重要的方法MapHttpHandler:

 context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);
複製代碼
internal IHttpHandler MapHttpHandler(HttpContext context, string requestType, VirtualPath path, string pathTranslated, bool useAppConfig)
{
    IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null;
   ...
        IHttpHandlerFactory factory = this.GetFactory(mapping);
        try
        {
            IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2;
            if (factory2 != null)
            {
                handler = factory2.GetHandler(context, requestType, path, pathTranslated);
            }
            else
            {
                handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated);
            }
        }
     ...
        ....
    }
    return handler;
}
複製代碼

經過實現了IHttpHandlerFactory(PageHandlerFactory 或者 SimpleHandlerFactory等)建立了HttpHandler

 由於steps.Add(new HttpApplication.MapHandlerExecutionStep(app))註冊了Handler,因此會在第八個事件裏經過反射建立了頁面請求的對象(實現了IHttpHandler接口)。
複製代碼
void HttpApplication.IExecutionStep.Execute()
{
    HttpContext context = this._application.Context;
    IHttpHandler handler = context.Handler;
  .....
  ...
        IHttpAsyncHandler handler2 = (IHttpAsyncHandler) handler;
        this._sync = false;
        this._handler = handler2;
 ....
}
複製代碼
而後再第11個和12個事件之間,會調用了第八個事件建立的頁面對象的ProcessRequest方法,具體內容詳看我下一篇文章:《ASP.NET那點鮮爲人知的事(二)》

  
補充:BuildSteps方法裏註冊的HttpApplication管道的19個事件:

19個事件的處理過程:

  • 在Asp.Net中,Asp.Net服務器對於每一次請求的處理過程是相同的,都要通過HttpApplication處理管道,管道內部的處理過程是固定的,在服務器處理請求的各個階段,伴隨着處理的進行,一次觸發對應的事件,以便程序員在處理的各個階段完成自定義的處理工做。

  • 首先觸發的事件是BeginRequest,這個事件標誌着ASP.NET服務器處理工做的開始,也是程序員在ASP.NET中針對請求可以處理的第一個事件。

  • 開始處理請求後,第一個重要的工做就是肯定請求用戶的身份以及實現安全機制。這個工做經過AuthenticateRequest和PostAuthenticateRequest兩個事件提供檢查當前請求用戶身份的機會。PostAuthenticateRequest則表示用戶身份已經檢查完成,檢查後的用戶能夠經過HttpContext的User屬性獲取列。

複製代碼
public IPrincipal User
{
    get
    {
        return this._principalContainer.Principal;
    }
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries"), SecurityPermission(SecurityAction.Demand, ControlPrincipal=true)]
    set
    {
        this.SetPrincipalNoDemand(value);
    }
}
 
複製代碼

 Iprincipal又有一個名爲Identity,類型了System.Security.Principal.IIdentity屬性

複製代碼
[ComVisible(true), __DynamicallyInvokable]
public interface IPrincipal
{
    // Methods
    [__DynamicallyInvokable]
    bool IsInRole(string role);

    // Properties
    [__DynamicallyInvokable]
    IIdentity Identity { [__DynamicallyInvokable] get; }
}
複製代碼

 

複製代碼
[ComVisible(true), __DynamicallyInvokable]
public interface IIdentity
{
    // Properties
    [__DynamicallyInvokable]
    string AuthenticationType { [__DynamicallyInvokable] get; }
    [__DynamicallyInvokable]
    bool IsAuthenticated { [__DynamicallyInvokable] get; }
    [__DynamicallyInvokable]
    string Name { [__DynamicallyInvokable] get; }
}
複製代碼
IsAuthenticated表示當前請求用戶是否已經被驗證,IsAuthenticated =false,那麼表示這是一個匿名用戶,若是爲True,那麼經過IIdentity類型爲string的Name屬性,
這就表示當前請求的用戶名。
  • 當ASP.NET獲取用戶身份後,根據當前請求的用戶身份,開始請求權限的檢查工做。當第四個事件AuthorizeRequest觸發的時候開始進行用戶的權限檢查,而第五個事件PostAuthorizeRequest則標誌已經完成用戶權限檢查工做。若是用戶沒有經過安檢,通常狀況下將跳過剩餘事件,直接觸發EndRequest事件結束處理請求過程。

 

  • 當用戶獲取了請求權限,那麼服務器開始準備用最快的方式來使用戶獲得迴應結果。ResolveRequestCache事件標誌着到從前緩存的結果進行檢查,看看是否能夠直接從之前的緩存結果中直接獲取處理結果,PostResolveRequestCache表示緩存檢查結束。

 

  • 當不能從緩存中獲取結果時,必須經過一次處理來計算出當前請求的結果。在ASP.NET中,用戶處理請求以獲得結果的對象稱爲處理程序Handler。爲了處理這個這個請求,ASP.NET必須按照匹配規則找到一個處理當前請求的處理程序,PostMapRequestHandler事件表示當前ASP.NET已經獲取了這個處理程序,HttpContext的Handler屬性就表示這個處理程序對象。

 

  • 獲得了處理程序以後,還不能立刻開始進行處理,這是因爲處理請求還須要與這個請求有關的數據,好比說這個用戶上一次向服務器發送請求的時候,在服務器上報錯了一些這個用戶特有的數據。因爲HTTP協議的無狀態性,狀態管理問題是個核心問題,因此ASP時代就引入Session,提供基於會話狀態的管理。爲了獲取這個用戶在之前保存的數據,經過AcquireRequestState事件取得請求狀態,PostAcquireRequest事件則表示已經完成了用戶數據的獲取工做,能夠再處理中使用了。

  • PreRequestHandlerExcute事件用來通知程序員,處理程序就要開始進行處理工做了,若是用戶的狀態已經獲取以後,還有須要的處理程序之進行的工做,那麼就在這個事件中處理吧。在PreRequestHandlerExcute事件以後,ASP.NET服務器將經過執行處理程序完成請求處理工做。這個處理程序有多是一個WebForm,也多是Web服務。這個工做是在第11個事件和第12個事件之間完成的。

 

  • 處理程序以後,服務器開始進行掃尾工做,PostRequestHandlerExcute事件通知程序員,ASP.NET服務器處理程序已經完成。

  • 在處理完成以後,因爲處理程中,用戶可能修改了用於特定的專屬數據,那麼修改以後的用戶狀態數據須要進行序列化或者進行保存處理。ReleaseRequestState事件通知程序員須要釋放這些狀態數據,PostReleaseRequestState則表示已經釋放完成。

 

  • 在處理完成以後,若是須要將此次處理結果緩存起來,以便於後繼的請求能夠直接使用這個結果,UpdateRequestCache事件提供了處理的機會,PostUpdateRequestCache則表示緩存已經更新完畢。

  • 在ASP.NET4.0中,新增長了兩個事件完成處理的日誌工做:LogRequest表示將此次請求加入日誌,PostLogRequest表示完成了日誌工做。

  • 在前面的事件中,請求並不必定要通過全部的事件,好比說,用戶沒用通過受權的檢查,那麼將跳事後面的事件,可是,EndRequest事件是全部請求都要通過的最後一個HttpApplication處理管道的事件,也是程序員處理的ASP.NET處理請求中的最後一個機會。這個事件以後,處理的結果將被迴應到瀏覽器,完成ASP.NET服務器的處理工做。

相關文章
相關標籤/搜索