这篇文章花了点时间,差点成烂到电脑里面,写的过程中有好几次修改,最终的这个版本也不是很满意,东西说的不够细,还需要认真的去看下源码才能有所体会,先这样吧,后面有时间把细节慢慢的再修改。顺便对于开发的学习,个人是觉得源码的阅读是最快的提高方式,当然阅读不是走马观花,应该多次阅读。

上次说到获得HttpApplication对象的创建,创建完成后调用InitInternal方法,这个方法任务比较多,也比较长,这里就不贴全码了,一个一个过程的去说:

初始化HttpModule

对于HttpModule的认识,首先应该看下HttpModule的使用情况,下面通过一个简单的例子展示:

httpModule使用实例

  1. 新建一个项目,添加一个webform的窗体default.aspx,使用IIS添加到网站,应用程序池使用集成模式。
  2. 添加一个MyModule.cs,继承自IHttpModule。
  3. 在IHttpMoudule中有两个方法,在MyModule中必须要实现:

    public void Init(HttpApplication context)
    {
       throw new System.NotImplementedException();
    }
    
    public void Dispose()
    {
       throw new System.NotImplementedException();
    }
  4. 在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>");
    }
  5. 添加web.config文件如下(在 system.webServer下 modules节点下面):

    <add name="MyModule" type="Application.MyModule,Application"/>

    运行结果:

  6. 在上面的例子中,使用的是集成模式,当改成经典模式的时候,module又不起作用了。对于经典模式的配置文件与集成模式不同。经典模式的配置文件如下:

    <httpModules>
       <add name="MyModule" type="IISIntegratedPipeline.MyModule,IISIntegratedPipeline"/>
    </httpModules>
  7. 对于 module的使用,有了一个简单的认识,在asp.net中module是一个灵活的配置,可以对请求进行自定义的处理,对于Asp.net如何处理的,在下面详细解说。

asp.net中HttpModule的处理

  1. 结合上面例子,HttpModule在Asp.net中有重要的作用,可以HttpApplication的事件进行订阅,也可以修改对应的响应的内容

  2. 对于HttpModule的初始化,asp.Net中会根据当前应用程序池的类型进行初始化,核心代码如下:

    if (HttpRuntime.UseIntegratedPipeline) {
        try {
        context.HideRequestResponse = true;
        _hideRequestResponse = true;
        InitIntegratedModules();
    }
    finally {
        context.HideRequestResponse = false;
        _hideRequestResponse = false;
       }
    }
    else {
        InitModules();
     }
  3. 对于Module的理解,需要根据应用程序池的模式来处理(经典和集成)。
  4. 对于集成模式,获得所有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);
  5. 对于经典模式获得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();
    }
  6. 最终会调用 InitModulesCommon方法,循环调用Modules中的方法

     private void InitModulesCommon()
     {
            int n = _moduleCollection.Count;
    
            ; i < n; i++) {
    
            _currentModuleCollectionKey = _moduleCollection.GetKey(i);
             _moduleCollection[i].Init(this);
            }
    
            _currentModuleCollectionKey = null;
            InitAppLevelCulture();
     }

Global内的方法调用

对于Global方法的调用,是调用HookupEventHandlersForApplicationAndModules(handlers);方法,这里的Handlers的收集和创建来源于上篇讲HttpAplication的三个方法调用的第一个方法。具体的看下上次的代码,这里不多叙述。对于方法的handlers的调用的核心代码如下,其实也是一个循环加上判断:

 ; i < handlers.Length; i++) {
        MethodInfo appMethod = handlers[i];
        String appMethodName = appMethod.Name;
        int namePosIndex = appMethodName.IndexOf('_');
        String targetName = appMethodName.Substring(, namePosIndex);

        ...
        ParameterInfo[] addMethodParams = addMethod.GetParameters();

        )
        continue;
        Delegate handlerDelegate = null;

        ParameterInfo[] appMethodParams = appMethod.GetParameters();

        ...
        try {
            addMethod.Invoke(target, ]{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 array
if (HttpRuntime.UseIntegratedPipeline) {
  _stepManager = new PipelineStepManager(this);
}
else {
 _stepManager = new ApplicationStepManager(this);
}

执行BuildStep

BuildStep与ResumeStep是Asp.net的核心运行环节。同样,在经典模式与集成模式下原理和过程也有所不一样。

集成模式

  1. 下面先讨论集成模式下是如何进行的。

    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;
        }
  2. 上面的代码是核心是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 >  );

        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();
                ; 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的更多相关文章

  1. 第36篇 Asp.Net源码解析(一)

    上面两篇文章说了http协议和IIS处理,这次说下当IIS把请求交给Asp.net后的过程. AppManagerAppDomainFactory 当IIS把请求交给asp.net时候,如果AppDo ...

  2. JQuery 源码解析 · extend()详解

    前言:最近想重写一个dropdown插件,于是想到了使用jquey实现插件,于是重温了一波$.extend()的知识,然后总结了这篇笔记 正文: $.extend(src)  jQuery.exten ...

  3. RxJava2源码解析(二)

    title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...

  4. Sentinel源码解析二(Slot总览)

    写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...

  5. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  6. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  7. 【转】ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

    原文地址:http://blog.csdn.net/a396901990/article/details/36475213 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量—— ...

  8. ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

    简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3. ...

  9. React源码 commit阶段详解

    转: React源码 commit阶段详解 点击进入React源码调试仓库. 当render阶段完成后,意味着在内存中构建的workInProgress树所有更新工作已经完成,这包括树中fiber节点 ...

随机推荐

  1. ubuntu16.04无法设置选择wifi的解决办法

    在公司上班一直连接的有线,直到昨天拿回家才发现ubuntu无法选择使用wifi上网,这让人非常无奈,截图类似如下: 而正常情况下我们应该在启用联网的下面有wifi链接的选项,如图: 我隐约猜测是和驱动 ...

  2. SVN-TortoiseSVN安装和常用操作步骤

    安装VisualSVN-Server-2.0.5 服务端: 运行VisualSVN-Server-2.0.5.msi程序,点击Next,下面的截图顺序即为安装步骤: 2 图2: 注意:Server P ...

  3. matlab switch case 和 try catch用法示例

    %清除变量或指令 clc;clear; % 允许用户输入参数 disp ('该功能练习switch语句'); disp ('输入1-10其中一个数,系统判定奇偶. '); count = input ...

  4. java中静态代码块,构造代码块,以及构造方法的执行顺序

    写了许久的代码,却把一些基础的东西都给忘了,今天无聊就顺手写了个,然后测试下,发现跟我记忆中的竟然有些出入,作为一个两年的开发,我感觉自己很失败啊. 父类pojo: public class Pojo ...

  5. protocol error, got 'n' as reply type byte

    centos6.5上安装redis3.2版本,本地访问redis报错protocol error, got 'n' as reply type byte 解决办法 在redis配置文件redis.co ...

  6. 【2017年新篇章】 .NET 面试题汇总(一)

    开篇 本次给大家介绍的是我收集以及自己个人保存一些.NET面试题 简介 此次包含的不止是.NET知识,也包含少许前端知识以及.net面试时所涉及的种种考点,希望能给找工作的同学们哪怕一点点帮助. 古人 ...

  7. 漂亮的代码2:遍历文件夹目录,使用promise

    看到一个问题: 找到文件夹下所有文件: 自己写了一个: function walk(dir, ext, callback) { ext = ext.charAt(0) === "." ...

  8. Javascript继承(暂略去中转函数、组合继承和寄生继承)

    继承,在JS中通过原型链实现.如: function Box(){ this.name="Lee"; } function Desk(){ this.age=100; } //通过 ...

  9. 为 instance 配置静态 IP - 每天5分钟玩转 OpenStack(157)

    这是 OpenStack 实施经验分享系列的第 7 篇. 传统运维中为服务器配置静态 IP 是再常见不过的了.但在 OpenStack 环境下只能指定 network,IP 都是 Neutron 从 ...

  10. Ansible 系列之 Ad-Hoc介绍及使用

    Ad-Hoc 介绍 一.什么是ad-hoc 命令? ad-hoc 命令是一种可以快速输入的命令,而且不需要保存起来的命令.就相当于bash中的一句话shell.这也是一个好的地方,在学习ansible ...