前言
前面和大家分享StepManager
是如何建立管道和依序呼叫IHttpModule
註冊事件
查看原始碼好站 Reference Source
此文的程式碼比較多我會在原始碼上邊上說明相對應編號方便大家觀看
今天跟大家分享HttpAppliaction
是如何找到要執行的IHttpHandler
物件.
此篇同步發布在筆者Blog [Day07] Asp.Net重要物件HttpApplication(三) 取得執行的IHttpHandler
呼叫HttpAppliaction取得HttpHandler並呼叫
在ApplicationStepManager
的ExecutionStep
中重要的實現類別為
MapHandlerExecutionStep
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
註冊表
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 /*useAppConfig*/);
}
}
internal IHttpHandler MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, bool useAppConfig) {
IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null;
using (new ApplicationImpersonationContext()) {
// Use remap handler if possible
if (handler != null){
return handler;
}
// Map new 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));
}
// Get factory from the mapping
IHttpHandlerFactory factory = GetFactory(mapping);
// Get factory from the 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) {
//...丟Exception
}
// Remember for recycling
if (_handlerRecycleList == null)
_handlerRecycleList = new ArrayList();
_handlerRecycleList.Add(new HandlerWithFactory(handler, factory));
}
return handler;
}
MapHandlerExecutionStep
是為了找到我們要執行的HttpHandler
物件.
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
型別就值型同步動作
// execution step -- call HTTP handler (used to be a separate module)
internal class CallHandlerExecutionStep : IExecutionStep {
private HttpApplication _application;
private AsyncCallback _completionCallback;
private IHttpAsyncHandler _handler; // per call
private AsyncStepCompletionInfo _asyncStepCompletionInfo; // per call
private bool _sync; // per call
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) {
// asynchronous handler
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 {
// The asynchronous step has completed, so we should disallow further
// async operations until the next step.
context.SyncContext.ProhibitVoidAsyncOperations();
throw;
}
bool operationCompleted;
bool mustCallEndHandler;
_asyncStepCompletionInfo.RegisterBeginUnwound(ar, out operationCompleted, out mustCallEndHandler);
if (operationCompleted) {
_sync = true;
_handler = null; // not to remember
context.SyncContext.ProhibitVoidAsyncOperations();
try {
if (mustCallEndHandler) {
asyncHandler.EndProcessRequest(ar);
}
_asyncStepCompletionInfo.ReportError();
}
finally {
SuppressPostEndRequestIfNecessary(context);
context.Response.GenerateResponseHeadersForHandler();
}
}
}
else {
// synchronous handler
_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相關的原始程式碼.
如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^