Agenda
前言 前面和大家分享StepManager
是如何建立管道和依序呼叫IHttpModule
註冊事件
查看原始碼好站 Reference Source
此文的程式碼比較多我會在原始碼上邊上說明相對應編號方便大家觀看
今天跟大家分享HttpAppliaction
是如何找到要執行的IHttpHandler
物件.
呼叫HttpAppliaction取得HttpHandler並呼叫 在ApplicationStepManager
的IExecutionStep
中重要的實現類別有兩個
MapHandlerExecutionStep
:找到執行IHttpHander
CallHandlerExecutionStep
MapHandlerExecutionStep程式碼解說 前面說過IExecutionStep
最核心就是要找到一個Execute
方法
MapHandlerExecutionStep
的Execute
方法是為了找到一個要執行的HttpHander
每次請求都會呼叫HttpContext.Handler
屬性.
MapHttpHandler
會依照下面權重來取得HttpHander
物件.
context.RemapHandlerInstance
如果有物件就優先返回(很重要因為這就是Asp.net MVC使用的HttpHander
物件)
透過IHttpHandlerFactory
工廠來取得物件,依照我們在Config
註冊的HttpHander
對應資料
副檔名*.ashx
泛型處理常式透過SimpleHandlerFactory
副檔名*.aspx
泛型處理常式透過PageHandlerFactory
想知道更多可以查看applicationhost.config
註冊表
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 internal class MapHandlerExecutionStep : IExecutionStep { void IExecutionStep.Execute() { HttpContext context = _application.Context; HttpRequest request = context.Request; context.Handler = _application.MapHttpHandler( context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false ); } } internal IHttpHandler MapHttpHandler (HttpContext context, String requestType, VirtualPath path, String pathTranslated, bool useAppConfig ) { IHttpHandler handler = (context.ServerExecuteDepth == 0 ) ? context.RemapHandlerInstance : null ; using (new ApplicationImpersonationContext()) { if (handler != null ){ return handler; } HttpHandlerAction mapping = GetHandlerMapping(context, requestType, path, useAppConfig); if (mapping == null ) { PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND); PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED); throw new HttpException(SR.GetString(SR.Http_handler_not_found_for_request_type, requestType)); } IHttpHandlerFactory factory = GetFactory(mapping); try { IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2; if (factory2 != null ) { handler = factory2.GetHandler(context, requestType, path, pathTranslated); } else { handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated); } } catch (FileNotFoundException e) { } if (_handlerRecycleList == null ) _handlerRecycleList = new ArrayList(); _handlerRecycleList.Add(new HandlerWithFactory(handler, factory)); } return handler; }
MapHandlerExecutionStep
是為了找到我們要執行的HttpHandler
物件.
1 2 3 if (handler != null ){ return handler; }
在一開始先判斷handler
是否已經有值如果有就直接返回(這個很重要因為這是為什麼MVC
,WebAPI
可以運作且不用在Config
設定配對IHttpHandlerFactory
原因).
只需要在MapHandlerExecutionStep
執行前將context.RemapHandlerInstance
給一個HttpHandler
物件即可.
CallHandlerExecutionStep程式碼解說 CallHandlerExecutionStep
物件透過context.Handler
可以找到要執行的HttpHandler
,這邊也是優先判斷是否可執行異步請求.
異步呼叫beginProcessRequestDelegate
方法(此方法將實現IHttpAsyncHandler
物件封裝成一個Func<T, AsyncCallback, object, IAsyncResult>
委派方法),之後再調用返回一個IAsyncResult
物件(處理後結果最後呼叫EndProcessRequest
方法).
同步呼叫ProcessRequest
:判斷context.Handler
不是IHttpAsyncHandler
型別就值型同步動作
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 internal class CallHandlerExecutionStep : IExecutionStep { private HttpApplication _application; private AsyncCallback _completionCallback; private IHttpAsyncHandler _handler; private AsyncStepCompletionInfo _asyncStepCompletionInfo; private bool _sync; internal CallHandlerExecutionStep (HttpApplication app ) { _application = app; _completionCallback = new AsyncCallback(this .OnAsyncHandlerCompletion); } void IExecutionStep.Execute() { HttpContext context = _application.Context; IHttpHandler handler = context.Handler; if (handler != null && HttpRuntime.UseIntegratedPipeline) { IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest; if (wr != null && wr.IsHandlerExecutionDenied()) { _sync = true ; HttpException error = new HttpException(403 , SR.GetString(SR.Handler_access_denied)); error.SetFormatter(new PageForbiddenErrorFormatter(context.Request.Path, SR.GetString(SR.Handler_access_denied))); throw error; } } if (handler == null ) { _sync = true ; } else if (handler is IHttpAsyncHandler) { IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)handler; _sync = false ; _handler = asyncHandler; var beginProcessRequestDelegate = AppVerifier.WrapBeginMethod<HttpContext>(_application, asyncHandler.BeginProcessRequest); _asyncStepCompletionInfo.Reset(); context.SyncContext.AllowVoidAsyncOperations(); IAsyncResult ar; try { ar = beginProcessRequestDelegate(context, _completionCallback, null ); } catch { context.SyncContext.ProhibitVoidAsyncOperations(); throw ; } bool operationCompleted; bool mustCallEndHandler; _asyncStepCompletionInfo.RegisterBeginUnwound(ar, out operationCompleted, out mustCallEndHandler); if (operationCompleted) { _sync = true ; _handler = null ; context.SyncContext.ProhibitVoidAsyncOperations(); try { if (mustCallEndHandler) { asyncHandler.EndProcessRequest(ar); } _asyncStepCompletionInfo.ReportError(); } finally { SuppressPostEndRequestIfNecessary(context); context.Response.GenerateResponseHeadersForHandler(); } } } else { _sync = true ; context.SyncContext.SetSyncCaller(); try { handler.ProcessRequest(context); } finally { context.SyncContext.ResetSyncCaller(); SuppressPostEndRequestIfNecessary(context); context.Response.GenerateResponseHeadersForHandler(); } } } }
小結: 希望可以讓大家對於為什麼Asp.net
為何可以針對IHttpModule
擴充且為何最後都會請求一個IHttpHandler
有更深入的了解.
微軟透過一系列的管道設計模式提供有高度擴展的系統對外提供一個IHttpHandler
讓我們可以客製化擴充要執行的請求.
對於此次請求又有IHttpModule
可對於HttpApplication
事件做擴充(透過AOP編成方式).
今天之後我們會開始講解Asp.net MVC 相關的原始程式碼.
__此文作者__:Daniel Shih(石頭) __此文地址__: https://isdaniel.github.io/Ithelp-day7/ __版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!