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 許可協議。轉載請註明出處!