ASP.NET MVC Controller的激活机制

ASP.NET MVC 从最初的版本到现在,已经不断地完善,发展成为一个非常成熟的、设计优良的Web框架。从剖析ASP.NET MVC的一些设计及机制能够加深我们对于MVC模式的理解,帮助我们更好地扩展ASP.NET MVC框架,甚至使用设计思想,设计其他的软件架构。

1.概要

ASP.NET MVC 与传统的ASP.NET最大的区别在于,用户通过浏览器发起的请求不再是直接访问某个物理文件,而是访问某个Action。由Action及传给Action的参数决定如何呈现View给用户。这种方式大大地提升了应用程序的灵活性,使得One app for all platform的设计初衷得以实现。这个架构最重要的连个方面,一个是路由,一个是控制器。路由的机制今后再探讨,这里我们聊一聊控制器(Controller)的激活机制。

2.Controller的执行

从Controller类的源码中我们可以看到,Controller类实现了IController接口。

using System.Web.Routing;

namespace System.Web.Mvc
{
  /// <summary>
  /// 定义控制器所需的方法。
  /// </summary>
  public interface IController
  {
    /// <summary>
    /// 执行指定的请求上下文。
    /// </summary>
    /// <param name="requestContext">请求上下文。</param>
    void Execute(RequestContext requestContext);
  }
}
Controller类的部分结构如下:

IController只有一个方法-Execute,这个方法根据上下文来具体执行相应的逻辑,现在关键的问题是如何得到这个IController对象呢?

整个ASP.NET MVC框架是通过自定义的HttpModule和HttpHandler对象ASP.NET进行扩展实现的。自定义HttpModule就是UrlRoutingModule,而这个自定义的HttpHandler则是我们要重点介绍的MvcHandler。下面的的代码片断体现了MvcHandler的整个定义,在实现的ProcessRequest中实现了对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);
   }
}

这里通过的核心方法ProcessRequest里面通过RouteData里面解析出来ControllerName,ControllerFactory通过名字和上下文得到IController实例,使用该实例调用Execute方法,这和我们上面讲到的完全吻合。

那么现在的问题时,如何获取到正确的Controller呢?这里面的机制又是怎么样的?

激活Controller类型的前提是能够正确解析出Controller的真实类型。作为CreateController方法输入参数的controllerName仅仅表示Controller的名称,我们需要加上Controller字符后缀作为类型名称。此外我们还需要得到类型的命名空间,而命名空间具有两个来源,即RouteData和当前ControllerBuilder。在DefualtControllerFactory初始化过程中,我们通过BuildManager加载所有应用的程序集,并加载所有实现了接口IController的类型并保存起来,而在CreateController方法中根据Controller的名称和命名空间从保存的Controller类型列表中得到对应的Controller类型,并通过反射的方式创建它。

注意:这里加载实现了IController接口的程序集,这个动作非常重要,确保了Controller的正确激活,因为Controller类都是实现了IController接口的,即被打上了某种标签的,通过实现是否了IController接口来决定是否加载这个程序集,从而确保了有效识别。

下面的代码演示和如何做到这一点

public class DefaultControllerFactory : IControllerFactory
{
   private List<Type> controllerTypes = new List<Type>();
   public 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";
      List<string> namespaces = new List<string>();
      namespaces.AddRange(requestContext.RouteData.Namespaces);
      namespaces.AddRange(ControllerBuilder.Current.DefaultNamespaces);
      foreach (var ns in namespaces)
      {
           string controllerTypeName = string.Format("{0}.{1}", ns, typeName);
           Type controllerType = controllerTypes.FirstOrDefault(type => string.Compare(type.FullName, controllerTypeName, true) == 0);
           if (null != controllerType)
           {
               return (IController)Activator.CreateInstance(controllerType); 
           }
       }            
       return null;
   }
}
了解了Controller的激活原理,我们还需要关注Controller的Action是如何被执行的。通过实现IContrller接口,我们为具有的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);
     }
}
3.说明

这篇文章是看过蒋金楠的<ASP.NET MVC 框架解密>及相关博客后的一个总结,前一段事件使用这种设计思想,设计了公司一个项目的架构,优点心得,故记录下来,感谢蒋金楠的书籍及博客。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值