接上一篇, 我在 HttpModule 的Init方法中, 添加了自己的事件, 在Pipeline里, 就会把握注册的事件给执行了. 那么Pipeline是如何执行并且按照什么顺序执行的呢?

现在我们重新回到HttpApplication.InitInternal()方法中来. 注: Integrated 是集成的意思, 明白这个单词的意思之后, 下面这几句代码就很好理解了.

  1. if (HttpRuntime.UseIntegratedPipeline)
  2. {
  3. this._stepManager = new PipelineStepManager(this); //集成
  4. }
  5. else
  6. {
  7. this._stepManager = new ApplicationStepManager(this); //经典
  8. }
  9. this._stepManager.BuildSteps(this._resumeStepsWaitCallback);

集成模式和经典模式(或IIS6)使用的是不同的StepManager,这个类的BuildSteps方法就是为了创建有序的ExecutionStep,其中包括各种事件的事情以及其它在各时间周期之间穿插的操作,最主要的操作,大家以前就应该知道的,比如哪个周期可以判定使用哪个HttpHandler,以及在哪个周期内执行这个HttpHandler的BeginProcessRequest方法。

1. 经典模式

由于不同的StepManager处理方式不同,我们先看经典模式的处理代码:

  1. //HttpApplication的内部类ApplicationStepManager
  2. internal override void BuildSteps(WaitCallback stepCallback)
  3. {
  4. ArrayList steps = new ArrayList();
  5. HttpApplication app = base._application;
  6. bool flag = false;
  7. UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
  8. flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > );
  9. steps.Add(new HttpApplication.ValidateRequestExecutionStep(app));
  10. steps.Add(new HttpApplication.ValidatePathExecutionStep(app));
  11. if (flag)
  12. {
  13. steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); //Url Mapping
  14. }
  15. app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
  16. app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
  17. app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
  18. app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
  19. app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
  20. app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
  21. app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
  22. app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
  23. steps.Add(new HttpApplication.MapHandlerExecutionStep(app)); //Handle Mapping
  24. app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
  25. app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
  26. app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
  27. app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
  28. steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());
  29. steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); //execute handler
  30. app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
  31. app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
  32. app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
  33. steps.Add(new HttpApplication.CallFilterExecutionStep(app)); //Filtering
  34. app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
  35. app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
  36. this._endRequestStepIndex = steps.Count;
  37. app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
  38. steps.Add(new HttpApplication.NoopExecutionStep());
  39. this._execSteps = new HttpApplication.IExecutionStep[steps.Count];
  40. steps.CopyTo(this._execSteps);
  41. this._resumeStepsWaitCallback = stepCallback;
  42. }

接着来看一下上面标红的那个方法:

  1. private void CreateEventExecutionSteps(object eventIndex, ArrayList steps)
  2. {
  3. AsyncAppEventHandler handler = this.AsyncEvents[eventIndex];
  4. if (handler != null)
  5. {
  6. handler.CreateExecutionSteps(this, steps);
  7. }
  8. EventHandler handler2 = (EventHandler) this.Events[eventIndex];
  9. if (handler2 != null)
  10. {
  11. Delegate[] invocationList = handler2.GetInvocationList();
  12. for (int i = ; i < invocationList.Length; i++)
  13. {
  14. steps.Add(new SyncEventExecutionStep(this, (EventHandler) invocationList[i]));
  15. }
  16. }
  17. }
  1. HttpApplication.EventBeginRequest 作为一个object的参数传入这个方法了, 并且从这个方法里面看, 它被当做了一个Key值来用. 那么他具体是个啥呢? HttpApplication中看一下.
  1. private static readonly object EventBeginRequest;
  2.  
  3. static HttpApplication()
  4. {
  5. _dynamicModuleRegistry = new DynamicModuleRegistry();
  6. ......
  7. EventBeginRequest = new object();
  8. ......
  9. AutoCulture = "auto";
  10. _moduleIndexMap = new Hashtable();
  11. }

从这里能看到, 他真的就是一个object 类型的Key.

看着上面的代码是不是有似曾相识的感觉,很多讲声明周期的文章都会提到20多个的事件(BeginRequest, EndRequest等),我们来看看这个方法的完整功能都是做了什么,归纳总结有5点:

  1. 对请求的Request进行验证,ValidateRequestExecutionStep。
  2. 对请求的路径进行安全检查,禁止非法路径访问(ValidatePathExecutionStep)。 
  3. 如果设置了UrlMappings, 进行RewritePath(UrlMappingsExecutionStep)。
  4. 执行事件处理函数,比如将BeginRequest、AuthenticateRequest转化成可执行ExecutionStep在正式调用时候执行。
  5. 在这18个事件操作处理期间,根据不同的时机加了4个特殊的ExecutionStep。
    1. MapHandlerExecutionStep:查找匹配的HttpHandler
    2. CallHandlerExecutionStep:执行HttpHandler的BeginProcessRequest
    3. CallFilterExecutionStep:调用Response.FilterOutput方法过滤输出
    4. NoopExecutionStep:空操作,留着以后扩展用

需要注意的是所有的ExecuteionStep都保存在ApplicationStepManager实例下的私有字段_execSteps里,而HttpApplication的BeginProcessRequest方法最终会通过该实例的ResumeSteps方法来执行这些操作(就是我们所说的那些事件以及4个特殊的Steps)。

2. 集成模式

好像在记忆里, 我发布的项目都是集成模式的. 那在这里来看一下集成模式是怎么执行的.

  1. //HttpApplication 的内部类 PipelineStepManager
  1. internal override void BuildSteps(WaitCallback stepCallback)
  2. {
  3. HttpApplication app = base._application;
       //add special steps that don't currently
       //correspond to a configured handler
  4. HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(app);
       //implicit map step
  5. app.AddEventMapping("ManagedPipelineHandler", RequestNotification.MapRequestHandler, false, step);
      
  6. app.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false,
        app.CreateImplicitAsyncPreloadExecutionStep());
       //implicit handler routing step
  7. HttpApplication.IExecutionStep step2 = new HttpApplication.CallHandlerExecutionStep(app);
  8. app.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false, step2);
  9. HttpApplication.IExecutionStep step3 = new HttpApplication.TransitionToWebSocketsExecutionStep(app);
  10. app.AddEventMapping("ManagedPipelineHandler", RequestNotification.EndRequest, true, step3);
  11. HttpApplication.IExecutionStep step4 = new HttpApplication.CallFilterExecutionStep(app);
       //normally, this executes during UpdateRequestCache as a high priority module
  12. app.AddEventMapping("AspNetFilterModule", RequestNotification.UpdateRequestCache, false, step4);
       //for error conditions, this executes during LogRequest as high priority module
  13. app.AddEventMapping("AspNetFilterModule", RequestNotification.LogRequest, false, step4);
  14. this._resumeStepsWaitCallback = stepCallback;
  15. }

来看一下 RequestNotification.MapRequestHandler 是个什么东东.

  1. [Flags]
  2. public enum RequestNotification
  3. {
  4. AcquireRequestState = 0x20,
  5. AuthenticateRequest = ,
  6. AuthorizeRequest = ,
  7. BeginRequest = ,
  8. EndRequest = 0x800,
  9. ExecuteRequestHandler = 0x80,
  10. LogRequest = 0x400,
  11. MapRequestHandler = 0x10,
  12. PreExecuteRequestHandler = 0x40,
  13. ReleaseRequestState = 0x100,
  14. ResolveRequestCache = ,
  15. SendResponse = 0x20000000,
  16. UpdateRequestCache = 0x200
  17. }

从这里看, 他是一个枚举, 使用的时候, 肯定也是当做一个key值来用的. 但是为什么这里的枚举值少了许多呢?看这里的1,2,4之间, 好像少了3(PostAuthenticateRequest). 这是为啥呢?

没办法, 我只能直接去 HttpApplication 里面先看看这个事件.

  1. public event EventHandler PostAuthenticateRequest
  2. {
  3. add
  4. {
  5. this.AddSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true);
  6. }
  7. remove
  8. {
  9. this.RemoveSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true);
  10. }
  11. }

在这里发现, 他使用的是 RequestNotification.AuthenticateRequest, 那 AuthenticateRequest 事件呢, 用的是什么?

  1. public event EventHandler AuthenticateRequest
  2. {
  3. add
  4. {
  5. this.AddSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest);
  6. }
  7. remove
  8. {
  9. this.RemoveSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest);
  10. }
  11. }

他们使用的是同一个Key, 并且调用的是同一个方法 AddSyncEventHookup, 只是Post时, 多入了一个参数true

那么就看一下这个方法吧

  1. private void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification)
  2. {
  3. this.ThrowIfEventBindingDisallowed();
  4. this.Events.AddHandler(key, handler);
  5. if (this.IsContainerInitalizationAllowed)
  6. {
  7. PipelineModuleStepContainer moduleContainer = this.GetModuleContainer(this.CurrentModuleCollectionKey);
  8. if (moduleContainer != null)
  9. {
  10. SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler) handler);
  11. moduleContainer.AddEvent(notification, isPostNotification, step);
  12. }
  13. }
  14. }

接着往下看AddEvent方法.

  1. internal void AddEvent(RequestNotification notification, bool isPostEvent, HttpApplication.IExecutionStep step)
  2. {
  3. int index = EventToIndex(notification);
  4. List<HttpApplication.IExecutionStep>[] listArray = null;
  5. if (isPostEvent)
  6. {
  7. if (this._modulePostSteps == null)
  8. {
  9. this._modulePostSteps = new List<HttpApplication.IExecutionStep>[0x20];
  10. }
  11. listArray = this._modulePostSteps;
  12. }
  13. else
  14. {
  15. if (this._moduleSteps == null)
  16. {
  17. this._moduleSteps = new List<HttpApplication.IExecutionStep>[0x20];
  18. }
  19. listArray = this._moduleSteps;
  20. }
  21. List<HttpApplication.IExecutionStep> list = listArray[index];
  22. if (list == null)
  23. {
  24. list = new List<HttpApplication.IExecutionStep>();
  25. listArray[index] = list;
  26. }
  27. list.Add(step);
  28. }

以这种方式, 节约了许多枚举值.

接着看 AddEventMapping 方法:

  1. private void AddEventMapping(string moduleName, RequestNotification requestNotification, bool isPostNotification, IExecutionStep step)
  2. {
  3. this.ThrowIfEventBindingDisallowed();
  4. if (this.IsContainerInitalizationAllowed)
  5. {
  6. PipelineModuleStepContainer moduleContainer = this.GetModuleContainer(moduleName);
  7. if (moduleContainer != null)
  8. {
  9. moduleContainer.AddEvent(requestNotification, isPostNotification, step);
  10. }
  11. }
  12. }

从这里能看到, 事件被注册到 PipelineModuleStepContainer 类型的一个moduleContainer 容器中了.

以上代码有2个地方和经典模式不相同:

  1. IIS7集成模式没有使用MapHandlerExecutionStep来装载ExecutionStep(也就是查找对应的HttpHandler),而是通过MaterializeHandlerExecutionStep类来获得HttpHandler,方式不一样,但最终都是调用HttpApplication.GetFactory方法来获取的,只不过IIS7集成模式有一些特殊操作而已罢了。
  2. IIS7集成模式是通过HttpApplication的AddEventMapping方法来添加事件的,从而将事件再次加入到前面所说的ModuleContainers容器。

另外有个很有技巧的代码:上述4个Steps所加的周期都不是准确的周期,比如CallHandlerExecutionStep应该是加载RequestNotification的枚举值PreExecuteRequestHandler 和ExecuteRequestHandler之间,为什么呢?因为本身CallHandlerExecutionStep只是一个特殊的step而不暴露事件的,所以枚举里也没有,那怎么办?回头看看AddEventMapping方法的第一个参数,它代表的是HttpModule的名字,查看其中的代码得知,在执行所有事件的时候,会遍历所有HttpModuel名称集合然后先执行全部BeginRequest事件,再全部执行AuthenticateRequest事件,以此类推,那我们能不能来伪造一个HttpModule的名称作为参数传递给AddEventMapping方法呢,答案是肯定的,看上面的代码,发现有2个伪造的名称分别是常量字符串 "AspNetFilterModule" (HttpApplication.IMPLICIT_FILTER_MODULE)和 "ManagedPipelineHandler" (HttpApplication.IMPLICIT_HANDLER),而因为之前其它HttpModule里的各种事件都已经load完了,所以这2个伪造HttpModule的是放在集合的最后面,所以在执行ExecuteRequestHandler类别的事件的时候,最后一个事件肯定就是这个伪造HttpModule的事件,再加上伪造HttpModule里没有别的事件,所以它对应的ExecutionStep的执行效果其实和IIS6里CallHandlerExecutionStep的效果是一样的,就这样,通过一个很奇特的技巧达到同样的目的。

最后,我们来总结一下.

在IIS7经典模式下,是用 Event+事件名称做key将所有事件的保存在HttpApplication的Events属性对象里,然后在BuildSteps里统一按照顺序组装,中间加载4个特殊的ExecutionStep,最后在统一执行;

在IIS7集成模式下,是通过HttpModule名称+RequestNotification枚举值作为key将所有的事件保存在HttpApplication的ModuleContainers属性对象里,然后也在BuildSteps里通过伪造HttpModule名称加载那4个特殊的ExecutionStep,最后按照枚举类型的顺序,遍历所有的HttpModule按顺序来执行这些事件。读者可以自行编写一个自定义的HttpModuel来执行这些事件看看效果如何。

最后关于Pipeline完整的图如下:

最后,需要注意的是:HttpApplication不是HttpRuntime所创建,HttpRuntime只是向HttpApplicationFactory提出请求,要求返回一个HttpApplication对象。 HttpApplicationFactory在接收到请求后,会先检查是否有已经存在并空闲的对象,如果有就取出一个HttpApplication对象返回给HttpRuntime,如果没有的话,则要创建一个HttpApplication对象给HttpRunTime。

转载参考:

  MVC之前的那点事儿

目录已同步

MVC源码解析 - Http Pipeline 解析(下)的更多相关文章

  1. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  2. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  3. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  4. springMVC源码分析--RequestParamMethodArgumentResolver参数解析器(三)

    之前两篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)和springMVC源码解析--HandlerMethodArgumentResol ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  9. [转]MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    本文转自:http://www.cnblogs.com/landeanfen/p/5989092.html 阅读目录 一.MVC原理解析 1.MVC原理 二.HttpHandler 1.HttpHan ...

  10. ASP.NET MVC 源码分析(一)

    ASP.NET MVC 源码分析(一) 直接上图: 我们先来看Core的设计: 从项目结构来看,asp.net.mvc.core有以下目录: ActionConstraints:action限制相关 ...

随机推荐

  1. Visual Studio 单元测试之二---顺序单元测试

    原文:Visual Studio 单元测试之二---顺序单元测试 此文是上一篇博文:Visual Studio 单元测试之一---普通单元测试的后续篇章.如果读者对Visual Studio的单元测试 ...

  2. Mysql 使用CMD 登陆

    1. 将mysql 的bin 目录加到 系统环境变量中: 这样的目的是为了可以直接在cmd中使用mysql命令,而不用先CD到mysql的bin目录中. 3. 连接目标数据库 . Syntax:   ...

  3. .Net中批量更新或添加数据

    方法一:使用SqlBulkCopy实现批量更新或添加数据. SqlBulkCopy类一般只能用来将数据批量插入打数据库中,如果数据表中设置了主键,出现重复数据的话会报错,如果没有设置主键,那么将会添加 ...

  4. oracle中sql语句的优化

    oracle中sql语句的优化 一.执行顺序及优化细则 1.表名顺序优化 (1) 基础表放下面,当两表进行关联时数据量少的表的表名放右边表或视图: Student_info   (30000条数据)D ...

  5. Javascript多线程引擎(三)

    Javascript多线程引擎(三) 完成对ECMAScript-262 3rd规范的阅读后, 列出了如下的限制条件 1. 去除正则表达式( 语法识别先不编写) 2. 去除对Function Decl ...

  6. 详解SpringMVC请求的时候是如何找到正确的Controller

    详解SpringMVC请求的时候是如何找到正确的Controller[附带源码分析] 目录 前言 源码分析 重要接口介绍 SpringMVC初始化的时候做了什么 HandlerExecutionCha ...

  7. 事件处理(Event Handlers) ng-click操作

    事件处理(Event Handlers) ng-click操作 step 10 本文主要通过介绍ng-click方法来对angularjs中的事件处理方法做个了解. 1.切换目录 git checko ...

  8. 多线程中lock用法的经典实例

    多线程中lock用法的经典实例 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.它可以把一段代码定义为互斥段(critical section),互斥段在一 ...

  9. media Queries实现一个响应式的菜单

    使用media Queries实现一个响应式的菜单   Media queries是CSS3引入的一个特性,使用它可以方便的实现各种响应式效果.在这个示例中我们将会使用media queries实现一 ...

  10. 程序媛也会画图 之 在ubuntu下用GIMP制作gif

    动画是什么?就是几张图片很快的顺序播放嘛,今天就画画gif小图吧,以后在演示效果图的时候也用的上. 1.准备一个小图 就用小绿人好了 2.打开GIMP 3.新建一个图片打开  文件 ->新建输入 ...