我們上網時,在瀏覽器地址輸入網址:Http://www.cnblogs.com,按下回車,一張網頁就呈現在我們眼前。這究竟發生了什么?對於一名優秀的Programmer來說,我想有必要一下熟悉瀏覽器--->服務器請求的過程。
ASP.NET
ASP.NET是運行在公共語言運行時刻時(CLR)上的應用程序框架。他用來在服務器端構建功能強大的web應用程序。當瀏覽器請求 ASP.NET 文件時,IIS 會把該請求傳遞給服務器上的 ASP.NET 引擎,ASP.NET 引擎會逐行地讀取該文件,並執行文件中的腳本,最后,ASP.NET 文件會以純 HTML 的形式返回瀏覽器。
客戶端瀏覽器和服務器之間的請求響應是通過Socket進行通信,基於HTTP協議,客戶端發送一次HTTP請求,服務器接收到請求,處理之后向瀏覽器回應響應報文。那么什么是HTTP協議呢?
HTTP協議:
當瀏覽器尋找到Web服務器地址后,瀏覽器將幫助我們把對服務器的請求轉換為一系列參數(消息)發給Web服務器,瀏覽器和Web服務器的對話中,需要使用雙方都能理解語法規范進行通信,這種程序之間進行通信的語法規定,我們稱之為協議。瀏覽器與服務器之間的協議是應用層協議,當前遵循的協議是HTTP/1.1。HTTP/1.1協議時Web開發的基礎,這是一個無狀態協議,客戶端瀏覽器和服務器通過Socket通信進行請求和響應完成一次會話。每次會話中,通信雙方發送的數據稱為消息,分為兩種:請求消息和響應消息。
對於消息而言,一般他有三部分組成,並且消息的頭和消息體之間用一個空行進行分隔:
我們通過瀏覽器插件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的靜態構造函數初始化。
|
點擊進入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)
|
在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執行過程
|
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屬性
|
當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服務器的處理工作。
小結
未完,待續。