利用少有的空余时间,详细的浏览了下ASP.NET MVC 4的源代码。照着之前的步伐继续前进(虽然博客园已经存在很多大牛对MVC源码分析的博客,但是从个人出发,还是希望自己能够摸索出这些)。首先有一个事实我们需要明白,就是ASP.NET MVC是基于ASP.NET的,并不是独立开来的,所以我们的伊始将会从路由配置入手。

在开始本节之前,需要读者对ASP.NET的路由配置以及C#的扩展方法有一定的掌握,如果读者不理解请根据情况选择下面的文章进行充电:

1.ASP.NET路由映射

2.C#扩展方法

RouteCollectionExtensions.cs

下面我们打开任意一个ASP.NET
MVC项目(以下示例均为ASP.NET
MVC 4,如果是其他版本请到响应的文件中查找),打开路由映射的文件(MVC4为App_Start下的RouteConfig.cs文件),可以看到我们再也熟悉不过的配置了,下面我们就来看看MapRoute背后的源码是什么样的

         public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
}
//这里的MvcRouteHandler就是一个重要的切入点
Route route = new Route(url, new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
//而这里的DataTokens仅仅只是作为附加的参数
//作为后面搜索控制器时的一个条件
if ((namespaces != null) && (namespaces.Length > ))
{
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}

这个方法是对RouteCollection的扩展,其实就是RouteTable的Routes。我们可以看到源码完全是利用ASP.NET的路由映射创建的,其中的关键点就是这个MvcRouteHandler,这是讲所有符合的请求都经由MVC处理的入口。

MvcRouteHandler.cs

我们顺藤摸瓜来到了MvcRouteHandler,这个类自然也要符合ASP.NET的要求,所以要实现IRouteHandler接口,其中有个关键的方法就是GetHttpHandler,这个方法要求返回一个实现了IHttpHandler接口的类型,而我们可以轻松的看到最终返回了一个MvcHandler类型

MvcHandler.cs

紧接着我们来到MvcHandler这个类,可以看到它不仅仅实现了IHttpHandler接口,同时还实现了IHttpAsyncHandler和IRequiresSessionState接口,前者是为了实现异步控制器,后者是为了访问会话(Session)

然后我们查看接口是如何实现的

(IHttpHandler中ProcessRequest的实现)

(IHttpAsyncHandler中BeginProcessRequest的实现)

PS:这部分代码比较长,所以没有全部截取,并且重点也不在那。

通过两个截图读者可以看到笔者红色框住的部分,也能够知道他们是我们接下来的主角,从它的参数也能够看出这个方法将会返回给我们控制器的实现(IController)。下面我们长话短说,直接看这个方法中的关键部分

这里首先通过路由参数获取了控制器的名称,然后调用ControllerBuilder的GetControllerFactory方法获取控制器工厂,我们可以简单的看下GetControllerFactory的是如何实现的

ControllerBuilder.cs

呵呵,这当然我们要看的,下面我们会看到Current的具体类型

这里笔者就没有继续探索进去了,因为我们已经得出控制器工厂的具体类型是DefaultControllerFactory,那么我们回到MvcHandler中,可以看到在获取了控制器工厂之后,就调用了它的CreateController方法,所以我们就打开DefaultControllerFactory.cs文件一探究竟。

DefaultControllerFactory.cs

二话不说,我们直接Search关键字CreateController就看到了下面这段代码

OK,我们可以看到在CreateController中首先调用了GetControllerType获取控制器的类型,然后再通过GetControllerInstance将控制器创建出来,既然本文的目的是探索控制器是如何定位查找的,所以我们就从GetControllerType入手,接招看代码 J

上图仅仅只是默认命名空间的情况,如果读者指定了查找的命名空间则是另外实现的代码,但是其中都是通过调用GetControllerTypeWithinNamespaces来查找的,所以我们就不必在此就留,直接F12来到这个方法中

到了这里,我们不能盲目自信,看到GetControllerTypes就兴冲冲的F12进去,因为在这个方法之前的EnsureInitialized才是真正的关键部分,所以我们要跟进去。

ControllerTypeCache.cs

下面是EnsureInitialized的具体代码

其中我们看到它首先是判断_cache是否为NULL,如果不为NULL是不会进行下面的操作的,这就是为什么第一次访问页面的时候会很慢,而之后就很快了,原因就在这了。当然我们的重点可不是讨论缓存的,我们看到TypeCacheUtil的GetFilteredTypesFromAssemblies返回的了一组类型,而这些就是所有的控制器,所以我们继续追下去。

TypeCacheUtil.cs

以下就是GetFilteredTypesFromAssemblies的代码

我们首先不考虑缓存,认为当前没有缓存。那么我们就可以发现其中通过调用FilterTypesInAssemblies获取到一组类型,然后才通过SaveTypesToCache保存至缓存中。既然我们先查看FilterTypesInAssemblies的实现代码

到这里我们就快看到湖底了,因为我们看到了Assembly,而这段代码则是通过遍历所有的程序集将所有的类添加进typesSoFar中,然后在return部分通过TypeIsPublicClass过滤一遍,这个时候只会剩下公开的,非纯虚的类了,而predicate则是在ControllerTypeCache中调用GetFilteredTypesFromAssemblies方法时传入的委托,可以见如下所示的图

而这个方法的具体代码如下

呵呵就是判断这个类型是否是Controller结尾,并且是否实现了Icontroller接口,这样我们就获取到了所有的控制器了。

细心的读者会发现FilterTypesInAssemblies方法中buildManager.GetReferencedAssemblies();到底是调用的什么类型呢?以及什么方法呢?下面我发个图,大家就能够明白了

小结

到此为止我们基本上就探索完了,如何按照路由参数的名称查找到具体的控制器这个任务就留给读者了,因为已经很明了,本身查找出来的控制器类型就是按照名字、类型保存进字典的,获取就非常简单了。

ASP.NET MVC 4源码分析之如何定位控制器的更多相关文章

  1. 在git上下载的Asp.Net MVC 4源码怎么编译?

    以本人的下载位置为例:E:\aspnetwebstack 1.win+r 输入cmd 打开dos 界面 2.e: 回车,定位到e 盘 3.cd E:\aspnetwebstack 进入e 盘aspne ...

  2. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  3. RyuBook1.0案例二:Traffic Monitor项目源码分析

    Traffic Monitor源码分析 从simple_switch_13.SimpleSwitch13控制器继承并开发 class SimpleMonitor13(simple_switch_13. ...

  4. asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析

    这篇文章我们开始看一下ActionFilter,从名字上其实就大概知道ActionFilter就是Action上的Filter,对吧,那么Action上的Filter大概有几个呢??? 这个问题其实还 ...

  5. ASP.NET WebForm / MVC 源码分析

    浏览器 Url:https//localhost:6565/Home/Index ,https//localhost:6565/WebForm1.aspx,请求服务器(构建请求报文,并且将请求报文发送 ...

  6. ASP.NET MVC源码分析

    MVC4 源码分析(Visual studio 2012/2013) HttpModule中重要的UrlRoutingModule 9:this.OnApplicationPostResolveReq ...

  7. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  8. asp.net mvc源码分析-ModelValidatorProviders 客户端的验证

    几年写过asp.net mvc源码分析-ModelValidatorProviders 当时主要是考虑mvc的流程对,客户端的验证也只是简单的提及了一下,现在我们来仔细看一下客户端的验证. 如图所示, ...

  9. ASP.NET MVC 源码分析(一)

    ASP.NET MVC 源码分析(一) 直接上图: 我们先来看Core的设计: 从项目结构来看,asp.net.mvc.core有以下目录: ActionConstraints:action限制相关 ...

随机推荐

  1. Python自动化 【第一篇】:Python简介和入门

    Python简介: 一.什么是python Python是一门动态解释性的强类型定义语言. pythonde 特点:“优雅”.“明确”.“简单”. 二.Python由来 python的创始人为吉多·范 ...

  2. Mycat配置文件rule.xml

    打开<MyCAT_HOME>/conf/rule.xml,对应的分片配置截取内容如下: <tableRule name="auto-sharding-rang-mod&qu ...

  3. JavaIDL开发CORBA实例演示

    转载 http://www.micmiu.com/opensource/corba/corba-javaidl-dev-demo/

  4. 解决C# 转到定义时打开的是元数据文件而非源代码文件的问题

    原因:添加引用时 使用的是“浏览"选项卡,选择了项目生成的dll作为引用的内容. 解决:添加引用时 使用的是"项目"选项卡,选择了项目本身作为引用的内容.

  5. mysql 数据库导入 导出,解决 导入 错误问题

    mysqldump -uxxxx -pxxxx -hrds2383jse53pi6ipwmf.mysql.rds.aliyuncs.com legaokao > /root/legaokaodu ...

  6. machine learning----->谷歌Cloud Machine Learning平台

    1.谷歌Cloud Machine Learning平台简介: 机器学习的三要素是数据源.计算资源和模型.谷歌在这三个方面都有强大的支撑:谷歌不仅有种类丰富且数量庞大的数据资源,而且有强大的计算机群提 ...

  7. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  8. [原创]HEXO博客搭建日记

    博客系统折腾了好久,使用过Wordpress,Ghost,Typecho,其中Typecho是我使用起来最舒心的一种,Markdown编辑+轻量化设计,功能不多不少刚好,着实让我这种强迫症患者舒服了好 ...

  9. document.referrer 特性

    最近需要用到document.referrer,但是在测试的时候,总是获取为空,百思不得其解. 于是发动百度,看了大量的文章没有一个说到点子上是为什么,后来偶然看到document.referrer ...

  10. Two Sum & Add Two Numbers

    Two Sum 题目:https://leetcode.com/problems/two-sum/ class Solution(object): def twoSum(self, nums, tar ...