【转】深入分析 ASP.NET Mvc 1.0 – 1. 深入MvcHandler

MvcHandler是一个mvc程序真正开始的地方,因为你可以直接看到并调试它的源码。

MvcHandler的主要代码如下:

protected internal virtual void ProcessRequest(HttpContextBase httpContext) {AddVersionHeader(httpContext);// Get the controller typestring controllerName = RequestContext.RouteData.GetRequiredString("controller");// Instantiate the controller and call ExecuteIControllerFactory factory = ControllerBuilder.GetControllerFactory();IController controller = factory.CreateController(RequestContext, controllerName);if (controller == null) {throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName));}try {controller.Execute(RequestContext);}finally {factory.ReleaseController(controller);}}

这个方法的流程可以概括为: 找到Requst中的Controller name, 根据Controller name创建这个Controller, 执行这个Controller中执行被请求的

Action。

具体分析如下:

1. 添加Http Header
AddVersionHeader(httpContext);

添加一个Http Header: HTTP/1.1 200 OK   …   X-AspNetMvc-Version: 1.0…

2. 从路由表中找到请求的controller的名子
string controllerName = RequestContext.RouteData.GetRequiredString("controller");

获取路由表中的controller name, 在下面的代码中根据这个controller name在缓存中查找到对应的controller类型并生成controller类。

3. 返回一个IControllerFactory对象
IControllerFactory factory = ControllerBuilder.GetControllerFactory();

返回一个继承自IControllerFactory接口的类的实例,这里默认返回DefaultControllerFactory类。 ControllerBuilder属性是ControllerBuilder类的一个静态实例,在mvc程序第一次启动时才会执行ControllerBuilder类的默认构造函数, 在这个构造函数将DefaultControllerFactory类的一个实例传入到SetControllerFactory()方法中, 这样做的目地是定义GetControllerFactory()的具体返回类型。ControllerBuilder类的构造函数代码如下:

public ControllerBuilder() {SetControllerFactory(new DefaultControllerFactory() {ControllerBuilder = this});}

所以想要改变GetControllerFactory()的默认返回类型的办法就是在执行ControllerBuilder.GetControllerFactory()之前调用ControllerBuilder类中的

SetControllerFactory()方法,这个方法有两个重载

public void SetControllerFactory(IControllerFactory controllerFactory) {if (controllerFactory == null) {throw new ArgumentNullException("controllerFactory");}_factoryThunk = () => controllerFactory;}public void SetControllerFactory(Type controllerFactoryType) {if (controllerFactoryType == null) {throw new ArgumentNullException("controllerFactoryType");}if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_MissingIControllerFactory,controllerFactoryType),"controllerFactoryType");}_factoryThunk = delegate() {try {return (IControllerFactory)Activator.CreateInstance(controllerFactoryType);}catch (Exception ex) {throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,controllerFactoryType),ex);}};}

只需要将我们自定义并继承自IControllerFactory接口的类的一个实例或type传入就可以。

4. 根据controller Name创建controller对象
IController controller = factory.CreateController(RequestContext, controllerName);

调用DefaultControllerFactory类中的CreateController方法创建controller类。 CreateController(…)方法的具体代码如下:

public virtual IController CreateController(RequestContext requestContext, string controllerName){if (requestContext == null){throw new ArgumentNullException("requestContext");}if (String.IsNullOrEmpty(controllerName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");}RequestContext = requestContext;Type controllerType = GetControllerType(controllerName);IController controller = GetControllerInstance(controllerType);return controller;}

这里比较简单,首先执行GetControllerType(controllerName)找到对应的controll type, 再调用GetControllerInstance(controllerType) 反射出具体的controll类,先来看看GetControllerType(…)方法中的代码:

protected internal virtual Type GetControllerType(string controllerName){if (String.IsNullOrEmpty(controllerName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");}// first search in the current route's namespace collectionobject routeNamespacesObj;Type match;if (RequestContext != null && RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)){IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;if (routeNamespaces != null){HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);match = GetControllerTypeWithinNamespaces(controllerName, nsHash);if (match != null){return match;}}}// then search in the application's default namespace collectionHashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);match = GetControllerTypeWithinNamespaces(controllerName, nsDefaults);if (match != null){return match;}// if all else fails, search every namespacereturn GetControllerTypeWithinNamespaces(controllerName, null /* namespaces */);}

RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)来返回一个namespace的集合, 一开始我对namespace很不理解,现在我明白了它的意思:在程序中不同的namespace下面可能会存在同名的controller,所以这里用namespace区分这些同名的但不同意义的controller。namespace可以在Global.asax.cs的RegisterRoutes(…)方法中指定,比如:

routes.MapRoute("Default",                                              // Route name"{controller}/{action}/{id}",                           // URL with parametersnew { controller = "Home", action = "Index", id = "" },  // Parameter defaultsnew { httpMethod = new HttpMethodConstraint("get", "post") },new string[]{"Namespace1"});

在继续看GetControllerType(…)方法,在GetControllerType(string controllerName) 方法中最终都是通过调用GetControllerTypeWithinNamespaces(…)方法返回controller type的, 具体代码如下:

private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces){// Once the master list of controllers has been created we can quickly index into itControllerTypeCache.EnsureInitialized(BuildManager);IList<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);switch (matchingTypes.Count){case 0:// no matching typesreturn null;case 1:// single matching typereturn matchingTypes[0];default:// multiple matching types// we need to generate an exception containing all the controller typesStringBuilder sb = new StringBuilder();foreach (Type matchedType in matchingTypes){sb.AppendLine();sb.Append(matchedType.FullName);}throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.DefaultControllerFactory_ControllerNameAmbiguous,controllerName, sb));}}

首先将执行ControllerTypeCache.EnsureInitialized(BuildManager);  他的作用是将程序中所有assembly中所有以Controller结尾的类放在缓存中,看一下

EnsureInitialized(…)方法的代码:

ControllerBuilder.cs

public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { List<Type> controllerTypes = GetAllControllerTypes(buildManager); var groupedByName = controllerTypes.GroupBy( t => t.Name.Substring(0, t.Name.Length - "Controller".Length), StringComparer.OrdinalIgnoreCase); _cache = groupedByName.ToDictionary( g => g.Key, g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } }

这是一个具有2级结构的缓存, 以controll name为key, 以Lookup<string, Type>对象为值保存到缓存中,而Lookup<string, Type>的结构是以namespace为key, 以

controller type为值的键值集合,这个2级结构的作用就是上面提到的用来解决不同namespace中同名controller的问题。

GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces) 方法中:

ControllerTypeCache.GetControllerTypes(controllerName, namespaces) 就是去找具有相同controllerName的controller type,不过这里有个问题就是如果没有在

Global中或其它地方提供默认的namespace而不同namespace下存在同名的controller,就导致GetControllerTypes(…)方法返回的controller数量大于1,这时程序会在

switch语句处抛出一个异常,所里一定要注意,尽量不要在不同的namespace中定义同名的controller

当找到一个对应的 controller type后,就将这个type返回给上面的CreateController(RequestContext requestContext, string controllerName) 方法中调用

GetControllerType(controllerName); 方法的地方, 然后再调用GetControllerInstance(controllerType); 方法将方法反射成具体的controller类并返回到ProcessRequest(…)中,并依次执行controller.Execute(RequestContext) –> factory.ReleaseController(controller);   至此整个MvcHandler的流程执行完毕.

附上MvcHandler的时序图:

MvcHandler_1

 

原文地址:深入分析 ASP.NET Mvc 1.0 – 1. 深入MvcHandler

转载于:https://www.cnblogs.com/shong/archive/2010/08/17/1801576.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379052.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++11 右值引用与常量左值引用保存临时变量(函数返回值)的底层分析

右值引用保存临时变量&#xff08;函数返回值&#xff09;的问题 &#xff1a;临时变量是右值 1、普通变量接收函数返回值&#xff1a; 2、右值引用变量接收函数返回值&#xff1a; 3、用const int& 和右值引用是一样的效果&#xff0c;只是const int& 就不可以修改…

axure源文件_Axure教程:实现网易云音乐有声播放效果

为了方便讲解&#xff0c;我们首先在桌面新建一个文件夹&#xff0c;命名为音乐。1、将自己想要演示播放的MP3音乐文件放在这个文件夹里面。2、给播放页添加一个中继器&#xff0c;随便命名&#xff0c;我给它命名为【音乐地址链接器】&#xff0c;用来链接播放本地音乐文件。并…

ffplay分析(从启动到读取数据线程插入到字幕、音频、视频解码前的队列操作)

《ffplay的数据结构分析》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay分析&#xff08;音频解码线程的操作&#xff09;》 《ffplay 分析&#xff08;音频从Frame(解码后)队列取数据到SDL输出&#xff09;》 《ffplay分析 &#xff08;视频从Frame(解…

并发进程同步

P是荷兰语Proberen&#xff08;测试&#xff09;的首字母。为阻塞原语&#xff0c;负责把当前进程由运行状态转换为阻塞状态&#xff0c;直到另外一个进程唤醒它。也就是不好的一方面。 V是荷兰语Verhogen&#xff08;增加&#xff09;的首字母。为唤醒原语&#xff0c;负责把一…

寄存器和pin_16x2 LCD的PIN图和寄存器

寄存器和pinIn these years the LCD is finding widespread use. It has replaced the LEDs or other multi-segment LEDs.This is due to the following reasons: 近年来&#xff0c; LCD正在广泛使用。 它已替换LED或其他多段LED&#xff0c;原因如下&#xff1a; The decli…

ffplay分析(视频解码线程的操作)

《ffplay的数据结构分析》 《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;音频解码线程的操作&#xff09;》 《ffplay 分析&#xff08;音频从Frame(解码后)队列取数据到SDL输出&#xff09;》 《ffplay分析 &#xff08;视频从Fram…

【转】.NET深入学习笔记(4):深拷贝与浅拷贝(Deep Copy and Shallow Copy)

今天继续利用准备WSE安全开发文章的空闲时间&#xff0c;完善《.NET深入学习笔记》系列&#xff08;基本都是.Net重要的知识点&#xff0c;我都做了详细的总结&#xff0c;是什么、为什么、和怎么实现&#xff09;。想必很多人也接触过这两个概念。做过C的人对深浅拷贝的概念一…

abap 添加alv上的工具栏的按钮_Excel里的置顶功能——快速访问工具栏

100万职场人都在看后台回复礼包领199元职场干货大家好&#xff0c;我是小可~今天跟大家分享一个提高Excel操作效率的小技巧自定义你的快速访问工具栏设置后的效果▼▼▼也就是把你最经常用到的两三个功能放到快速访问工具栏可以一眼就找到这些功能不需要靠快捷键或者功能选项卡…

用递归法求1²+2²+...+n²的值

思路分析: 谈到递归,我个人会联想到数学里面的通式。因为递归调用的函数的对应法则是相同的。例如这道题:f(x)=x。这个就是函数通式,只不过把每个求得的结果进行累加求和即可。用户输入5的时候,会出现f(5)=5,之后再进行x减一操作,执行f(4)=4,最后将每个进行累加即可。…

编写一个函数,计算下式当n=10和n=100的值。

思路分析: 首先,我个人看法:当我拿到这道题的时候,我会把它当成一道数学题对待。分子是动的,恒为一,分母是进行依次增加的。且奇数项为正,偶数项为负。因为设计运算出现的是分数,故,设计选取存储类型为double。 找出问题: ①正负号问题、②分母问题、③累计求和问题…

POJ 1001 大数的乘法

对这道题的理解 大数的乘法 关键是 实型的 那么首先就是数出来小数点有几位这个相信很简单 从后面往前数刚开始0 不算接着就是遇到小数点结束如果没有小数点 那么置为0 接着就是输出地时候首先算出小数点的位置然后输出 你想怎么样都行 从后往前数这个时候输出 那么就是你也…

铃木uy125摩托车机油_济南铃木安徽发布国四新车—6480元瑞梦125、9380元UY125

安徽合肥&#xff0c;这个具有两千多年历史的古城&#xff0c;以“三国故地、包拯家乡”而闻名海内外&#xff0c;2019年4月22日济南铃木为这座城市带来一份惊喜&#xff0c;今年正值国四执行&#xff0c;济南铃木旗下两款国四新车瑞梦125与UY125正式在合肥与大家相见。济南铃木…

编写一个程序,计算用户输入的起始时间到终止时间之间相隔的天数。

思路分析&#xff1a; 闰年&#xff1a;闰年又分为普通闰年和世纪闰年 普通闰年&#xff1a;能被4整除且不能被100整除的为闰年(2004为闰年&#xff0c;1999不是闰年) 世纪闰年&#xff1a;能被400整除的是闰年(2020年是闰年&#xff0c;1900年不是闰年) 闰年共有366天&#x…

mvc的Controller返回值类型ActionResult详解

一、简介 ActionResult 操作方法通过执行工作并返回操作结果来响应用户输入。 操作结果表示框架将代表操作方法执行的命令。 ActionResult 类是操作结果的基类。 以下类型从 ActionResult 派生&#xff1a; ContentResult EmptyResult FileResult HttpUnauthorizedResult …

栅格布局一般怎么用_建筑混凝土色差大怎么办?用这种方法处理,一般都看不出来...

由于模板锈蚀、脱模剂污染、原材料等原因&#xff0c;建筑混凝土成形后经常会遇到颜色不一致的现象&#xff0c;为此我们总结了混凝土面色差调整施工工艺&#xff0c;可供大家参考使用。一、混凝土面色差调整施工工艺流程及说明基层表面打磨→吸尘器吸尘→湿润墙面→素水泥处理…

FusionChart完全入门手册4

想不想打造让人震撼的图表系统&#xff0c;想不想做出和别人不一样的图表&#xff0c;从本节起&#xff0c;我就带领大家走入这片神奇的土地&#xff0c;让大家去采摘属于自己的创意之果&#xff0c;我们的目标是------个性无罪&#xff0c;个性万岁&#xff01; 问题三、如何做…

ffplay分析(音频解码线程的操作)

《ffplay的数据结构分析》 《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay 分析&#xff08;音频从Frame(解码后)队列取数据到SDL输出&#xff09;》 《ffplay分析 &#xff08;视频从Fram…

More Effective C++ (运算符)

4.1&#xff1a;谨慎定义类型转换函数<1>容易的方法是利用一个最新的编译器特性&#xff1a;explicit关键字<2>C编译器把">>"作为一个符号来解释&#xff0c;在两个">"间没有空格&#xff0c;语句会产生语法错误。<3>隐式类型转…

php微信获取mediaid超出限制_Python实现每日微信自动打卡

众所周知&#xff0c;因为疫情的原因&#xff0c;很多高校和公司都要求员工每日在微信上进行打卡&#xff0c;来汇报自己的当前身体状态和所处地区。但绝大多数情况下&#xff0c;每天打卡的信息其实是不会变的&#xff0c;我们要做的就是进入公众号——自动登录点进打卡页面—…

ffplay 分析(音频从Frame(解码后)队列取数据到SDL输出)

《ffplay的数据结构分析》 《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay分析&#xff08;音频解码线程的操作&#xff09;》 《ffplay分析 &#xff08;视频从Frame(解码后)队列取数据到…