前言
前面有提到InitInternal
方法,是如何載入註冊HttpModule
並呼叫Init
方法,經典模式和管道模式比較.
查看原始碼好站 Reference Source
此文的程式碼比較多我會在原始碼上邊上說明相對應編號方便大家觀看
今天跟大家介紹StepManager
是如何建立管道和依序呼叫IHttpModule
註冊事件
ApplicationStepManager
這部分可說是Asp.net
最核心部分,利用Event
事件和AOP
概念,讓Asp.net
可以擁有高度的可擴展性.
BuildSteps 建置Pipleline流程
BuildSteps
最主要透過CreateEventExecutionSteps
方法,把所有Applicationevent
註冊添加到steps
集合中方便後面依照順序去呼叫使用.
steps
最後把載入所有事件給_execSteps
這裡就是我們熟知的管道事件介紹 Asp.Net支柱 IHttpMoudle & IHttphandler 有介紹到
透過BuildSteps
方法step by step把Asp.net
執行事件依照順序註冊進去.
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)); // map handler
app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
steps.Add(app.CreateImplicitAsyncPreloadExecutionStep()); // implict async preload step
steps.Add(new CallHandlerExecutionStep(app)); // execute handler
app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
steps.Add(new CallFilterExecutionStep(app)); // filtering
app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
_endRequestStepIndex = steps.Count;
app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
steps.Add(new NoopExecutionStep()); // the last is always there
_execSteps = new IExecutionStep[steps.Count];
steps.CopyTo(_execSteps);
}
如果在
Web.Config
設定urlMappingsEnabled
就會實施UrlMappingsModule.UrlMappingRewritePath
(UrlRerwite)MapHandlerExecutionStep
:找尋匹配HttpHandler
物件
CreateEventExecutionSteps 載入事件
下面程式碼可以看到CreateEventExecutionSteps
方法透過eventIndex
去事件集合查找註冊事件,並把事件寫入ArrayList steps
集合中.
private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) {
// async
AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
if (asyncHandler != null) {
asyncHandler.CreateExecutionSteps(this, steps);
}
// sync
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
集合中
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
非同步使用事件.
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
:屬於哪種分群.
public event EventHandler PostMapRequestHandler {
add { AddSyncEventHookup(EventPostMapRequestHandler, value, RequestNotification.MapRequestHandler, true); }
remove { RemoveSyncEventHookup(EventPostMapRequestHandler, value, RequestNotification.MapRequestHandler); }
}
其他10幾個事件使用方式大同小異,這裡就不一一介紹了
IExecutionStep介面
IExecutionStep
這個介面,裡面最重要的方法是void Execute();
來執行注冊的事件方法.
// interface to represent one execution step
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
方法
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 (; ; ) {
// record error
if (syncContext.Error != null) {
error = syncContext.Error;
syncContext.ClearError();
}
if (error != null) {
app.RecordError(error);
error = null;
}
// check for any outstanding async operations
if (syncContext.PendingCompletion(_resumeStepsWaitCallback)) {
// wait until all pending async operations complete
break;
}
// advance to next step
if (_currentStepIndex < _endRequestStepIndex && (context.Error != null || _requestCompleted)) {
// end request
context.Response.FilterOutput();
_currentStepIndex = _endRequestStepIndex;
}
else {
_currentStepIndex++;
}
if (_currentStepIndex >= _execSteps.Length) {
appCompleted = true;
break;
}
// execute the current step
_numStepCalls++; // count all calls
// enable launching async operations before each new step
syncContext.Enable();
// call to execute current step catching thread abort exception
error = app.ExecuteStep(_execSteps[_currentStepIndex], ref stepCompletedSynchronously);
// unwind the stack in the async case
if (!stepCompletedSynchronously)
break;
_numSyncStepCalls++; // count synchronous calls
}
}
finally {
if (appCompleted) {
// need to raise OnRequestCompleted while within the ThreadContext so that things like User, CurrentCulture, etc. are available
context.RaiseOnRequestCompleted();
}
if (threadContext != null) {
try {
threadContext.DisassociateFromCurrentThread();
}
catch {
}
}
}
}
catch { // Protect against exception filters
throw;
}
} // using
if (appCompleted) {
context.RaiseOnPipelineCompleted();
context.Unroot();
app.AsyncResult.Complete((_numStepCalls == _numSyncStepCalls), null, null);
app.ReleaseAppInstance();
}
}
finally {
if (appInstanceConsumersCounter != null) {
appInstanceConsumersCounter.MarkOperationCompleted(); // ResumeSteps call complete
}
}
}
上面程式碼最核心的片段在
if (_currentStepIndex < _endRequestStepIndex && (context.Error != null || _requestCompleted)) {
context.Response.FilterOutput();
_currentStepIndex = _endRequestStepIndex;
}
else {
_currentStepIndex++;
}
if (_currentStepIndex >= _execSteps.Length) {
appCompleted = true;
break;
}
// execute the current step
_numStepCalls++;
// enable launching async operations before each new step
syncContext.Enable();
// call to execute current step catching thread abort exception
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();
internal Exception ExecuteStep(IExecutionStep step, ref bool completedSynchronously) {
Exception error = null;
try {
try {
if (step.IsCancellable) {
_context.BeginCancellablePeriod(); // request can be cancelled from this point
try {
ExecuteStepImpl(step);
}
finally {
_context.EndCancellablePeriod(); // request can be cancelled until this point
}
_context.WaitForExceptionIfCancelled(); // wait outside of finally
}
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) {
// Timed out
error = new HttpException(SR.GetString(SR.Request_timed_out),
null, WebEventCodes.RuntimeErrorRequestAbort);
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TIMED_OUT);
}
else {
// Response.End
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
編成),值得讓我們思考且學習.
如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^