首页 > 代码库 > 第37篇 Asp.Net源码解析(二)--详解HttpApplication
第37篇 Asp.Net源码解析(二)--详解HttpApplication
这篇文章花了点时间,差点成烂到电脑里面,写的过程中有好几次修改,最终的这个版本也不是很满意,东西说的不够细,还需要认真的去看下源码才能有所体会,先这样吧,后面有时间把细节慢慢的再修改。顺便对于开发的学习,个人是觉得源码的阅读是最快的提高方式,当然阅读不是走马观花,应该多次阅读。
上次说到获得HttpApplication对象的创建,创建完成后调用InitInternal方法,这个方法任务比较多,也比较长,这里就不贴全码了,一个一个过程的去说:
初始化HttpModule
对于HttpModule的认识,首先应该看下HttpModule的使用情况,下面通过一个简单的例子展示:
httpModule使用实例
- 新建一个项目,添加一个webform的窗体default.aspx,使用IIS添加到网站,应用程序池使用集成模式。
- 添加一个MyModule.cs,继承自IHttpModule。
在IHttpMoudule中有两个方法,在MyModule中必须要实现:
public void Init(HttpApplication context){ throw new System.NotImplementedException();}public void Dispose(){ throw new System.NotImplementedException();}
在Init方法中,有一个HttpApplication类型的对象context,这里可以对其中的响应的内容进行更改,修改如下:
public void Init(HttpApplication context){ context.EndRequest += Context_EndRequest;}private void Context_EndRequest(object sender, System.EventArgs e){ var context = (HttpApplication) sender; context.Response.Write("<h1>Hello MyModule</h1>");}
添加web.config文件如下(在 system.webServer下 modules节点下面):
<add name="MyModule" type="Application.MyModule,Application"/>
运行结果:
在上面的例子中,使用的是集成模式,当改成经典模式的时候,module又不起作用了。对于经典模式的配置文件与集成模式不同。经典模式的配置文件如下:
<httpModules> <add name="MyModule" type="IISIntegratedPipeline.MyModule,IISIntegratedPipeline"/></httpModules>
对于 module的使用,有了一个简单的认识,在asp.net中module是一个灵活的配置,可以对请求进行自定义的处理,对于Asp.net如何处理的,在下面详细解说。
asp.net中HttpModule的处理
结合上面例子,HttpModule在Asp.net中有重要的作用,可以HttpApplication的事件进行订阅,也可以修改对应的响应的内容
对于HttpModule的初始化,asp.Net中会根据当前应用程序池的类型进行初始化,核心代码如下:
if (HttpRuntime.UseIntegratedPipeline) { try { context.HideRequestResponse = true; _hideRequestResponse = true; InitIntegratedModules();}finally { context.HideRequestResponse = false; _hideRequestResponse = false; }}else { InitModules(); }
- 对于Module的理解,需要根据应用程序池的模式来处理(经典和集成)。
对于集成模式,获得所有Modules的方法是调用非托管的方法的进行获得,具体获得的代码如下:
InitIntegratedModules的方法
private void InitIntegratedModules(){ _moduleCollection = BuildIntegratedModuleCollection(_moduleConfigInfo); InitModulesCommon();}
_moduleConfigInfo 的来源
这个_moduleConfigInfo的来源,还需要追到上篇 HttpApplication中三个方法的调用(EnsureAppStartCalled 第二个方法的调用)具体调用步骤如下:
[DllImport(_IIS_NATIVE_DLL)] internal static extern int MgdGetModuleCollection( IntPtr pConfigSystem, IntPtr appContext, out IntPtr pModuleCollection, out int count);
对于经典模式获得Modules简单的多,直接获得是调用配置文件
private void InitModules(){ HttpModulesSection pconfig = RuntimeConfig.GetAppConfig().HttpModules; // get the static list, then add the dynamic members HttpModuleCollection moduleCollection = pconfig.CreateModules(); HttpModuleCollection dynamicModules = CreateDynamicModules(); moduleCollection.AppendCollection(dynamicModules); _moduleCollection = moduleCollection; // don‘t assign until all ops have succeeded InitModulesCommon();}
最终会调用 InitModulesCommon方法,循环调用Modules中的方法
private void InitModulesCommon() { int n = _moduleCollection.Count; for (int i = 0; i < n; i++) { _currentModuleCollectionKey = _moduleCollection.GetKey(i); _moduleCollection[i].Init(this); } _currentModuleCollectionKey = null; InitAppLevelCulture(); }
Global内的方法调用
对于Global方法的调用,是调用HookupEventHandlersForApplicationAndModules(handlers);方法,这里的Handlers的收集和创建来源于上篇讲HttpAplication的三个方法调用的第一个方法。具体的看下上次的代码,这里不多叙述。对于方法的handlers的调用的核心代码如下,其实也是一个循环加上判断:
for (int i = 0; i < handlers.Length; i++) { MethodInfo appMethod = handlers[i]; String appMethodName = appMethod.Name; int namePosIndex = appMethodName.IndexOf(‘_‘); String targetName = appMethodName.Substring(0, namePosIndex); ... ParameterInfo[] addMethodParams = addMethod.GetParameters(); if (addMethodParams.Length != 1) continue; Delegate handlerDelegate = null; ParameterInfo[] appMethodParams = appMethod.GetParameters(); ... try { addMethod.Invoke(target, new Object[1]{handlerDelegate}); } catch { if (HttpRuntime.UseIntegratedPipeline) { throw; } } if (eventName != null) { if (_pipelineEventMasks.ContainsKey(eventName)) { if (!StringUtil.StringStartsWith(eventName, "Post")) { _appRequestNotifications |= _pipelineEventMasks[eventName]; } else { _appPostNotifications |= _pipelineEventMasks[eventName]; } } }}
根据应用程序池的类型创建不同的_stepManager
这里很简单,直接看代码:
// Construct the execution steps arrayif (HttpRuntime.UseIntegratedPipeline) { _stepManager = new PipelineStepManager(this);}else { _stepManager = new ApplicationStepManager(this);}
执行BuildStep
BuildStep与ResumeStep是Asp.net的核心运行环节。同样,在经典模式与集成模式下原理和过程也有所不一样。
集成模式
下面先讨论集成模式下是如何进行的。
internal override void BuildSteps(WaitCallback stepCallback) { HttpApplication app = _application; IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app); app.AddEventMapping( HttpApplication.IMPLICIT_HANDLER, RequestNotification.MapRequestHandler, false, materializeStep); app.AddEventMapping( HttpApplication.IMPLICIT_HANDLER, RequestNotification.ExecuteRequestHandler, false, app.CreateImplicitAsyncPreloadExecutionStep()); IExecutionStep handlerStep = new CallHandlerExecutionStep(app); app.AddEventMapping( HttpApplication.IMPLICIT_HANDLER, RequestNotification.ExecuteRequestHandler, false, handlerStep); IExecutionStep webSocketsStep = new TransitionToWebSocketsExecutionStep(app); app.AddEventMapping( HttpApplication.IMPLICIT_HANDLER, RequestNotification.EndRequest, true /* isPostNotification */, webSocketsStep); IExecutionStep filterStep = new CallFilterExecutionStep(app); app.AddEventMapping( HttpApplication.IMPLICIT_FILTER_MODULE, RequestNotification.UpdateRequestCache, false, filterStep); app.AddEventMapping( HttpApplication.IMPLICIT_FILTER_MODULE, RequestNotification.LogRequest, false, filterStep); _resumeStepsWaitCallback = stepCallback; }
上面的代码是核心是AddEventMapping方法,把相关的步骤添加到一个PipelineModuleStepContainer.
private void AddEventMapping(string moduleName,RequestNotification requestNotification,bool isPostNotification, IExecutionStep step){ ThrowIfEventBindingDisallowed(); if (!IsContainerInitalizationAllowed) { return; } PipelineModuleStepContainer container = GetModuleContainer(moduleName); if (container != null) { container.AddEvent(requestNotification, isPostNotification, step); }}
经典模式
1.看下代码:
internal override void BuildSteps(WaitCallback stepCallback ) { ArrayList steps = new ArrayList(); HttpApplication app = _application; bool urlMappingsEnabled = false; UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings; urlMappingsEnabled = urlMappings.IsEnabled && ( urlMappings.UrlMappings.Count > 0 ); steps.Add(new ValidateRequestExecutionStep(app)); steps.Add(new ValidatePathExecutionStep(app)); if (urlMappingsEnabled) steps.Add(new UrlMappingsExecutionStep(app)); // url mappings 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); _resumeStepsWaitCallback = stepCallback; }
对于上面的代码,可以看出都调用了 CreateEventExecutionSteps 方法,这个方法的详细如下 :
private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) { // async 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])); } } }
可以看出, CreateEventExecutionSteps是把注册的步骤都转换成了SyncEventExecutionStep,最终会被按顺序进行调用。
执行BeginProcessRequest
HttpApplication在完成BuildSteps的时候,把生成的App经过层层返回到HttpRuntime,前面几篇文章提到,在HttpRuntime里面有对app的类型进行判断,如果是IHttpAsyncHandler直接调用BeginProcessRequest方法,具体的代码如下:
if (app is IHttpAsyncHandler) { IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app; context.AsyncAppHandler = asyncHandler; asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context); } else { // synchronous handler app.ProcessRequest(context); FinishRequest(context.WorkerRequest, context, null); }
BeginProcessRequest 方法:
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) { HttpAsyncResult result; _context = context; _context.ApplicationInstance = this; _stepManager.InitRequest(); _context.Root(); result = new HttpAsyncResult(cb, extraData); AsyncResult = result; if (_context.TraceIsEnabled) HttpRuntime.Profile.StartRequest(_context); ResumeSteps(null); return result; }
其中最核心的方法是ResumeSteps方法,具体如下:
internal override void ResumeSteps(Exception error){ for (; ; ) { // ... IExecutionStep step = _application.CurrentModuleContainer.GetNextEvent(context.CurrentNotification, context.IsPostNotification,context.CurrentModuleEventIndex); context.SyncContext.Enable(); stepCompletedSynchronously = false; //******* error = _application.ExecuteStep(step, ref stepCompletedSynchronously); //********* ... if (!stepCompletedSynchronously) { _application.AcquireNotifcationContextLock(ref locked); context.NotificationContext.PendingAsyncCompletion = true; break; } else { context.Response.SyncStatusIntegrated(); } }}
BeginProcessRequest 方法:
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) { HttpAsyncResult result; _context = context; _context.ApplicationInstance = this; _stepManager.InitRequest(); _context.Root(); result = new HttpAsyncResult(cb, extraData); AsyncResult = result; if (_context.TraceIsEnabled) HttpRuntime.Profile.StartRequest(_context); ResumeSteps(null); return result;}
其中最核心的方法是ResumeSteps方法,具体如下:
internal override void ResumeSteps(Exception error) { ... for (; ; ) { // ... IExecutionStep step = _application.CurrentModuleContainer.GetNextEvent(context.CurrentNotification, context.IsPostNotification,context.CurrentModuleEventIndex); context.SyncContext.Enable(); stepCompletedSynchronously = false; //******* error = _application.ExecuteStep(step, ref stepCompletedSynchronously); //********* ... if (!stepCompletedSynchronously) { _application.AcquireNotifcationContextLock(ref locked); context.NotificationContext.PendingAsyncCompletion = true; break; } else { context.Response.SyncStatusIntegrated(); } } }
对于上面的内容总结原理为:
写于 2017.03.21
第37篇 Asp.Net源码解析(二)--详解HttpApplication