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方法.經典模式 和管道模式 除了執行流程不同最終目標還是找尋一個HttpHandlerHttpRunTime是呼叫異步請求了解HttpModule添加Asp.net事件原理解析 
 
很多文章都會提到10多個事件(BeginRequest, EndRequest…..等)
下篇會介紹StepManager如何建立管道和如何呼叫事件並找尋HttpHandler來執行.
__此文作者__:Daniel Shih(石頭)https://isdaniel.github.io/Ithelp-day5/  CC BY-NC-SA 3.0 TW  許可協議。轉載請註明出處!