1 引言
小弟在学习技术的时候有个毛病,往往喜欢先吃下一大桶理论然后再去实践,不仅要知道这个 如何去做 还想知道 为什么可以这么做? ,但是事实是在工作时候项目经理可没有兴趣让你去研究这些,特别是在小弟现在的公司是国企的情况下差点被辞退(这里就不详说了...) 显然我也知道这样不对,因为公司请你是来快速完成任务的而不是让你研究的!但是小弟依旧没完全改掉这个毛病!或许说不知道工作和学习正常的分配吧!正因为这个毛病所以写了这篇博客,还望和各位bigGod共同学习吧!!
2 进入正题
在进入正题前小弟希望阅读者能了解最基本ASP.NET MVC 路由模板 ,小弟不会从服务器(如:IIS)最低层的请求如何到达你的WebApplication进程进行说明,这个网上资料有很多如果想了解推荐http://www.cnblogs.com/lumnm/archive/2009/08/08/1541901.html这篇清晰易懂。这里只讲解ASP.NET MVC管道的http请求如何到达控制器操作。
- 创建实例
在说明之前首先我们创建一个默认的MVC项目(相信大家都会创建,这里就不演示了),打开根目录下Global.asax.cs文件
在此文件下项目自动生成了一些代码如下:
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 protected void Application_Start() 4 { 5 AreaRegistration.RegisterAllAreas(); //配置区域路由 目的在大型复杂网站下方便管理路由 6 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); //配置过滤特性 7 RouteConfig.RegisterRoutes(RouteTable.Routes); //配置路由 8 BundleConfig.RegisterBundles(BundleTable.Bundles); //配置捆绑缩减的JS和css文件 9 10 } 11 12 }
上面代码中MVCApplication类的Application_Start()是整个程序的入口,在这里可以配置整个web程序的全局属性,而且我们的MvcApplication 继承至HttpApplication。 服务器(如IIS)的请求都要经过HttpApplication的处理管道,管道内的处理过程是固定的,在服务器处理请求的各个阶段,依次触发对应的事件,便于程序员在不同的阶段完成自定义的处理工作。查看HttpApplication的元数据(鼠标右键转到定义)发现有如下19个标准的管道事件:
(注:图片转至 木宛城主的博客:http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-apply-asp-net-identity-authentication.html)
在MVC里处理请求的是UrlRoutingModule类,它是一个实现了IHttpModule接口的类,那这个接口的作用是什么呢?我们先来看下IHttpModule接口的代码:(非常简单)
1 public interface IHttpModule 2 { 3 void Dispose(); 4 void Init(HttpApplication context); 5 }
只要继承至IHttpModule接口的类,就可以参与到管道处理请求中。 接口中的Init方法是实现功能的主要方法,它有一个context参数,这个参数允许访问当前HttpApplication的环境。路由机制就是利用这个原理,它通过注册订阅HttpApplication事件参与到管道处理请求中。UrlRoutingModule类注册订阅了PostResolveRequestCache 事件实现了url的映射。为什么是该事件呢?因为该事件的下一步就要完成请求和物理文件的映射,所以必须要在此之前进行拦截。
为了更好的理解UrlRoutingModule类的运行机制,我写个小例子,该例修改http输出流,在每个输出流加上请求时间和请求处理完毕时间。现在添加一个类,然后让它继承IHttpModule接口并实现。代码如下:
1 public class MyModule : IHttpModule 2 { 3 4 private HttpApplication _appliction = null; 5 6 public void Init(HttpApplication context) 7 { 8 _appliction = context; 9 10 string reqTime = string.Empty; //请求时间 11 string resTime = string.Empty; //请求完毕时间 12 13 // //通过订阅HttpApplication的BeginRequest事件,这个事件在收到一个http请求时触发 14 _appliction.BeginRequest += (obj, e) => 15 { 16 reqTime = string.Format("请求时间:{0}", DateTime.Now.ToString()); 17 }; 18 19 //通过订阅HttpApplication的EndRequest事件,这个事件会在把响应内容发送给客户端前触发 20 _appliction.EndRequest += (obj, e) => 21 { 22 resTime = string.Format("请求处理后时间:{0}", DateTime.Now.ToString()); 23 24 _appliction.Context.Response.Write(reqTime+"<br/>"+resTime); //在每个响应内容流里写入获取的时间 25 26 }; 27 } 28 }
为了使用这个模块,我们要在请求处理管道上包含该模块。为此我们配置web.config文件使其包含它一个引用。在web.config文件的<httpModules>节点下添加:
<add name="ClassName" type="NameSpace.ClassName,AssemblyName" />
我的例子ClassName是MyModule,命名空间是MvcDemo.Models,所以我的节点如下
<httpModules>
<add name="MyModule" type="MvcDemo.Models.MyMoule,MvcDemo" />
</httpModules>
然后按F5运行你就会发现每个页面多了2排分别表示请求和请求完毕的时间的文字
细心的朋友可能发现在 <httpModules>节点下并没有添加 UrlRoutingModule类的引用啊,其实web.config配置可以分为本站和全局,全局的配置在C:WindowsMicrosoft.NETFrameworkv版本号Configweb.config文件里
现在大家应该了解UrlRoutingModule类的功能了,我们继续查看它如何映射到控制器操作的,核心源代码如下:
public class UrlRoutingModule : IHttpModule { protected virtual void Init(HttpApplication application) {
if (application.Context.Items[_contextKey] == null) { application.Context.Items[_contextKey] = _contextKey;
//注册订阅HttpApplication对象的PostResolveRequestCache事件 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);//事件触发时会执行下面的方法 } } private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context); this.PostResolveRequestCache(context); } }
从源码看出UrlRoutingModule其实跟我们上面那例子差不多都是通过注册事件来执行相应的代码功能的。在源代码里UrlRoutingModule还有个RouteCollection成员,代码如下
public System.Web.Routing.RouteCollection RouteCollection { get { if (this._routeCollection == null) {
this._routeCollection = RouteTable.Routes; //注意这句话 } return this._routeCollection; } set { this._routeCollection = value; } }
估计大家都能看懂这段代码,非常简单就是一个设置属性的代码段。但是仔细看这个属性就是RouteTable.Routes,一看到RouteTable.Routes估计大家都不陌生,因为这是我们在一开始学习路由模板的时候都会接触!如果你实在想不起这个东西的话就给你点提示吧!依旧打开根目录下Global.asax.cs文件
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { RouteConfig.RegisterRoutes(RouteTable.Routes); //配置路由 } }
在配置大家路由模板的时候会传入RouteTable.Routes这个参数,那么这个参数到底是个什么东西呢?代码如下:(非常简单)
public class RouteTable { private static RouteCollection _instance = new RouteCollection(); public static RouteCollection Routes => _instance; }
可以看出RouteTable.Routes这个参数其实就是一个RouteCollection对象,也就是路由的集合,为什么封装成RouteTable?因为它保证了全局的唯一和调用的方便!小弟查了下RouteCollection的源码,底层就是使用IList封装的集合,这样我们就可以配置添加我们的路由模板:
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapMvcAttributeRoutes(); //开启特性理由 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");//忽略映射文件
//这里我们添加了2个路由模板,理由模板的作用我相信大家都知道,所以这里也不说明了 //MapRoute方法的源码显示最后还是调用的集合的Add方法添加的路由 routes.MapRoute( name: "Default1", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); routes.MapRoute( name: "Default2", url: "Side/{Year}/{Month}/{Day}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
到了这里我们应该可以知道UrlRoutingModule尝试使用我们在RouteTable.Routes中注册的路由匹配当前请求,如果有一个路由匹配成功,它就会从匹配成功的Route对象中获取一个IRouteHandler对象,这个接口有一个GetHandler方法,它会返回一个IHttpHandler对象,后续我会讲解返回的这个IHttpHandler对象,这个对象主要用来实例化控制器,并调用该控制器的操作方法。
---------------------------------------------------------------------------------未完待续--------------------------------------------------------------------------------------------------------------
说明
由于小弟第一次写博客,所以也不知道表达的清不清楚,语文也是体育老师教的。如果有什么错误之处和建议还望大神指点江山 !!!
- 4楼nele
- 研究的很细致,支持。
- 3楼林Lin☆☆
- 好奇的看完,却发现只是一半.哎!
- 2楼安度
- 任务第一,技术嘛,用的到再学...用不到学来干啥??
- Re: 狂扁小朋友
- @安度,嗯 谢谢大神指导 以后会注意的
- 1楼明明不爱
- 说的挺好,最近我也在看这块,有时想想这块确实挺有意思的……