ASP.NET MVC Controller激活系统详解:默认实现

Controller激活系统最终通过注册的ControllerFactory创建相应的Conroller对象,如果没有对ControllerFactory类型或者类型进行显式注册(通过调用当前ControllerBuilder的SetControllerFactory方法),默认使用的是一个DefaultControllerFactory对象,我们现在就来讨论实现在DefaultControllerFactory类型中的默认Controller激活机制。

一、Controller类型的解析

激活目标Controller对象的前提是能够正确解析出对应的Controller类型。对于DefaultControllerFactory来,用于解析目标Controller类型的信息包括:通过与当前请求匹配的路由对象生成的RouteData(其中包含Controller的名称和命名空间)和包含在当前ControllerBuilder中的命名空间。很对读者可以首先想到的是通过Controller名称得到对应的类型,并通过命名空间组成Controller类型的全名,最后遍历所有程序集以此名称去加载相应的类型即可。

这貌似一个不错的解决方案,实际上则完全不可行。不要忘了作为请求地址URL一部分的Controller名称是不区分大小写的,而类型名称则是区分大小的;不论是注册路由时指定的命名空间还是当前ControllerBuilder的默认命名空间,有可能是包含统配符(*)。由于我们不能通过给定的Controller名称和命名空间得到Controller的真实类型名称,自然就不可能通过名称去解析Controller的类型了。

ASP.NET MVC的Controller激活系统反其道而行之。它先遍历通过BuildManager的静态方法GetReferencedAssemblies方法得到的编译Web应用所使用的程序集,通过反射得到所有实现了接口IController的类型,最后通过给定的Controller的名称和命名空间作为匹配条件在这个预先获取的类型列表中得到目标Controller的类型。

实例演示:创建一个自定义ControllerFactory模拟Controller默认激活机制

为了让读者对默认采用的Controller激活机制,尤其是Controller类型的解析机制有一个深刻的认识,我们通过一个自定义的ControllerFactory来模拟其中的实现。由于我们采用反射的方式来创建Controller对象,所以我们将该自定义ControllerFactory起名为ReflelctionControllerFactory。[源代码从这里下载]

   1: public class ReflelctionControllerFactory : IControllerFactory   2: {   3:     //其他成员   4:     private static List<Type> controllerTypes;   5:     static ReflelctionControllerFactory()   6:     {   7:         controllerTypes = new List<Type>();   8:         foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())   9:         {  10:             controllerTypes.AddRange(assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)));  11:         }  12:     }  13:  14:     public IController CreateController(RequestContext requestContext, string controllerName)  15:     {  16:         Type controllerType = this.GetControllerType(requestContext.RouteData, controllerName);  17:         if (null == controllerType)  18:         {  19:             throw new HttpException(404, "No controller found");  20:         }  21:         return (IController)Activator.CreateInstance(controllerType);  22:     }  23:  24:     private static bool IsNamespaceMatch(string requestedNamespace, string targetNamespace)  25:     {  26:         if (!requestedNamespace.EndsWith(".*", StringComparison.OrdinalIgnoreCase))  27:         {  28:             return string.Equals(requestedNamespace, targetNamespace, StringComparison.OrdinalIgnoreCase);  29:         }  30:         requestedNamespace = requestedNamespace.Substring(0, requestedNamespace.Length - ".*".Length);  31:         if (!targetNamespace.StartsWith(requestedNamespace, StringComparison.OrdinalIgnoreCase))  32:         {  33:             return false;  34:         }  35:         return ((requestedNamespace.Length == targetNamespace.Length) || (targetNamespace[requestedNamespace.Length] == '.'));  36:     }  37:  38:    private Type GetControllerType(IEnumerable<string> namespaces, Type[] controllerTypes)  39:     {  40:         var types = (from type in controllerTypes  41:                         where namespaces.Any(ns => IsNamespaceMatch(ns, type.Namespace))  42:                         select type).ToArray();  43:         switch (types.Length)  44:         {  45:             case 0: return null;  46:             case 1: return types[0];  47:             default: throw new InvalidOperationException("Multiple types were found that match the requested controller name.");  48:         }  49:     }  50:  51:     protected virtual Type GetControllerType(RouteData routeData, string controllerName)  52:     {  53:         //省略实现  54:     }  55: }

如上面的代码片断所示,ReflelctionControllerFactory具有一个静态的controllerTypes字段由于保存所有Controller的类型。在静态构造函数中,我们调用BuildManager的GetReferencedAssemblies方法得到所有用于编译Web应用的程序集,并从中得到所有实现了IController接口的类型,这些类型全部被添加到通过静态字段controllerTypes表示的类型列表。

Controller类型的解析实现在受保护的GetControllerType方法中,在用于最终激活Controller对象的CreateController方法中,我们通过调用该方法得到与指定RequestContext和Controller名称相匹配的Controller类型,最终通过调用Activator的静态方法CreateInstance根据该类型创建相应的Controller对象。如果不能找到匹配的Controller类型(GetControllerType方法返回Null),则抛出一个HTTP状态为404的HttpException。

ReflelctionControllerFactory中定义了两个辅助方法,IsNamespaceMatch用于判断Controller类型真正的命名空间是否与指定的命名空间(可能包含统配符)相匹配,在进行字符比较过程中是忽略大小写的。私有方法GetControllerType根据指定的命名空间列表和类型名称匹配的类型数组得到一个完全匹配的Controller类型。如果得到多个匹配的类型,直接抛出InvalidOperation异常,并提示具有多个匹配的Controller类型;如果找不到匹配类型,则返回Null。

在如下所示的用于解析Controller类型的GetControllerType方法中,我们从预先得到的所有Controller类型列表中筛选出类型名称与传入的Controller名称相匹配的类型。我们首先通过路由对象的命名空间对 之前 得到的类型列表进行进一步筛选,如果能够找到一个唯一的类型,则直接将其作为Controller的类型返回。为了确定是否采用后备命名空间对Controller类型进行解析,我们从作为参数参数的RouteData对象的DataTokens中得到获取一个Key为“UseNamespaceFallback”的元素,如果该元素存在并且值为False,则直接返回Null。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索controller
, 方法
, 类型
, 激活
, 空间
, control
, 名称
, Controllers
, controllers详解
, 反射机制详解
, 默认controller
模拟命名空间
,以便于您获取更多的相关知识。

时间: 2024-11-02 00:23:23

ASP.NET MVC Controller激活系统详解:默认实现的相关文章

ASP.NET MVC Controller激活系统详解:总体设计

我们将整个ASP.NET MVC框架划分为若干个子系统,那么针对请求上下文激活目标Controller对象的子系统被我们成为Controller激活系统.在正式讨论Controller对象具体是如何被创建爱之前,我们先来看看Controller激活系统在ASP.NET MVC中的总体设计,了解一下组成该子系统的一些基本的组件,以及它们对应的接口或者抽象类是什么. 一.Controller 我们知道作为Controller的类型直接或者间接实现了IController接口.如下面的代码片断所示,I

ASP.NET MVC Controller激活系统详解:IoC的应用[下篇]

[上篇]除了通过自定义ControllerFactory的方式引入IoC之外,在使用默认DefaultControllerFactory情况下也可以通过一些扩展使基于IoC的Controller激活成为可能.主要的方式就是自定义ControllerActivator和 DependencyResolver. 四.ControllerActivator V.S. DependencyResolver 如下面的代码片断所示,DefaultControllerFactory具有两个构造函数重载,其中一

ASP.NET MVC Controller激活系统详解:IoC的应用[上篇]

所谓控制反转(IoC: Inversion Of Control)简单地说就是应用本身不负责依赖对象的创建和维护,而交给一个外部容器来负责.这样控制权就由应用转移到了外部IoC容器,控制权就实现了所谓的反转.比如在类型A中需要使用类型B的实例,而B实例的创建并不由A来负责,而是通过外部容器来创建.通过IoC的方式是实现针对目标Controller的激活具有重要的意义. 一.从Unity来认识IoC 有时我们又将IoC称为依赖注入(DI: Dependency Injection).所谓依赖注入,

ASP.NET MVC数组模型绑定详解_实用技巧

在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例: <input type="text" name="[0].Name" /> <input type="text" name="[1].Name" /> <input type="text" name="[2].Name" />

Win8激活方式详解

Windows的用户恐怕都绕不开一个问题,那就是激活.最近,尝鲜Win8的用户越来越多,但漂在屏幕上的激活提醒实在让人看了很不爽,因此关于Win8激活的问题又开始成为了网上热门的话题. 今天笔者就将详细地介绍一下Win8的激活方法,大家只要照着步骤一步步进行操作,相信都可以完成Win8的激活. 网络常规激活 Win8的激活方法目前有两种较为方便的,一种为联网激活,还有一种为电话激活,主要应对在断网环境下激活系统.首先我们来看看联网激活. 在Win7和之前的XP版本中,我们一般会右击"我的电脑&q

AngularJS tab栏实现和mvc小案例实例详解

tab栏: 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Tab 标签</title> <style> body { margin: 0; padding: 0; background-color: #F7F7F7; } .tabs { width: 400px; margin: 3

.NET/ASP.NET MVC Controller 控制器(IController控制器的创建过程)

阅读目录: 1.开篇介绍 2.ASP.NETMVC IControllerFactory 控制器工厂接口 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂 4.ASP.NETMVC ControllerBuilder 控制器创建入口设置 5.ASP.NETMVC 自定义IControllerFactory 1]开篇介绍 上一篇文章".NET/ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)"详细的讲解了MvcH

ExtJS 4.2 教程-08:布局系统详解

ExtJS 4.2 系列教程导航目录: ExtJS 4.2 教程-01:Hello ExtJS ExtJS 4.2 教程-02:bootstrap.js 工作方式 ExtJS 4.2 教程-03:使用Ext.define自定义类 ExtJS 4.2 教程-04:数据模型 ExtJS 4.2 教程-05:客户端代理(proxy) ExtJS 4.2 教程-06:服务器代理(proxy) ExtJS 4.2 教程-07:Ext.Direct ExtJS 4.2 教程-08:布局系统详解 今天我们来对

asp.net iis URLRewrite 实现方法详解

原文 asp.net iis URLRewrite 实现方法详解 实现非常简单首先你要在你的项目里引用两个dll:actionlessform.dll.urlrewriter.dll,真正实现重写的是 urlrewriter.dll   但是如果你要实现分页,那么必须使用这个actionlessform .dll.文件下载见文章底部.现在来看操作步骤: 第一步,下载组件,把urlrewriter.dll 复制到你的项目 bin 目录下. 第二步,在web.config中的<configurati