zoukankan      html  css  js  c++  java
  • MVC源码解析

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

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

     if (HttpRuntime.UseIntegratedPipeline)
     {
           this._stepManager = new PipelineStepManager(this); //集成
     }
     else
     {
           this._stepManager = new ApplicationStepManager(this); //经典
     }
     this._stepManager.BuildSteps(this._resumeStepsWaitCallback);

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

    1. 经典模式

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

    //HttpApplication的内部类ApplicationStepManager
    internal override void BuildSteps(WaitCallback stepCallback)
    {
        ArrayList steps = new ArrayList();
        HttpApplication app = base._application;
        bool flag = false;
        UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
        flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);
        steps.Add(new HttpApplication.ValidateRequestExecutionStep(app));
        steps.Add(new HttpApplication.ValidatePathExecutionStep(app));
        if (flag)
        {
            steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); //Url Mapping
        }
        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 HttpApplication.MapHandlerExecutionStep(app)); //Handle Mapping
        app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
        steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());
        steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); //execute handler
        app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
        steps.Add(new HttpApplication.CallFilterExecutionStep(app)); //Filtering
        app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
        this._endRequestStepIndex = steps.Count;
        app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
        steps.Add(new HttpApplication.NoopExecutionStep());
        this._execSteps = new HttpApplication.IExecutionStep[steps.Count];
        steps.CopyTo(this._execSteps);
        this._resumeStepsWaitCallback = stepCallback;
    }

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

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

    从这里能看到, 他真的就是一个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. 集成模式

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

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

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

    [Flags]
    public enum RequestNotification
    {
        AcquireRequestState = 0x20,
        AuthenticateRequest = 2,
        AuthorizeRequest = 4,
        BeginRequest = 1,
        EndRequest = 0x800,
        ExecuteRequestHandler = 0x80,
        LogRequest = 0x400,
        MapRequestHandler = 0x10,
        PreExecuteRequestHandler = 0x40,
        ReleaseRequestState = 0x100,
        ResolveRequestCache = 8,
        SendResponse = 0x20000000,
        UpdateRequestCache = 0x200
    }

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

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

    public event EventHandler PostAuthenticateRequest
    {
        add
        {
            this.AddSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true);
        }
        remove
        {
            this.RemoveSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true);
        }
    } 

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

    public event EventHandler AuthenticateRequest
    {
        add
        {
            this.AddSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest);
        }
        remove
        {
            this.RemoveSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest);
        }
    }

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

    那么就看一下这个方法吧

    private void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification)
    {
        this.ThrowIfEventBindingDisallowed();
        this.Events.AddHandler(key, handler);
        if (this.IsContainerInitalizationAllowed)
        {
            PipelineModuleStepContainer moduleContainer = this.GetModuleContainer(this.CurrentModuleCollectionKey);
            if (moduleContainer != null)
            {
                SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler) handler);
                moduleContainer.AddEvent(notification, isPostNotification, step);
            }
        }
    }

    接着往下看AddEvent方法.

    internal void AddEvent(RequestNotification notification, bool isPostEvent, HttpApplication.IExecutionStep step)
    {
        int index = EventToIndex(notification);
        List<HttpApplication.IExecutionStep>[] listArray = null;
        if (isPostEvent)
        {
            if (this._modulePostSteps == null)
            {
                this._modulePostSteps = new List<HttpApplication.IExecutionStep>[0x20];
            }
            listArray = this._modulePostSteps;
        }
        else
        {
            if (this._moduleSteps == null)
            {
                this._moduleSteps = new List<HttpApplication.IExecutionStep>[0x20];
            }
            listArray = this._moduleSteps;
        }
        List<HttpApplication.IExecutionStep> list = listArray[index];
        if (list == null)
        {
            list = new List<HttpApplication.IExecutionStep>();
            listArray[index] = list;
        }
        list.Add(step);
    }

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

    接着看 AddEventMapping 方法:

    private void AddEventMapping(string moduleName, RequestNotification requestNotification, bool isPostNotification, IExecutionStep step)
    {
        this.ThrowIfEventBindingDisallowed();
        if (this.IsContainerInitalizationAllowed)
        {
            PipelineModuleStepContainer moduleContainer = this.GetModuleContainer(moduleName);
            if (moduleContainer != null)
            {
                moduleContainer.AddEvent(requestNotification, isPostNotification, step);
            }
        }
    }

    从这里能看到, 事件被注册到 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之前的那点事儿

    目录已同步

  • 相关阅读:
    回文自动机(PAM) 学习笔记
    UOJ#103. 【APIO2014】Palindromes PAM模板题
    UOJ#348. 【WC2018】州区划分
    STL 抄袭笔记
    ZJOI2019Day1 游记
    UOJ#370. 【UR #17】滑稽树上滑稽果 动态规划
    UOJ#24. 【IOI2014】Rail 交互题
    UOJ#373. 【ZJOI2018】线图 搜索,树哈希,动态规划
    UOJ#75. 【UR #6】智商锁 随机化算法 矩阵树定理
    AtCoder Grand Contest 031 (AGC031) D
  • 原文地址:https://www.cnblogs.com/elvinle/p/6269731.html
Copyright © 2011-2022 走看看