• asp.net MVC 应用程序的生命周期(下)


    看看上面的UrlRoutingModule源码里面是怎么实现Init方法的,Init()方法里面我标注红色的地方:

    application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);

    这一步至关重要哈,看到没有,就是对我们在HttpApplication那19个事件中的PostResolveRequestCache事件的注册。

    注册的方法是OnApplicationPostResolveRequestCache事件。也就是说HttpApplication对象在执行到PostResolveRequestCache这个事件的时候,就会执行OnApplicationPostResolveRequestCache事件。决定是MVC机制处理请求的关键所在就是OnApplicationPostResolveRequestCache事件。

    从源码中我们看出,OnApplicationPostResolveRequestCache事件执行的时候,最终执行了PostResolveRequestCache这个方法。最关键的地方呢就在这里了。

    当请求到达UrlRoutingModule的时候,UrlRoutingModule取出请求中的Controller、Action等RouteData信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler,即MVCRouteHandler。

    我们可以看下UrlRoutingModule的源码来看看,以下是几句核心的代码:

    我们再分析一下这个方法的源码:

    public virtual void PostResolveRequestCache(HttpContextBase context)

    {

        // 通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例

        RouteData routeData = this.RouteCollection.GetRouteData(context);

        if (routeData != null)

        {

            // 再从RouteData中获取MVCRouteHandler

            IRouteHandler routeHandler = routeData.RouteHandler;

            ......

            if (!(routeHandler is StopRoutingHandler))

            {

                ......

                // 调用 IRouteHandler.GetHttpHandler(),获取的IHttpHandler 类型实例,它是由 IRouteHandler.GetHttpHandler获取的,这个得去MVC的源码里看

                IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

                ......

                // 合适条件下,把之前将获取的IHttpHandler 类型实例 映射到IIS HTTP处理管道中

                context.RemapHandler(httpHandler);

            }

        }

    }

    看到了吧,通过路由规则,返回的不为空,说明匹配正确,关于路由规则的匹配,说起来也不短,这里就不大幅介绍,有时间下次再开篇详解路由机制。匹配成功后,返回一个RouteData类型的对象,RouteData对象都有些什么属性呢?看看这行源码: IRouteHandler routeHandler = routeData.RouteHandler;或者看源码我们知道,RouteDate有一个RouteHandler属性。

    那么UrlRouting Module是如何选择匹配规则的呢?

    我们看看我们新建的MVC应用程序,在App_Start文件夹下面有一个RouteConfig.cs类,这个类的内容如下:

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Web;

    using System.Web.Mvc;

    using System.Web.Routing;

    namespace ApiDemo

    {

        public class RouteConfig

        {

            public static void RegisterRoutes(RouteCollection routes)

            {

                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

                routes.MapRoute(

                    name: "Default",

                    url: "{controller}/{action}/{id}",

                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

                );

            }

        }

    }

    我们在这个类里面,主要是给路由表添加路由规则。在看看上面的UrlRoutingModule类,里面有一个RoutCollection属性,所以UrlRoutingModule能够获取路由表中的所有规则,这里值得注意的是,路由规则的匹配是有顺序的,如果有多个规则都能够匹配,UrlRoutingModule至选择第一个匹配的规则就返回,不再继续往下匹配了。相反的如果一个请求,没有匹配到任何路由,那么该请求就不会被处理。

    这里返回的RouteData里的RouteHandler就是MVCRouteHandler。为什么呢?那我们继续往下看RouteHandler。

    RouteHandler                                                        

    生成MvcHander

    在上面路由匹配的过程中,与匹配路由相关联的MvcRouteHandler ,MvcRouteHandler 实现了IRouteHandler 接口。

    MvcRouteHandler 主要是用来获取对MvcHandler的引用。MvcHandler实现了IhttpHandler接口。

    MVCRouteHandler的作用是用来生成实现IHttpHandler接口的MvcHandler。而我们前面说过最终处理请求的都是相对应的HttpHandler。

    那么处理MVC请求的自然就是这个MvcHandler。所以这里返MvcRouteHandler至关重要:

    那么,MvcRouteHandler从何而来呢?众所周知,ASP.NET MVC项目启动是从Global中的Application_Start()方法开始的,那就去看看它:

    public class MvcApplication : System.Web.HttpApplication

        {

            protected void Application_Start()

            {

                RouteConfig.RegisterRoutes(RouteTable.Routes);

                BundleConfig.RegisterBundles(BundleTable.Bundles);

            }

        }

        public class RouteConfig

        {

            public static void RegisterRoutes(RouteCollection routes)

            {

                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    System.Web.Mvc.RouteCollectionExtensions

                routes.MapRoute(

                    name: "Default",

                    url: "{controller}/{action}/{id}",

                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

                );

            }

        }

    看看我上面标红的代码:这是路由注册,玄机就在这里。那我们去看看MapRoute源码就知道咯:

    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) 

            {

                ......

                Route route = new Route(url, new MvcRouteHandler())             {

                    Defaults = new RouteValueDictionary(defaults),

                    Constraints = new RouteValueDictionary(constraints),

                    DataTokens = new RouteValueDictionary()

                };

                ......

                return route;

            }

    看看我们5-8行代码,在MVC应用程序里,在路由注册的时候,我们就已经给他一个默认的HttpRouteHandler对象,就是 New MvcRouteHandler().现在我们反推回去,我们MVC程序在路由注册的时候就已经确定了HttpRouteHandler为MvcRouteHandler,那么当我们在前面PostResolveRequestCache方法里,当我们的请求与路由匹配成功后,自然会返回的是MvcRouteHandler。

    好啦,MvcRouteHandler生成了。那么MvcRouteHandler能做什么呢?又做了什么呢?

    再回头看看 PostResolveRequestCache方法,在成功获取到IHttpRouteHandler对象即MvcRouteHandler之后,又做了下面这一个操作:

    IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

    我们看看这个IHttpHandler 的源码:

    namespace System.Web.Routing

    {  

        public interface IRouteHandler

        {       

            IHttpHandler GetHttpHandler(RequestContext requestContext);

        }

    }

    有一个GetHttpHandler的方法,恰好就调用了这个方法。那我们看看MvcRouteHandler是怎么实现这个GetHttpHandler的呢:

    public class MvcRouteHandler : IRouteHandler

    {

        // Fields

        private IControllerFactory _controllerFactory;

        // Methods

        public MvcRouteHandler()

        {

        }

        public MvcRouteHandler(IControllerFactory controllerFactory)

        {

            this._controllerFactory = controllerFactory;

        }

        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)

        {

            requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));

            return new MvcHandler(requestContext);

        }

        protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)

        {

            string str = (string) requestContext.RouteData.Values["controller"];

            if (string.IsNullOrWhiteSpace(str))

            {

                throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);

            }

            IControllerFactory factory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();

            return factory.GetControllerSessionBehavior(requestContext, str);

        }

        IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)

        {

            return this.GetHttpHandler(requestContext);

        }

    }

    看第16-20行代码,这时候应该明白了吧。顺理成章的返回了MvcHandler对象。记得我们前面说过,请求最终是被相对应的HttpHander对象处理的。

    MvcHandler就是那个用来处理Mvc请求的HttpHandler。MvcRouteHandler把请求交给了MvcHandler去做请求处理管道中后续事件的处理操作了。

    下面我们就看看MvcHandler做了些什么:

    MvcHandler                                                           

    MvcHandler就是最终对request进行处理。

    MvcHandler的定义如下:

    我们可以看到MvcHandler就是一个普通的Http  Handler.我们知道一个http handler需要实现一个ProcessRequest()的方法,这个方法就是处理request的核心。所以MvcHandler实现了ProcessRequest()方法。

    ProcessRequest主要功能:

    (1)在ASP.NET MVC中,会调用MvcHandler的ProcessRequest()方法,此方法会激活具体请求的Controller类对象,触发Action方法,返回ActionResult实例。

    (2)如果ActionResult是非ViewResult,比如JsonResult, ContentResult,这些内容将直接被输送到Response响应流中,显示给客户端;如果是ViewResult,就会进入下一个渲染视图环节。

    (3)在渲染视图环节,ViewEngine找到需要被渲染的视图,View被加载成WebViewPage<TModel>类型,并渲染生成Html,最终返回Html。

    ProcessRequest()定义如下:

    // Copyright (c) Microsoft Open Technologies, Inc.<pre>// All rights reserved. See License.txt in the project root for license information.

    void IHttpHandler.ProcessRequest(HttpContext httpContext) 

    {

        ProcessRequest(httpContext);

    }

    protected virtual void ProcessRequest(HttpContext httpContext) 

    {

        HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);

                    ProcessRequest(iHttpContext);

    }

    protected internal virtual void ProcessRequest(HttpContextBase httpContext) {

        SecurityUtil.ProcessInApplicationTrust(() => {

            IController controller;

            IControllerFactory factory;

            ProcessRequestInit(httpContext, out controller, out factory);

        try

        {

            controller.Execute(RequestContext);

        }

        finally

        {

            factory.ReleaseController(controller);

        }

        });

    }

    从上面的代码可以看出调用了一个ProcessRequestInit()方法,定义如下:

    private void ProcessRequestInit(HttpContextBase httpContext, 

                 out IController controller, out IControllerFactory factory) {

        // If request validation has already been enabled, make it lazy.

        // This allows attributes like [HttpPost] (which looks

        // at Request.Form) to work correctly without triggering full validation.

        bool? isRequestValidationEnabled = 

           ValidationUtility.IsValidationEnabled(HttpContext.Current);

        if (isRequestValidationEnabled == true) {

            ValidationUtility.EnableDynamicValidation(HttpContext.Current);

        }

        AddVersionHeader(httpContext);

        RemoveOptionalRoutingParameters();

        // Get the controller type

        string controllerName = RequestContext.RouteData.GetRequiredString("controller");

        // Instantiate the controller and call Execute

        factory = ControllerBuilder.GetControllerFactory();

        controller = factory.CreateController(RequestContext, controllerName);

        if (controller == null) {

            throw new InvalidOperationException(

            String.Format(

                CultureInfo.CurrentCulture,

                MvcResources.ControllerBuilder_FactoryReturnedNull,

                factory.GetType(),

                controllerName));

        }

    }

    在ProcessRequestInit()方法中首先创建了ControllerFactory()的对象 factory.然后ControllerFactory创建了相关Controller的实例.最终调用了Controller的Excute()方法。

    好我们再来看看ControllerFactory:

    ControllerFactory           

                                           

    主要是用来生成Controller对象

    ControllerFactory实现了接口IControllerFactory.

    Controller         

                                                                           

    到这里我们大概就知道了,MvcHandler通过ProcessRequest()方法最终创建了Controller对象,这里我们都应该知道,Controller里面包含很多的Action方法,每一次请求至少一个Action方法会被调用。

    为了明确的实现IController接口,框架里面有一个ControllerBase的类已经实现了IController接口,其实我们自己的Controller也可以不继承ControllerBase,只要实现IController接口即可。

    public abstract class ControllerBase : IController

    {

        protected virtual void Execute(RequestContext requestContext)

        {

            if (requestContext == null)

            {

                throw new ArgumentNullException("requestContext");

            }

            if (requestContext.HttpContext == null)

            {

                throw new ArgumentException(

                  MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, 

                  "requestContext");

            }

            VerifyExecuteCalledOnce();

            Initialize(requestContext);

            using (ScopeStorage.CreateTransientScope())

            {

                ExecuteCore();

            }

        }

        protected abstract void ExecuteCore(); 

     // .......

    controller对象实际上使用ActionInvoker来调用Action方法的,当Controller对象被创建后,会执行Controller对象的基类ControllerBase类里面的Excute方法。Excute方法又调用了ExcuteCore()方法。Controller类里面实现了ExcuteCore()方法。ExcuteCore调用了ActionInvoker的InvokerAction方法来调用Action方法。

    ActionInvoker    

                                                       

    ActionInvoker方法有很重要的责任来查找Controller中的Action方法并且调用。

    ActionInvoker是一个实现了IActionInvoker接口的对象:

    bool InvokeAction(

                   ControllerContext controllerContext,

                   string actionName

    Controller类里面暴露了一个ActionInvoker 属性,会返回一个ControllerActionInvoker 。ActionInvoker通过CreateActionInvoker()方法来创建ControllerActionInvoker对象。

    public IActionInvoker ActionInvoker {

        get {

            if (_actionInvoker == null) {

                _actionInvoker = CreateActionInvoker();

            }

            return _actionInvoker;

        }

        set {

           _actionInvoker = value;

        }

    }

       protected virtual IActionInvoker CreateActionInvoker() {

        return new ControllerActionInvoker();

    }

    我们看到CreateActionInvoker()是一个Virtual方法,我们可以实现自己的ActionInvoker.

    ActionInvoker类需要匹配Controller中详细的Action来执行,而这些详细的信息是由ControllerDescriptor 提供的。ControllerDescriptor 和ActionDescriptor在ActionInvoker中扮演重要的角色。这两个分别是对Controler和Action的详细描述。ControllerDescriptor 描述了Controller的相关信息比如name,action,type等。

    ActionDescriptor 描述了Action相关的详情,比如name,controller,parameters,attributes和fiflters等。

    ActionDescriptor 中一个中要的方法就是FindAction(),这个方法返回一个ActionDescriptor 对象,所以ActionInvoker知道该调用哪个Action。

     ActionResult           

    到目前为止,我们看到了Action方法被ActionInvoker调用。所有的Action方法有一个特性,就是返回一个ActionResult类型的数据。

    public abstract class ActionResult

      {

          public abstract void ExecuteResult(ControllerContext context);

      }

    ExecuteResult()是一个抽象方法,所以不同的子类可以提供不同的ExecuteResult()实现。

    ActionResult执行后响应输出到客户端。

    ViewEngine                 

                                              

    ViewResult几乎是大部分应用程序的返回类型,主要通过ViewEngine引擎来展示view的。ViewEngine可能主要就是生成Html元素的引擎。

    Framwork提供了2种引擎,Razor View Engine 和Web Form View Engine.如果你想自定义引擎,你可以创建一个引擎只要实现IViewEngine接口即可。

    IViewEngine 有下面几个方法:

    1、FindPartialView :当controller需要返回一个PartialView的时候,FindPartialView方法 就会被调用。

    2、FindView 

    3、ReleaseView :主要用来有ViewEngine释放资源

    ViewResultBase 和ViewResult是比较重要的两个类。ViewResultBase 包含下面的实现代码:

    if (View == null)

               {

                   result = FindView(context); //calls the ViewResult's FindView() method

                   View = result.View;

               }

               ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);

               View.Render(viewContext, context.HttpContext.Response.Output);

    protected abstract ViewEngineResult FindView(ControllerContext context); //this is implemented by                                                                        

    //the ViewResult

    protected override ViewEngineResult FindView(ControllerContext context)

        {

            ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);

            if (result.View != null)

            {

                return result;

            }

           //rest of the code omitted 

        }

    当ViewResult的方法ExecuteResult被调用后,ViewResultBase 的ExecuteResult 方法被调用,然后ViewResultBase 调用ViewResult的FindView 。

    紧接着ViewResult 返回ViewEngineResult,之后ViewEngineResult调用Render()方法来绘制html输出响应。

    总结:如果我们理解了整个过程中发生了什么,哪些类和哪些方法被调用,我们就可以在需要扩展的地方轻松的进行扩展。

  • 相关阅读:
    activity学习参考
    Activity工作流入门
    Mysql 命令查看函数,触发器。。
    本地redis下载并启动
    Could not initialize class com.taobao.diamond.client.impl.DiamondEnvRepo
    mysql把表(表已有数据)的某列属性由空到非空,以及常用列操作
    easyexcel导出两种方式response返回文件流下载和保存到服务器返回下载链接
    EasyExcel导出带下拉框,并解决导出之后打开总是显示发现不可读取内容
    el-table的fixed固定列属性导致数据错位
    el-input按回车时,解决同时触发回车和失焦事件的问题
  • 原文地址:https://www.cnblogs.com/zhangxiaolei521/p/5691345.html
Copyright © 2020-2023  润新知