博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASP.NET MVC Controller的激活机制
阅读量:2236 次
发布时间:2019-05-09

本文共 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{  ///   /// 定义控制器所需的方法。  ///   public interface IController  {    ///     /// 执行指定的请求上下文。    ///     /// 请求上下文。    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
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; }}
了解了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 框架解密>及相关博客后的一个总结,前一段事件使用这种设计思想,设计了公司一个项目的架构,优点心得,故记录下来,感谢蒋金楠的书籍及博客。

你可能感兴趣的文章
(PAT 1019) General Palindromic Number (进制转换)
查看>>
(PAT 1073) Scientific Notation (字符串模拟题)
查看>>
(PAT 1080) Graduate Admission (排序)
查看>>
Play on Words UVA - 10129 (欧拉路径)
查看>>
mininet+floodlight搭建sdn环境并创建简答topo
查看>>
【linux】nohup和&的作用
查看>>
Set、WeakSet、Map以及WeakMap结构基本知识点
查看>>
【NLP学习笔记】(一)Gensim基本使用方法
查看>>
【NLP学习笔记】(二)gensim使用之Topics and Transformations
查看>>
【深度学习】LSTM的架构及公式
查看>>
【python】re模块常用方法
查看>>
剑指offer 19.二叉树的镜像
查看>>
剑指offer 20.顺时针打印矩阵
查看>>
剑指offer 21.包含min函数的栈
查看>>
剑指offer 23.从上往下打印二叉树
查看>>
剑指offer 25.二叉树中和为某一值的路径
查看>>
剑指offer 60. 不用加减乘除做加法
查看>>
Leetcode C++《热题 Hot 100-15》437.路径总和III
查看>>
Leetcode C++《热题 Hot 100-17》461.汉明距离
查看>>
Leetcode C++《热题 Hot 100-18》538.把二叉搜索树转换为累加树
查看>>