iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 7
3
Software Development

從Asp.net框架角度進入Asp.net MVC原始碼系列 第 7

[Day07] Asp.Net重要物件HttpApplication(三) 取得執行的IHttpHandler

前言

前面和大家分享StepManager是如何建立管道和依序呼叫IHttpModule註冊事件

查看原始碼好站 Reference Source

此文的程式碼比較多我會在原始碼上邊上說明相對應編號方便大家觀看

今天跟大家分享HttpAppliaction是如何找到要執行的IHttpHandler物件.

此篇同步發布在筆者Blog [Day07] Asp.Net重要物件HttpApplication(三) 取得執行的IHttpHandler

呼叫HttpAppliaction取得HttpHandler並呼叫

ApplicationStepManagerExecutionStep中重要的實現類別為

  1. MapHandlerExecutionStep
  2. CallHandlerExecutionStep

MapHandlerExecutionStep程式碼解說

前面說過IExecutionStep最核心就是要找到一個Execute方法

MapHandlerExecutionStepExecute方法是為了找到一個要執行的HttpHander

每次請求都會呼叫HttpContext.Handler屬性.

MapHttpHandler會依照下面權重來取得HttpHander物件.

  1. context.RemapHandlerInstance如果有物件就優先返回(很重要因為這就是Asp.net MVC使用的HttpHander物件)
  2. 透過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相關的原始程式碼.


上一篇
[Day06] Asp.Net重要物件HttpApplication(二) 建置執行管道
下一篇
[Day08] 揭密Mvc使用IHttpHandler by UrlRoutingModule-4.0
系列文
從Asp.net框架角度進入Asp.net MVC原始碼30

尚未有邦友留言

立即登入留言