本文共 4322 字,大约阅读时间需要 14 分钟。
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{ ///Controller类的部分结构如下: IController只有一个方法-Execute,这个方法根据上下文来具体执行相应的逻辑,现在关键的问题是如何得到这个IController对象呢?/// 定义控制器所需的方法。 /// public interface IController { ////// 执行指定的请求上下文。 /// /// 请求上下文。 void Execute(RequestContext requestContext); }}
整个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了解了Controller的激活原理,我们还需要关注Controller的Action是如何被执行的。通过实现IContrller接口,我们为具有的Controller定义了一个具有如下定义的ControllerBase抽象基类。从中我们可以看到在实现的Execute方法中,ControllerBase通过一个实现了接口IActionInvoker的对象完成了针对Action方法的执行。controllerTypes = new List (); 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 namespaces = new List (); 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; }}
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 框架解密>及相关博客后的一个总结,前一段事件使用这种设计思想,设计了公司一个项目的架构,优点心得,故记录下来,感谢蒋金楠的书籍及博客。