ASP.NET那點不為人知的事(一)
我們上網(wǎng)時,在瀏覽器地址輸入網(wǎng)址:Http://www.rzrgm.cn,按下回車,一張網(wǎng)頁就呈現(xiàn)在我們眼前。這究竟發(fā)生了什么?對于一名優(yōu)秀的Programmer來說,我想有必要一下熟悉瀏覽器--->服務(wù)器請求的過程。
ASP.NET
ASP.NET是運行在公共語言運行時刻時(CLR)上的應(yīng)用程序框架。他用來在服務(wù)器端構(gòu)建功能強(qiáng)大的web應(yīng)用程序。當(dāng)瀏覽器請求 ASP.NET 文件時,IIS 會把該請求傳遞給服務(wù)器上的 ASP.NET 引擎,ASP.NET 引擎會逐行地讀取該文件,并執(zhí)行文件中的腳本,最后,ASP.NET 文件會以純 HTML 的形式返回瀏覽器。
客戶端瀏覽器和服務(wù)器之間的請求響應(yīng)是通過Socket進(jìn)行通信,基于HTTP協(xié)議,客戶端發(fā)送一次HTTP請求,服務(wù)器接收到請求,處理之后向瀏覽器回應(yīng)響應(yīng)報文。那么什么是HTTP協(xié)議呢?
HTTP協(xié)議:
當(dāng)瀏覽器尋找到Web服務(wù)器地址后,瀏覽器將幫助我們把對服務(wù)器的請求轉(zhuǎn)換為一系列參數(shù)(消息)發(fā)給Web服務(wù)器,瀏覽器和Web服務(wù)器的對話中,需要使用雙方都能理解語法規(guī)范進(jìn)行通信,這種程序之間進(jìn)行通信的語法規(guī)定,我們稱之為協(xié)議。瀏覽器與服務(wù)器之間的協(xié)議是應(yīng)用層協(xié)議,當(dāng)前遵循的協(xié)議是HTTP/1.1。HTTP/1.1協(xié)議時Web開發(fā)的基礎(chǔ),這是一個無狀態(tài)協(xié)議,客戶端瀏覽器和服務(wù)器通過Socket通信進(jìn)行請求和響應(yīng)完成一次會話。每次會話中,通信雙方發(fā)送的數(shù)據(jù)稱為消息,分為兩種:請求消息和響應(yīng)消息。
對于消息而言,一般他有三部分組成,并且消息的頭和消息體之間用一個空行進(jìn)行分隔:

我們通過瀏覽器插件HttpWatch Professional可以清晰看到瀏覽器和服務(wù)器之間的通信內(nèi)容:


了解了什么是HTTP協(xié)議之后,我們在回到先前提出的那個問題,瀏覽器的請求怎樣到達(dá)服務(wù)器?
HTTP.SYS組件
我們知道要訪問一個網(wǎng)站,必須要其部署在相應(yīng)服務(wù)器軟件上(如IIS),瀏覽器向服務(wù)器發(fā)送請求之后,當(dāng)請求通過Socket到達(dá)服務(wù)器時,首先服務(wù)器Windows內(nèi)核中的HTTP.SYS組件捕獲請求,根據(jù)URL的請求地址將其轉(zhuǎn)發(fā)到應(yīng)用程序池(Application Pool,ASP.NET應(yīng)用程序必須運行在一個應(yīng)用程序池中),再由運行在應(yīng)用程序池里的工作者進(jìn)程(Worker Process,用于裝載專門處理ASP.NET頁面的一個ISAPI擴(kuò)展程序:aspnet_isapi.dll)響應(yīng)請求,當(dāng)請求處理完成時,HTTP.SYS又將結(jié)果發(fā)送出去(HTTP.SYS會在內(nèi)部建立一個緩存區(qū),用于緩存近期的處理結(jié)果)。當(dāng)HTTP.SYS請求分析這是一個需要交給IIS服務(wù)器處理的HTTP請求時,HTTP.SYS組件就會把這次請求交給IISl處理,服務(wù)器軟件(IIS)會判斷用戶請求的是靜態(tài)頁面(Html)還是動態(tài)頁面(Aspx.Ashx),如果請求的是Html靜態(tài)頁面或者js,css,xml以及圖片等,IIS直接返回請求的Html靜態(tài)頁面和js等相應(yīng)文件。那么如果請求的是動態(tài)頁面呢?還是向處理靜態(tài)頁面一樣嗎?顯然是不可能的。IIS服務(wù)器會分析請求的類型,然后從處理程序映射(即下文IIS服務(wù)器擴(kuò)展)表中去匹配,當(dāng)在處理程序映射表中能夠匹配到請求的類型時,那么IIS服務(wù)器就將請求交給處理程序映射表中所對應(yīng)的程序來處理。當(dāng)IIS發(fā)現(xiàn),在處理程序映射表中沒有能匹配的項的時候,就直接返回請求所對應(yīng)物理路徑下的文件,如Html,JS,CSS,JPG,PNG等。
IIS服務(wù)器擴(kuò)展
由于IIS服務(wù)器在設(shè)計時引入了開放的ISAPI接口標(biāo)準(zhǔn),具備極高的可擴(kuò)展性。在核心組件不變的情況下可靈活支持不同類型不同版本的ASP.NET應(yīng)用程序。
ISAPI(Internet Server Application Programming Interface)
ISAPI(服務(wù)器應(yīng)用編程接口),它為開發(fā)人員提供了強(qiáng)大的可編程能力,只要按照標(biāo)準(zhǔn)接口開發(fā)不同類型的Web應(yīng)用程序的ISAPI擴(kuò)展程序,就能實現(xiàn)對IIS功能上的擴(kuò)展,從而使IIS可以處理不同類型的客戶端請求。IIS管理器提供了應(yīng)用程序配置功能,可以對不同的客戶端請求配置不同的ISAPI擴(kuò)展程序ISAPI擴(kuò)展程序通常以DLL形式存在,可以被IIS加載并調(diào)用。有了基于ISAPI的擴(kuò)展擴(kuò)展程序,IIS服務(wù)器就可以根據(jù)客戶端請求的資源擴(kuò)展名,來決定應(yīng)由哪個ISAPI擴(kuò)展程序來處理客戶端請求,然后就可以將請求轉(zhuǎn)發(fā)給合適的ISAPI擴(kuò)展程序。
IIS7處理程序映射


ASP.NET的后臺輔助進(jìn)程aspnet_wp.exe
實際上客戶發(fā)起的請求最終要由aspnet_isapi.dll(被工作者進(jìn)程Worker Process裝載)傳遞給aspnet_wp.exe去處理,.NET平臺下稱其為ASP.NET Process(簡稱為WP),該文件位于.Net Framework安裝目錄下,與aspnet_isapi.dll所在位置相同。當(dāng)aspnet_isapi接收到IIS轉(zhuǎn)發(fā)的ASP.NET請求后,會將請求放入隊列,并根據(jù)實際情況分配請求處理任務(wù)給WP進(jìn)程。一旦請求被轉(zhuǎn)送給WP進(jìn)程,WP進(jìn)程便會通知aspnet_isapi請求正在被處理。這個通知的過程是通過同步I/O完成的,這么實現(xiàn)目的是為了保證處理過程的完整性,因為只有當(dāng)請求在aspnet_isapi內(nèi)部被標(biāo)記為"executing"后,WP才會真正開始處理該請求。此后請求便在WP的上下文環(huán)境中執(zhí)行。當(dāng)執(zhí)行結(jié)束后處理結(jié)果會通過一個異步的開放管道回送給aspnet_isapi,這時請求的狀態(tài)會被更新為“Done”。接著請求就會從隊列中清除。如果WP進(jìn)程崩潰,所有正在處理中的請求都將維持“executing”狀態(tài)一段時間,等到aspnet_isapi檢測到WP進(jìn)程死掉后,會自動丟棄所有的請求并釋放已經(jīng)分配的資源。
WP會分析每一個請求的信息解析出其中的虛擬目錄信息,并檢查該虛擬目錄對應(yīng)的AppDomain(應(yīng)用程序域)是否已經(jīng)存在,如果不存在,則創(chuàng)建一個新的AppDomain(ApplicationManager創(chuàng)建應(yīng)用程序域),然后使用它。否則直接重用已經(jīng)建立的AppDomain對象。這里的AppDomain指的是.NET中引入的應(yīng)用程序域的概念,程序集管理的最小邏輯單位為應(yīng)用程序域,包括四個重要的機(jī)制,隔離、卸載、安全、配置,它可以理解為一個進(jìn)程或一個邊界或一個容器,它是應(yīng)用程序的執(zhí)行環(huán)境.NET下所有的應(yīng)用程序都運行在AppDomain中,每一個ASP.NET應(yīng)用程序IIS中的站點或者虛擬目錄都會有一個AppDomain與之對應(yīng),它保存了Applcation對象、Cache等全局變量。
由一張流程圖回顧上述瀏覽器到達(dá)服務(wù)器的過程

ISAPIRuntme.ProcessRequest方法第一個進(jìn)入ASP.NET
當(dāng)aspnet_wp.exe接受到aspnet_isapi.dll的請求后,就將請求轉(zhuǎn)給指定虛擬目錄對應(yīng)的AppDomain中的ISAPIRuntime對象,ISAPIRuntime.ProcessRequest()開始進(jìn)入ASP.NET,并將瀏覽器發(fā)送請求消息封裝成HttpWorkerRequest類(抽象類,開發(fā)環(huán)境中對應(yīng)SimpleWorkRequest)。之后再執(zhí)行HttpRuntime的靜態(tài)方法:ProcessRequestNoDemand(參數(shù)為封裝了瀏覽器請求的信息:HttpWorkerRequest)
補(bǔ)充:默默無聞的工作者對象HttpWorkerRequest
在Asp.Net中,準(zhǔn)備用于處理的請求,必須封裝為HttpWorkerRequest類型的對象,這是一個抽象類:
[ComVisibleAttribute(false)] public abstract class HttpWorkerRequest
客戶的請求首先會被ISAPIRuntme對象ProcessRequest方法處理
創(chuàng)建了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調(diào)用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); } }該方法先從請求隊列中取出一個請求,然后更新請求的引用計數(shù)器的信息,然后ProcessRequestNow方法處理請求。
在這兒終于找到了HttpRuntime這個對象了:
internal static void ProcessRequestNow(HttpWorkerRequest wr)
{
_theRuntime.ProcessRequestInternal(wr);
}
|
_theRuntime就是HttpRuntime類型的對象,他在HttpRuntime的靜態(tài)構(gòu)造函數(shù)初始化。
|
點擊進(jìn)入ProcessRequsetNow(Wr)方法,Wr即封裝了HTTP Message的HttpWorkRequest對象
在HttpRuntime接受到請求后,立刻通過HttpWorkerRequest傳遞的參數(shù)進(jìn)行分析和分解,創(chuàng)建方便用戶網(wǎng)站應(yīng)用程序處理用的對象。HttpRequest,HttpResponse
終于發(fā)現(xiàn)了HttpContext,根據(jù)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;
}
}
......
}
}
|
在進(jìn)入看看:根據(jù)WR,初始化了請求參數(shù)的類型HttpRequest對象和處理回應(yīng)類型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這個方法很重要,前面分析了它創(chuàng)建了上下文對象HttpContext,接下來分析HttpApplication的創(chuàng)建。
{ ..... 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,然后調(diào)用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; } } } }
- 執(zhí)行InitResponseWrite創(chuàng)建HttpWrite對象,用于寫入結(jié)果返回信息。
- 創(chuàng)建HttpApplication實例,HttpApplicationFactory.GetApplicationInstance(注意其實不是這個方法直接創(chuàng)建,而是通過這個方法里面又調(diào)用了GetNormalApplicationInstance方法來創(chuàng)建默認(rèn)的HttpApplication實例)
- 那什么是HttpApplicationFactotry?
- HttpApplicationFactotry用于負(fù)責(zé)管理一個HttpApplication的對象池。
看一下HttpApplication這個類的申明:
[ToolboxItem(false)]
public class HttpApplication : IComponent, IDisposable, IHttpAsyncHandler, IHttpHandler, IRequestCompletedNotifier, ISyncContext
{}
調(diào)用HttpApplicationFactory對象的GetNormalApplicationInstance得到一個HttpApplication實例:
internal static IHttpHandler GetApplicationInstance(HttpContext context) { ......return _theApplicationFactory.GetNormalApplicationInstance(context); }
GetApplicationInstance方法生成一個默認(rèn)的HttpApplication對象,HttpApplication實現(xiàn)了IHttpAsyncHandler接口。
調(diào)用HttpApplication對象(實現(xiàn)了IHttpAsyncHandler接口)的BeginProcessRequest方法執(zhí)行客戶請求。
if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);
}
OK,回到前一步,再深入一步,進(jìn)入GetNormalApplicationInstance方法之后,我們看到了HttpApplication對象是如何被創(chuàng)建和初始化:
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)我們發(fā)現(xiàn)HttpApplication類提供了一個名為InitInternal的方法,調(diào)用它來完成HttpApplication實例的初始化工作,點擊進(jìn)入InitInternal方法內(nèi)部:
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));//---------------------->用于創(chuàng)建處理用戶請求的對象(Handler)
|
在HttpApplication對象初始化時,首先會調(diào)用InitModules方法來加載在web.config文件中配置的所有HttpModule模塊。
接著HookupEventHandlersForApplicationAndModules方法被調(diào)用,這個方法完成global.asax文件中配置的HttpModule或HttpApplication事件的綁定
最后ApplicationStopManager對象的BuildSteps方法被調(diào)用,完成HttpApplication19個管道事件的注冊。這個方法很重要,它將創(chuàng)建各種HttpApplication.IExecutionStep保存到一個數(shù)組列表:
internal override void BuildSteps(WaitCallback stepCallback) { ..... this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; steps.CopyTo(this._execSteps); ..... }
以便在BeginProcessRequest方法內(nèi)部調(diào)用ResumeSteps方法依次執(zhí)行這些對象的Execute()方法,完成各種處置。
調(diào)用BeginProcessRequest方法來實現(xiàn)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);//---------->依次執(zhí)行管道事件 return result; }
|
BeginProcessRequest執(zhí)行過程
|
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); } }
這兒調(diào)用了一個很重要的方法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; }
通過實現(xiàn)了IHttpHandlerFactory(PageHandlerFactory 或者 SimpleHandlerFactory等)創(chuàng)建了HttpHandler
因為steps.Add(new HttpApplication.MapHandlerExecutionStep(app))注冊了Handler,所以會在第八個事件里通過反射創(chuàng)建了頁面請求的對象(實現(xiàn)了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個事件之間,會調(diào)用了第八個事件創(chuàng)建的頁面對象的ProcessRequest方法,具體內(nèi)容詳看我下一篇文章:《ASP.NET那點不為人知的事(二)》
補(bǔ)充:BuildSteps方法里注冊的HttpApplication管道的19個事件:

19個事件的處理過程:
在Asp.Net中,Asp.Net服務(wù)器對于每一次請求的處理過程是相同的,都要經(jīng)過HttpApplication處理管道,管道內(nèi)部的處理過程是固定的,在服務(wù)器處理請求的各個階段,伴隨著處理的進(jìn)行,一次觸發(fā)對應(yīng)的事件,以便程序員在處理的各個階段完成自定義的處理工作。
首先觸發(fā)的事件是BeginRequest,這個事件標(biāo)志著ASP.NET服務(wù)器處理工作的開始,也是程序員在ASP.NET中針對請求能夠處理的第一個事件。
開始處理請求后,第一個重要的工作就是確定請求用戶的身份以及實現(xiàn)安全機(jī)制。這個工作通過AuthenticateRequest和PostAuthenticateRequest兩個事件提供檢查當(dāng)前請求用戶身份的機(jī)會。PostAuthenticateRequest則表示用戶身份已經(jīng)檢查完成,檢查后的用戶可以通過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屬性
|
當(dāng)ASP.NET獲取用戶身份后,根據(jù)當(dāng)前請求的用戶身份,開始請求權(quán)限的檢查工作。當(dāng)?shù)谒膫€事件AuthorizeRequest觸發(fā)的時候開始進(jìn)行用戶的權(quán)限檢查,而第五個事件PostAuthorizeRequest則標(biāo)志已經(jīng)完成用戶權(quán)限檢查工作。如果用戶沒有通過安檢,一般情況下將跳過剩余事件,直接觸發(fā)EndRequest事件結(jié)束處理請求過程。
當(dāng)用戶獲取了請求權(quán)限,那么服務(wù)器開始準(zhǔn)備用最快的方式來使用戶得到回應(yīng)結(jié)果。ResolveRequestCache事件標(biāo)志著到從前緩存的結(jié)果進(jìn)行檢查,看看是否可以直接從以前的緩存結(jié)果中直接獲取處理結(jié)果,PostResolveRequestCache表示緩存檢查結(jié)束。
當(dāng)不能從緩存中獲取結(jié)果時,必須通過一次處理來計算出當(dāng)前請求的結(jié)果。在ASP.NET中,用戶處理請求以得到結(jié)果的對象稱為處理程序Handler。為了處理這個這個請求,ASP.NET必須按照匹配規(guī)則找到一個處理當(dāng)前請求的處理程序,PostMapRequestHandler事件表示當(dāng)前ASP.NET已經(jīng)獲取了這個處理程序,HttpContext的Handler屬性就表示這個處理程序?qū)ο蟆?/strong>
得到了處理程序之后,還不能馬上開始進(jìn)行處理,這是由于處理請求還需要與這個請求有關(guān)的數(shù)據(jù),比如說這個用戶上一次向服務(wù)器發(fā)送請求的時候,在服務(wù)器上報錯了一些這個用戶特有的數(shù)據(jù)。由于HTTP協(xié)議的無狀態(tài)性,狀態(tài)管理問題是個核心問題,所以ASP時代就引入Session,提供基于會話狀態(tài)的管理。為了獲取這個用戶在以前保存的數(shù)據(jù),通過AcquireRequestState事件取得請求狀態(tài),PostAcquireRequest事件則表示已經(jīng)完成了用戶數(shù)據(jù)的獲取工作,可以再處理中使用了。
PreRequestHandlerExcute事件用來通知程序員,處理程序就要開始進(jìn)行處理工作了,如果用戶的狀態(tài)已經(jīng)獲取之后,還有需要的處理程序之進(jìn)行的工作,那么就在這個事件中處理吧。在PreRequestHandlerExcute事件之后,ASP.NET服務(wù)器將通過執(zhí)行處理程序完成請求處理工作。這個處理程序有可能是一個WebForm,也可能是Web服務(wù)。這個工作是在第11個事件和第12個事件之間完成的。
處理程序之后,服務(wù)器開始進(jìn)行掃尾工作,PostRequestHandlerExcute事件通知程序員,ASP.NET服務(wù)器處理程序已經(jīng)完成。
在處理完成之后,由于處理程中,用戶可能修改了用于特定的專屬數(shù)據(jù),那么修改之后的用戶狀態(tài)數(shù)據(jù)需要進(jìn)行序列化或者進(jìn)行保存處理。ReleaseRequestState事件通知程序員需要釋放這些狀態(tài)數(shù)據(jù),PostReleaseRequestState則表示已經(jīng)釋放完成。
在處理完成之后,如果需要將這次處理結(jié)果緩存起來,以便于后繼的請求可以直接使用這個結(jié)果,UpdateRequestCache事件提供了處理的機(jī)會,PostUpdateRequestCache則表示緩存已經(jīng)更新完畢。
在ASP.NET4.0中,新增加了兩個事件完成處理的日志工作:LogRequest表示將這次請求加入日志,PostLogRequest表示完成了日志工作。
在前面的事件中,請求并不一定要經(jīng)過所有的事件,比如說,用戶沒用經(jīng)過授權(quán)的檢查,那么將跳過后面的事件,但是,EndRequest事件是所有請求都要經(jīng)過的最后一個HttpApplication處理管道的事件,也是程序員處理的ASP.NET處理請求中的最后一個機(jī)會。這個事件之后,處理的結(jié)果將被回應(yīng)到瀏覽器,完成ASP.NET服務(wù)器的處理工作。
小結(jié)
未完,待續(xù)。

浙公網(wǎng)安備 33010602011771號