ASP.NET路由模型解析 ps:这是针对ASP.NET4.5版本的好像在最新的5.0版本中加入了OWIN彻底解耦了和Web服务器的耦合我还没有研究过不敢妄言4.5的模型适用5.0。是不是被我严谨的态度震慑了-action*0x1大话ASP.NET模型首先我们先来了解下一个请求的悲欢离合的命运看看它的一生中所走过的蜿蜒曲折的道路。如下图所示在这里感谢马伦老师唾沫横飞的讲解终于让我理解它的一生。在如上所示的风光旖旎的画卷中我们可以看到一个“请求”从客户端浏览器出发经历千山万水到达服务器服务器的内核模块的HTTP.SYS热情款待了它对它进行简单的修饰之后就和它依依惜别了因为HTTP.SYS知道它是一个有梦想的“请求”它应该去它该去的地方于是就把它送到了IIS。IIS是片神奇的土地这里有一位伟大的神灵叫做inetinfo.exe于是它便去神灵的居所W3SVC服务windows服务祈祷希望能给他一些指示神灵通过查阅天书IIS的配置文件知道了它不是一般的静态文件不能把它直接送回去应该让它去它的族人开办的加工厂即对应网站的工作进程中好好修习一番。现任加工厂老大叫w3wp.exe在IIS6以前是aspnet_wp.exe,其因为没有管理好各个加工厂之间的地盘问题被罢免了asp.net_wp.exe用一个进程寄宿所有的网站用应用程序域进行分割的结果导致网站之间相互影响现任老大w3wp.exe通过一个网站一个进程的方式把问题解决了因此顺利上位。初入加工厂的“请求”拜访了门卫asp.net_isapi.dll,门卫发现它是第一个过来的“请求”于是为它打开了工厂的生产车间即第一个请求到达时启动了asp.net运行的环境后来的请求就可以直接进入这个环境里。并请车间主任ISAPIRuntime来负责它主任兴高采烈的来欢迎它即ISAPIRuntime调用ProcessRequest简称PR方法访问当前请求所在的ecb句柄并让土里土气的它换上了统一服装HttpWorkRequest即把请求进行简单的封装然后叫来班长HttpRuntime,让班长安排它的工作。班长说”车间里面有危险你先穿上安全制服HttpContext。”即通过PR方法把HttpWorkRequest封装成HttpContext然后去组长宿舍HttpApplicationFactory准备叫一个组长HttpApplication来带领它结果发现还没有组长班长只好去招聘一个新组长。每一个组长都是经过严格训练才能上岗的先要熟读入厂准则Global.asax即先编译Global.asax文件再通过准则中Application_Start方法考验即调用ApplicationStart方法如此这般方成为一代组长。每位新任组长第一件事就是把所有的车间模块装配好并创建好车间管道通过读取配置文件加载所有的IHttpModule并调用他们的Init方法一般init方法都是注册管道事件之后通过BuidStepManager方法根据经典模式或者集成模式生成对应的StepManager。新任组长见到“请求”二话不说直接启动车间管道将其丢进去。穿着安全制服HttpContext的“请求”要依次通过管道中所有的关卡asp.net管道模型其中在第7个关卡之后生成了IHttpHandler类型的对象并在第11个关卡之后执行该对象的ProcessRequest方法处理请求在这里“请求”得到完美的加工塑造生成了HttpResponse再通过剩下的管道实现了梦想的请求就沿着原路返回了。上图中第11、12个事件之间描述的是WebForm的Page对象处理请求的流程即页面生命周期。至此一个请求的跌宕起伏的人生就说完了各位观众欲知路由模块具体怎么发挥作用的还请先捧个人场右下角点个赞。action*0x2路由模型解析通过上文我们知道组长HttpApplication对象会负责组装所有的IHttpModule它是如何加载的呢我们观察反编译的代码private void InitModules() { HttpModuleCollection modules RuntimeConfig.GetAppConfig().HttpModules.CreateModules(); HttpModuleCollection other this.CreateDynamicModules(); modules.AppendCollection(other); this._moduleCollection modules; this.InitModulesCommon(); }RuntimeConfig.GetAppConfig().HttpModules.CreateModules();通过这行代码我们可以清楚的发现它读取了运行时的配置文件那么我们打开运行时的配置文件以观究竟。果然在这里add了一个System.WebRouting.UrlRoutingModule类型。接下来我们再用反编译工具看这个类型的源码如我们所料UrlRoutingModule实现了IHttpModule接口我们看看它的Init方法干了些什么protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] null) { application.Context.Items[_contextKey] _contextKey; application.PostResolveRequestCache new EventHandler(this.OnApplicationPostResolveRequestCache); } }对第7个事件PostResolveRequestCache注册方法OnApplicationPostResolveRequestCache那么这个方法又是干啥的呢public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData this.RouteCollection.GetRouteData(context);//匹配路由得到匹配结果RouteData。 if (routeData ! null) { IRouteHandler routeHandler routeData.RouteHandler; if (routeHandler null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString(UrlRoutingModule_NoRouteHandler), new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext new RequestContext(context, routeData); context.Request.RequestContext requestContext; IHttpHandler httpHandler routeHandler.GetHttpHandler(requestContext);//获取处理当前请求的IHttpHandler对象。 if (httpHandler null) { object[] args new object[] { routeHandler.GetType() }; throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString(UrlRoutingModule_NoHttpHandler), args)); } if (httpHandler is UrlAuthFailureHandler) { if (!FormsAuthenticationModule.FormsAuthRequired) { throw new HttpException(0x191, SR.GetString(Assess_Denied_Description3)); } UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); } else { context.RemapHandler(httpHandler);//映射用当前IHttpHandler对象处理请求。 } } } }代码已经加了注释3步走匹配路由→获取处理当前请求的IHttpHandler对象→映射用当前IHttpHandler对象处理请求。之后会在第11、12个事件之间调用IHttpHandler对象的PR方法处理当前请求。我们再整理下思路ASP.NET先注册了UrlRoutingModule模块他就是一个实现了IHttpModule接口的类其Init方法就是在第7个事件上注册一个方法该方法先匹配路由如果匹配成功了则用匹配结果RouteData中的IHttpHandler对象映射到当前上下文中这样在之后第11、12个事件之间就会调用这个IHttpHandler对象处理请求。那么问题来了Route对象是什么时候注入进去的IHttpHandler对象又是谁还记得路由规则是怎么添加的吗如下面代码所示public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { var defaults new RouteValueDictionary(); defaults.Add(name, *); //方式一 //通过RouteTable的静态对象Routes新增一个Route类型的对象。 RouteTable.Routes.Add(app, new Route(app/{name}, defaults, new MyRouteHandler())); //方式二 //通过RouteTable的静态对象Routes的扩展方法新增一个路由规则。 RouteTable.Routes.MapPageRoute(default, app/{name}, ~/WebForm1.aspx, false, defaults); } }这是我们经常用的两种方式添加路由规则方式一中有我们自己编写的MyRouteHandler类型的实例作为参数其实就是通过IRouteHandler接口返回一个IHttpHandler对象。/// summary /// 实现了IRouteHandler接口的类型 /// /summary internal class MyRouteHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { //返回一个Page对象用于处理请求。 return new WebForm1(); } }