Agenda
前言: 附上Asp.net執行請求流程圖.
上一篇文章分享HttpApplicationFactory.GetApplicationInstance方法返回一個HttpApplication給HttpRuntime來呼叫使用.
今天開始介紹HttpApplication這個很重要的類別,它可謂是我們Asp.net中很複雜但重要的類別
Global.cs是繼承HttpApplication類別,但為什麼需要繼承這個類別呢? 讓我們繼續看下去.
查看原始碼好站 Reference Source 此文的程式碼比較多我會在原始碼上邊上說明相對應編號方便大家觀看
初始化HttpApplication (InitInternal) 在GetNormalApplicationInstance返回一個HttpApplication物件前會呼叫初始化HttpApplication.InitInternal方法
這個方法主要做下面幾件事情
初始化HttpModule,讀取Host config或appconfig 註冊的HttpMoudle,並調用Init方法,使用AOP編成方式註冊使用事件
提供一個Hock給繼承Application物件來初始化設定使用
判斷要走管道模式 還是經典模式
建置Pipleline流程
建立許多實現IExecutionStep接口的物件並添加到目前HttpApplication物驗的_execSteps集合中.從這裡我們可以看到HttpApplication是以異步的方式處理請求
HttpModule是在InitInternal方法中被讀取執行.
我們可以透過 HttpContext.ApplicationInstance.Modules ,得知目前所有載入HttpModule.
下面是InitInternal原始碼(核心動作有寫中文註解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 internal void InitInternal (HttpContext context, HttpApplicationState state, MethodInfo[] handlers ) { _state = state; PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES); try { try { _initContext = context; _initContext.ApplicationInstance = this ; context.ConfigurationPath = context.Request.ApplicationPathObject; using (new DisposableHttpContextWrapper(context)) { if (HttpRuntime.UseIntegratedPipeline) { try { context.HideRequestResponse = true ; _hideRequestResponse = true ; InitIntegratedModules(); } finally { context.HideRequestResponse = false ; _hideRequestResponse = false ; } } else { InitModules(); } if (handlers != null ) HookupEventHandlersForApplicationAndModules(handlers); _context = context; if (HttpRuntime.UseIntegratedPipeline && _context != null ) { _context.HideRequestResponse = true ; } _hideRequestResponse = true ; try { Init(); } catch (Exception e) { RecordError(e); } } if (HttpRuntime.UseIntegratedPipeline && _context != null ) { _context.HideRequestResponse = false ; } _hideRequestResponse = false ; _context = null ; _resumeStepsWaitCallback= new WaitCallback(this .ResumeStepsWaitCallback); if (HttpRuntime.UseIntegratedPipeline) { _stepManager = new PipelineStepManager(this ); } else { _stepManager = new ApplicationStepManager(this ); } _stepManager.BuildSteps(_resumeStepsWaitCallback); } finally { _initInternalCompleted = true ; context.ConfigurationPath = null ; _initContext.ApplicationInstance = null ; _initContext = null ; } } catch { throw ; } }
載入所有註冊HttpModule(InitModules方法) 這個方法讀取註冊的HttpModule並共同放在一起,在一起呼叫InitModulesCommon方法來呼叫所有Modules的Init方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private void InitModules () { HttpModulesSection pconfig = RuntimeConfig.GetAppConfig().HttpModules; HttpModuleCollection moduleCollection = pconfig.CreateModules(); HttpModuleCollection dynamicModules = CreateDynamicModules(); moduleCollection.AppendCollection(dynamicModules); _moduleCollection = moduleCollection; InitModulesCommon(); } private void InitModulesCommon () { int n = _moduleCollection.Count; for (int i = 0 ; i < n; i++) { _currentModuleCollectionKey = _moduleCollection.GetKey(i); _moduleCollection[i].Init(this ); } _currentModuleCollectionKey = null ; InitAppLevelCulture(); }
_moduleCollection[i].Init(this); 其中的this就是把HttpApplication物件本身傳入這也是為什麼我們繼承IHttpMoudel介面可以共同使用同一個HttpApplication物件.
1 2 3 4 5 6 7 public interface IHttpModule { void Init (HttpApplication context ) ; void Dispose () ; }
上面呼叫的就是void Init(HttpApplication context)方法.
如果要取得目前所註冊HttpModule可透過HttpApplication.Modules屬性
HttpModule添加Asp.net事件原理解析. 我們在HttpModule上10多個事件作擴充在ASP.net是如何完成呢?
首先我們來看看事件方法 原始碼.
發現每個事件都會呼叫AddSyncEventHookup方法來建立事件,此方法有幾個參數
object key:此事件識別資訊(每個事件都有自己的Object),如BeginRequest事件傳入EventBeginRequest物件.
Delegate handler:使用者撰寫事件方法.
RequestNotification notification:屬於哪種分群.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public event EventHandler BeginRequest { add { AddSyncEventHookup(EventBeginRequest, value , RequestNotification.BeginRequest); } remove { RemoveSyncEventHookup(EventBeginRequest, value , RequestNotification.BeginRequest); } } public event EventHandler AuthenticateRequest { add { AddSyncEventHookup(EventAuthenticateRequest, value , RequestNotification.AuthenticateRequest); } remove { RemoveSyncEventHookup(EventAuthenticateRequest, value , RequestNotification.AuthenticateRequest); } } internal event EventHandler DefaultAuthentication { add { AddSyncEventHookup(EventDefaultAuthentication, value , RequestNotification.AuthenticateRequest); } remove { RemoveSyncEventHookup(EventDefaultAuthentication, value , RequestNotification.AuthenticateRequest); } } private void AddSyncEventHookup (object key, Delegate handler, RequestNotification notification, bool isPostNotification = false ) { ThrowIfEventBindingDisallowed(); Events.AddHandler(key, handler); if (IsContainerInitalizationAllowed) { PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey); if (container != null ) { SyncEventExecutionStep step = new SyncEventExecutionStep(this , (EventHandler)handler); container.AddEvent(notification, isPostNotification, step); } } }
上面AddSyncEventHookup傳入object key在Httpapplication物件在一開始就會建立下面這些靜態方法(當作每個事件Key)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private static readonly object EventDisposed = new object ();private static readonly object EventErrorRecorded = new object ();private static readonly object EventRequestCompleted = new object ();private static readonly object EventPreSendRequestHeaders = new object ();private static readonly object EventPreSendRequestContent = new object ();private static readonly object EventBeginRequest = new object ();private static readonly object EventAuthenticateRequest = new object ();private static readonly object EventDefaultAuthentication = new object ();private static readonly object EventPostAuthenticateRequest = new object ();private static readonly object EventAuthorizeRequest = new object ();private static readonly object EventPostAuthorizeRequest = new object ();private static readonly object EventResolveRequestCache = new object ();private static readonly object EventPostResolveRequestCache = new object ();private static readonly object EventMapRequestHandler = new object ();private static readonly object EventPostMapRequestHandler = new object ();private static readonly object EventAcquireRequestState = new object ();private static readonly object EventPostAcquireRequestState = new object ();private static readonly object EventPreRequestHandlerExecute = new object ();private static readonly object EventPostRequestHandlerExecute = new object ();private static readonly object EventReleaseRequestState = new object ();private static readonly object EventPostReleaseRequestState = new object ();private static readonly object EventUpdateRequestCache = new object ();private static readonly object EventPostUpdateRequestCache = new object ();private static readonly object EventLogRequest = new object ();private static readonly object EventPostLogRequest = new object ();private static readonly object EventEndRequest = new object ();
最後把事件資訊添加到Events集合中,已便建立管道時使用.
1 2 3 4 5 6 7 8 9 10 11 protected EventHandlerList Events { get { if (_events == null ) { _events = new EventHandlerList(); } return _events; } }
透過上面機制就可以確保對於Events取得事件時順序.
管道模式 vs 經典模式 下面兩張圖是管道模式 和經典模式
經典模式
管道模式
圖片來源
除了執行流程不一樣跟一些差異外,他們最終還是為了要找到一個HttpHandler來執行.
取得執行HttpHandler物件 如果有認真看原始碼的小夥伴,會發現HttpApplication的ProcessRequest目前是throw 一個錯誤.
那他是怎麼找到使用HttpHandler物件並完成請求的呢?
1 2 3 void IHttpHandler.ProcessRequest(HttpContext context) { throw new HttpException(SR.GetString(SR.Sync_not_supported)); }
因為HttpRunTime是呼叫異步請求BeginProcessRequest方法.
這邊提一下 啟動吧!Asp.Net IsapiRunTime & HttpRuntime 會先判斷app物件是否實現IHttpAsyncHandler.
HttpApplication有實現IHttpAsyncHandler介面.所以優先執行異步請求.
1 2 3 4 5 6 7 8 9 10 11 if (app is IHttpAsyncHandler) { IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app; context.AsyncAppHandler = asyncHandler; asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context); } else { app.ProcessRequest(context); FinishRequest(context.WorkerRequest, context, null ); }
小結 今天我們學到
HttpApplication去讀取所有註冊的HttpModule並呼叫他們的Init方法.
經典模式 和管道模式 除了執行流程不同最終目標還是找尋一個HttpHandler
HttpRunTime是呼叫異步請求
了解HttpModule添加Asp.net事件原理解析
很多文章都會提到10多個事件(BeginRequest, EndRequest…..等)
下篇會介紹StepManager如何建立管道和如何呼叫事件並找尋HttpHandler來執行.
__此文作者__:Daniel Shih(石頭) __此文地址__: https://isdaniel.github.io/Ithelp-day5/ __版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!