Agenda
前言 前面有提到InitInternal
方法,是如何載入註冊HttpModule
並呼叫Init
方法,經典模式和管道模式比較.
查看原始碼好站 Reference Source 此文的程式碼比較多我會在原始碼上邊上說明相對應編號方便大家觀看
今天跟大家介紹StepManager
是如何建立管道和依序呼叫IHttpModule
註冊事件
ApplicationStepManager 這部分可說是Asp.net
最核心部分,利用Event
事件和AOP
概念,讓Asp.net
可以擁有高度的可擴展性.
BuildSteps 建置Pipleline流程 BuildSteps
最主要透過CreateEventExecutionSteps
方法,把所有Application event
註冊添加到steps
集合中方便後面依照順序去呼叫使用.
steps
最後把載入所有事件給 _execSteps
這裡就是我們熟知的管道事件介紹 Asp.Net支柱 IHttpMoudle & IHttphandler 有介紹到
透過BuildSteps
方法step by step 把Asp.net
執行事件依照順序註冊進去.
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 internal override void BuildSteps (WaitCallback stepCallback ) { ArrayList steps = new ArrayList(); HttpApplication app = _application; steps.Add(new ValidateRequestExecutionStep(app)); steps.Add(new ValidatePathExecutionStep(app)); if (urlMappingsEnabled) steps.Add(new 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 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(new CallHandlerExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); steps.Add(new CallFilterExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); _endRequestStepIndex = steps.Count; app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); steps.Add(new NoopExecutionStep()); _execSteps = new IExecutionStep[steps.Count]; steps.CopyTo(_execSteps); }
如果在Web.Config
設定urlMappingsEnabled
就會實施UrlMappingsModule.UrlMappingRewritePath
(UrlRerwite)MapHandlerExecutionStep
:找尋匹配HttpHandler
物件
CreateEventExecutionSteps 載入事件 下面程式碼可以看到CreateEventExecutionSteps
方法透過eventIndex
去事件集合 查找註冊事件,並把事件寫入ArrayList steps
集合中.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void CreateEventExecutionSteps (Object eventIndex, ArrayList steps ) { AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex]; if (asyncHandler != null ) { asyncHandler.CreateExecutionSteps(this , steps); } EventHandler handler = (EventHandler)Events[eventIndex]; if (handler != null ) { Delegate[] handlers = handler.GetInvocationList(); for (int i = 0 ; i < handlers.Length; i++) { steps.Add(new SyncEventExecutionStep(this , (EventHandler)handlers[i])); } } }
為了建立管道最後可看到steps.CopyTo(_execSteps);
把建立的管道Step
複製到_execSteps
集合中
1 2 3 4 5 6 7 8 private IExecutionStep[] _execSteps;internal override void BuildSteps (WaitCallback stepCallback ) { ArrayList steps = new ArrayList(); _execSteps = new IExecutionStep[steps.Count]; steps.CopyTo(_execSteps); }
steps
最後把所有註冊事件Copy
到ApplicationStepManager
的private IExecutionStep[] _execSteps
集合中提供ResumeSteps
方法呼叫使用.
HttpApplication事件集合 這兩個欄位集合乘載我們註冊的Asp.net
事件
EventHandlerList
同步使用事件.
AsyncAppEventHandlersTable
非同步使用事件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private EventHandlerList _events;protected EventHandlerList Events { get { if (_events == null ) { _events = new EventHandlerList(); } return _events; } } private AsyncAppEventHandlersTable _asyncEvents;private AsyncAppEventHandlersTable AsyncEvents { get { if (_asyncEvents == null ) _asyncEvents = new AsyncAppEventHandlersTable(); return _asyncEvents; } }
用其中一個事件舉例
PostMapRequestHandler
提供擴充的事件註冊點,透過AddSyncEventHookup
把事件加入集合中.
object key
:此事件識別資訊(每個事件都有自己的Object),如PostMapRequestHandler
事件傳入EventPostMapRequestHandler
物件.
Delegate handler
:使用者撰寫事件方法.
RequestNotification notification
:屬於哪種分群.
1 2 3 4 public event EventHandler PostMapRequestHandler { add { AddSyncEventHookup(EventPostMapRequestHandler, value , RequestNotification.MapRequestHandler, true ); } remove { RemoveSyncEventHookup(EventPostMapRequestHandler, value , RequestNotification.MapRequestHandler); } }
其他10幾個事件使用方式大同小異,這裡就不一一介紹了
IExecutionStep介面 IExecutionStep
這個介面,裡面最重要的方法是void Execute();
來執行注冊的事件方法.
1 2 3 4 5 6 internal interface IExecutionStep { void Execute () ; bool CompletedSynchronously { get ;} bool IsCancellable { get ; } }
在BuildSteps
方法中可以看到,全部事件轉成IExecutionStep
介面放入_execSteps
待被執行IExecutionStep
集合列表.
_execSteps
是一個物件區域變數,提供internal override void ResumeSteps(Exception error)
呼叫使用.
ResumeSteps方法呼叫IExecutionStep物件 ResumeSteps
這個方法做了許多事情,我下面只保留ResumeSteps
方法如何去呼叫IExecutionStep
物件的Execute
方法
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 private int _currentStepIndex; [System.Diagnostics.DebuggerStepperBoundaryAttribute ] internal override void ResumeSteps (Exception error ) { bool appCompleted = false ; bool stepCompletedSynchronously = true ; HttpApplication app = _application; CountdownTask appInstanceConsumersCounter = app.ApplicationInstanceConsumersCounter; HttpContext context = app.Context; ThreadContext threadContext = null ; AspNetSynchronizationContextBase syncContext = context.SyncContext; try { using (syncContext.AcquireThreadLock()) { try { threadContext = app.OnThreadEnter(); } catch (Exception e) { if (error == null ) error = e; } try { try { for (; ; ) { if (syncContext.Error != null ) { error = syncContext.Error; syncContext.ClearError(); } if (error != null ) { app.RecordError(error); error = null ; } if (syncContext.PendingCompletion(_resumeStepsWaitCallback)) { break ; } if (_currentStepIndex < _endRequestStepIndex && (context.Error != null || _requestCompleted)) { context.Response.FilterOutput(); _currentStepIndex = _endRequestStepIndex; } else { _currentStepIndex++; } if (_currentStepIndex >= _execSteps.Length) { appCompleted = true ; break ; } _numStepCalls++; syncContext.Enable(); error = app.ExecuteStep(_execSteps[_currentStepIndex], ref stepCompletedSynchronously); if (!stepCompletedSynchronously) break ; _numSyncStepCalls++; } } finally { if (appCompleted) { context.RaiseOnRequestCompleted(); } if (threadContext != null ) { try { threadContext.DisassociateFromCurrentThread(); } catch { } } } } catch { throw ; } } if (appCompleted) { context.RaiseOnPipelineCompleted(); context.Unroot(); app.AsyncResult.Complete((_numStepCalls == _numSyncStepCalls), null , null ); app.ReleaseAppInstance(); } } finally { if (appInstanceConsumersCounter != null ) { appInstanceConsumersCounter.MarkOperationCompleted(); } } }
上面程式碼最核心的片段在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 if (_currentStepIndex < _endRequestStepIndex && (context.Error != null || _requestCompleted)) { context.Response.FilterOutput(); _currentStepIndex = _endRequestStepIndex; } else { _currentStepIndex++; } if (_currentStepIndex >= _execSteps.Length) { appCompleted = true ; break ; } _numStepCalls++; syncContext.Enable(); error = app.ExecuteStep(_execSteps[_currentStepIndex], ref stepCompletedSynchronously);
_currentStepIndex
這個欄位表示取得當前需要跑事件Index
(從之前IExecutionStep[]
集合取得),每次執完都會_currentStepIndex++
有一個無限迴圈for (; ; )
一直在跑除非兩種情況才會終止迴圈.
_currentStepIndex >= _execSteps.Length
代表全部事件都跑完了.
判斷context.Error != null
執行得過程是否有出錯,如果有就終止繼續執行.
透過HttpApplication.ExecuteStep
方法執行前面註冊的事件
bool appCompleted
來判斷目前是否執行完全部事件.
只要還有事件就呼叫ExecuteStep
,把當前事件傳入(_execSteps[_currentStepIndex]
)
HttpApplication的ExecuteStep 這邊蠻有趣一件事情是ExecuteStep
方法回傳一個Exception
物件當作這次執行成功或失敗,而ExecuteStep
執行過程是主要是呼叫ExecuteStepImpl
方法來呼叫step.Execute();
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 internal Exception ExecuteStep (IExecutionStep step, ref bool completedSynchronously ) { Exception error = null ; try { try { if (step.IsCancellable) { _context.BeginCancellablePeriod(); try { ExecuteStepImpl(step); } finally { _context.EndCancellablePeriod(); } _context.WaitForExceptionIfCancelled(); } else { ExecuteStepImpl(step); } if (!step.CompletedSynchronously) { completedSynchronously = false ; return null ; } } catch (Exception e) { error = e; if (e is ThreadAbortException && ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) == 0 )) { error = null ; _stepManager.CompleteRequest(); } } } catch (ThreadAbortException e) { if (e.ExceptionState != null && e.ExceptionState is CancelModuleException) { CancelModuleException cancelException = (CancelModuleException)e.ExceptionState; if (cancelException.Timeout) { error = new HttpException(SR.GetString(SR.Request_timed_out), null , WebEventCodes.RuntimeErrorRequestAbort); PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TIMED_OUT); } else { error = null ; _stepManager.CompleteRequest(); } Thread.ResetAbort(); } } completedSynchronously = true ; return error; } private void ExecuteStepImpl (IExecutionStep step ) { if (_stepInvoker != null ) { bool stepCalled = false ; _stepInvoker.Invoke(() => { if (!stepCalled) { stepCalled = true ; step.Execute(); } }); if (!stepCalled) { step.Execute(); } } else { step.Execute(); } }
重要的兩個IExecutionStep
MapHandlerExecutionStep
:透過HttpApplication.MapHttpHandler
方法取得使用HttpHandler
(透過IHttpHandlerFactory
和XML註冊表來完成)
CallHandlerExecutionStep
:取得使用HttpHandler
依照非同步或同步HttpHandler
執行相對應呼叫(先判斷是否非同步)
小結: 今天我們了解到
Appliaction
管道是如何被建立(透過BuildSteps
方法)依照Asp.net
順序註冊事件
所有事件被封裝到繼承IExecutionStep
物件中
透過呼叫ResumeSteps
方法來依序執行註冊事件.
下篇會跟大家詳細分享重要的兩個IExecutionStep
物件
MapHandlerExecutionStep
CallHandlerExecutionStep
微軟管道設計(透過事件)讓程式開發人員提供高擴展設計方式(AOP
編成),值得讓我們思考且學習.
__此文作者__:Daniel Shih(石頭) __此文地址__: https://isdaniel.github.io/ithelp-day6/ __版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!