学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(三)

Controller的激活

ASP.NET MVC的路由系统通过注册的路由表对当前HTTP请求实施路由解析,从而得到一个用于封装路由数据的RouteData对象,这个过程是通过自定义的UrlRoutingModuleHttpApplicationPostResolveRequestCache事件进行注册实现的。由于得到的RouteData对象中已经包含了目标Controller的名称,我们需要根据该名称激活对应的Controller对象。

1.MvcRouteHandler

通过前面的介绍我们知道,继承自RouteBaseRoute类型具有一个类型为IRouteHandler借口的属性RouteHandler,它主要的用途就是用于根据指定的请求上下文(RequestContext)来获取一个HttpHandler对象。当GetRouteData方法被执行后,RouteRouteHandler属性值将反映在得到的RouteData的同名属性上,在默认的情况下,RouteRouteHandler属性是一个MvcRouteHandler对象。

对于我们的“迷你版”ASP.NET MVC框架来说,MvcRouteHandler是一个具有如下定义的类型。在实现的GetHttpHandler方法中它会直接返回一个MvcHandler对象。

public class MvcRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new MvcHandler(requestContext);
        }
    }

2.MvcHandler

整个ASP.NET MVC框架是通过自定义的HttpModuleHttpHandlerASP.NET进行扩展构建起来的,这个自定义的HttpModule类型就是UrlRouteModule,而这个自定义的HttpHandler类型则是需要重点介绍的MvcHandler

UrlRouteModule在利用路由表对当前请求实施路由解析并得到封装路由数据的RouteData对象后,会调用其RouteHandlerGetHttpHandler方法得到一个HttpHandler对象,然后将其映射到当前的HTTP上下文。由于RouteDataRouteHandler来源于对应Route对象的RouteHandler,而后者在默认的情况下是一个MvcRouteHandler对象,所有默认情况下用于处理HTTP请求的就是这么一个MvcHandler对象。MvcHandler实现了对Controller对象的激活和对目标Action方法的执行。

如下面的代码片段所示,MvcHandler具有一个类型为RequestContext的属性,它表示当前请求上下文,该属性在构造函数中指定。MvcHandlerProcessRequest方法中实现了对Controller对象的激活和执行。

public class MvcHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { return false; }
        }
        public RequestContext RequestContext { get; private set; }
 
        public MvcHandler(RequestContext requestContext)
        {
            this.RequestContext = requestContext;
        }
 
        public void ProcessRequest(HttpContext context)
        {
            string controllerName = this.RequestContext.RouteData.Controller;
            IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();
            IController controller = controllerFactory.CreateController(this.RequestContext, controllerName);
            controller.Execute(this.RequestContext);
        }
    }


3.ControllerControllerFactory

我们为Controller定义了一个接口IController。如下所示,该接口具有唯一的方法Execute表示对当前Controller对象的执行。该方法在MvcHandlerProcessRequest方法中被调用,而传入该方法的参数是表示当前请求上下文的RequestContext对象。

public interface IController
    {
        void Execute(RequestContext requestContext);
    }

MvcHandler的定义可以看到,Controller对象的激活是通过工厂模式实现的,我们为激活Controller的工厂定义了IControllerFactory接口。该接口具有唯一的方法CreateController,该方法根据当前请求上下文和通过路由解析得到的目标Controller的名称激活相应的Controller对象。

 public interface IControllerFactory
    {
        IController CreateController(RequestContext requestContext, string controllerName);
    }

MvcHandlerProcessRequest方法中,他通过ControllerBuilder的静态属性Current得到当前的ControllerBuilder对象,并调用其GetControllerFactory方法获得当前的ControllerFactory。接下来MvcHandler通过从RequestContext中提取的RequestData对象获得目标Controller的名称,最后将它连同RequestContext一起作为参数调用ControllerFactoryCreateController方法实现对目标Controller对象的创建。

ControllerBuilder的整个定义如下,表示当前ControllerBuilder的静态只读属性Current在静态构造函数中被创建,其SetControllerFactoryGetControllerFactory方法用于ControllerFactory的注册和获取。

 

public class ControllerBuilder
    {
        private Func<IControllerFactory> factoryThunk;
        public static ControllerBuilder Current { get; private set; }
 
        static ControllerBuilder()
        {
            Current = new ControllerBuilder();
        }
 
        public IControllerFactory GetControllerFactory()
        {
            return factoryThunk();
        }
 
        public void SetControllerFactory(IControllerFactory controllerFactory)
        {
            factoryThunk = () => controllerFactory;
        }
    }

在之前我们建立在自定义ASP.NET MVC框架的Web应用,我们就是通过当前的ControllerBuilder来注册ControllerFactory。注册的ControllerFactory类型为DefaultControllerFactory

public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory());
        }
    }


作为默认ControllerFactoryDefaultControllerFactory类型定义如下。由于激活Controller对象的前提是能够正确的解析出Controller的真实类型,作为CreateController方法输入参数的controllerName仅仅表示Controller的名称,所以我们需要加上Controller字符后缀作为类型名称。在DefaultControllerFactory类型被加载的时候(静态构造函数被调用),他通过BuilderMessage加载所有被引用的程序集,得到所有实现了接口的IController的类型并将其缓存起来。在CreateController方法中,DefaultControllerFactory根据Controller的名称从保存的Controller类型列表中得到对应的Controller类型,并通过反射的方式创建它。

public class DefaultControllerFactory : IControllerFactory
    {
        private static List<Type> controllerTypes = new List<Type>();
 
        static DefaultControllerFactory()
        {
            foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
            {
                foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)))
                {
                    controllerTypes.Add(type);
                }
            }
        }
 
        public IController CreateController(RequestContext requestContext,string controllerName)
        {
            string typeName = controllerName + "Controller";
            Type controllerType = controllerTypes.FirstOrDefault(c => string.Compare(typeName, c.Name, true) == 0);
            if (null == controllerType)
            {
                return null;
            }
            return (IController)Activator.CreateInstance(controllerType);
        }
    }

上面我们详细地介绍了 Controller 的激活原理,现在将关注点返回到 Controller 自身。我们通过实现 IController 接口为所有的 Controller 定义了一个具有如下定义的 ControllerBase 抽象基类,从中可以看到在实现的 Execute 方法中 ControllerBase 通过一个实现了接口 IActionInvoker 的对象完成了针对 Action 方法的执行。

public abstract class ControllerBase : IController
    {
        protected IActionInvoker ActionInvoker { get; set; }
 
        public ControllerBase()
        {
            this.ActionInvoker = new ControllerActionInvoker();
        }
 
        public void Execute(RequestContext requestContext)
        {
            ControllerContext context = new ControllerContext
            {
                RequestContext = requestContext,
                Controller = this
            };
            string actionName = requestContext.RouteData.ActionName;
            this.ActionInvoker.InvokeAction(context, actionName);
        }
    }
在下一章我们讲解Action的执行。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值