简单的mvc之三:灵活的路由(上)
在上一篇关于管线的随笔中已经提及了管线,通过对管线的分析,我们可以得到下面几个结论:路由系统由URLRoutingModule模块实现,它订阅了PostResolvRequestCache事件;路由系统通过查阅路由并尽可能的通过RemapHandler方法,确定excuteHandler阶段执行的IHttphandler。这一篇随笔想详细谢谢路由的定义、注册和导航的具体过程。
路由系统的导航过程定义于URLRoutingModule,具体实现如下:
public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData != null)
{
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[]));
}
if (!(routeHandler is StopRoutingHandler))
{
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
}
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);
}
}
}
}
代码中可以很明显看出导航逻辑:通过全局全局路由表Routeable.Routes.GetRouteData()方法获得RouteData,然后通过RouteData.GetHttphandler()获得IRoutehandler,最后通过HttpContext.RemapHandler()注册IRouteHandler.GetHttpHandler()得到的IhttpHandler。代码中的出现的StopRoutingHandler后面再说。
路由表的切入口在全局静态类RouteTable.Routes,注册的过程放在HttpApplication的Appliation_Start方法中,这个方法如同静态构造函数一般,只启动一次,所以所有需要初始化的全局对象都可以放在这个方法中。vs建立的项目中,Route表的注册被分离出来,放在了App_Start文件夹中,使代码更加清晰。例如以下代码所示,3个路由被注册:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Self", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "CatchAll",
url: "{*input}",
defaults: new { controller = "Home", action = "index" }
);
}
上面的代码中使用的IgnoreRoute()与MapRoute()皆为扩展方法,用来简化路由注册过程,前者初始化一个Route对象,或者初始化一个IgnoreRouteInternal(继承于Route,私有封闭)对象,然后使用RouteTable.Routes.Add()方法添加如注册表。通过如下代码,可以清楚的看到注册的route:
public void Routes() {
foreach (var routeBase in RouteTable.Routes) {
var route = routeBase as Route;
if (route != null) {
Response.Write(String.Format("{1,26} {0}<br/>",
route.GetType().ToString(), route.Url
).Replace(" "," "));
} else {
Response.Write(String.Format("other:{0}<br/>", routeBase.GetType()));
}
}
}
得到的结果如下:

从图中我们可以看到后3个路由正是我们已经注册的3个路由,至于第一个路由,实际上是托管于asp.net的web api应用路由,这个后面再说。同时图中的第二列的Type也佐证了上面所说的不同的Route类说法。
那么Route类的具体实现呢?首先我们看看Route的继承树:

看着很熟悉,因为集成树中的类在上述的路由图中已经全部出现,整个树的基类是RouteBase,也就是图中高亮的那个,Routes中的集合类型也是RouteBase,下面是RouteBase的定义:
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public abstract class RouteBase
{
// Fields
private bool _routeExistingFiles;
// Methods
protected RouteBase();
public abstract RouteData GetRouteData(HttpContextBase httpContext);
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
// Properties
public bool RouteExistingFiles { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
}
这个一个抽象类,核心方法就是URLRouting中出现过的GetRouteData()方法,还有一个很重要的属性—RouteExistingFiles,这个属性一般用来实现对.aspx兼容支持。RouteBase最常用也是最重要的子类便是Route,代码如下:
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public class Route : RouteBase
{
// Fields
private ParsedRoute _parsedRoute;
private string _url;
private const string HttpMethodParameterName = "httpMethod";
// Methods
public Route(string url, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);
public override RouteData GetRouteData(HttpContextBase httpContext);
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection);
// Properties
public RouteValueDictionary Constraints { get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
public RouteValueDictionary DataTokens { [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
public RouteValueDictionary Defaults { [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
public IRouteHandler RouteHandler { [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
public string Url { get; set; }
}
相较于RouteBase,Route有5个属性,其中Url,Constraints用来过滤进入请求进行匹配,RouteHandler用来返回IHttpHandler,DataTokens与Defaults则应用于后面的控制器查找,激活与数据传递。这些就以后在细节补充中说这里就不散开了。
从上面的Route的定义结合最前面说的导航过程,RouteBase->IRouteHandler->IhttpHandler的过程清晰可见。在前面曾经搁置的类StopRoutingHandler就属于IRoutehandler,而这个RouteHandler并不是由Route类获得的,而是前面出现的IgnoreRoute获得的,相关代码:
public IgnoreRouteInternal(string url) : base(url, new StopRoutingHandler())
{
}
到这里IgnoreRoute()的方法的作用就真正明了了,和MapRoute()方法想反,它定义的不是对某一类路径的拦截,而是放弃对某一类路径的拦截,以便留给后面的管线过程处理,这个管线过程就是后面要说的ExcuteHandler过程(内部,没有公开事件),具体到文中注册的路由可以看到它放弃的是对*.axd类地址的拦截。但是注册的好处是直接跳过,提高效率。
本来想一次说完的,写到这里发现还有很多没写到,比如全局类型RouteTable等,那就分上下篇吧。最后留个问题:两个路由,一个的Url定义为{input},一个定义为{*input},二者有什么区别?
简单的mvc之三:灵活的路由(上)的更多相关文章
- 005. Asp.Net Routing与MVC 之三: 路由在MVC的使用
上次讲到请求如何激活Controller和Action,这次讲下MVC中路由的使用.本次两个关注点: 遗留:ModelBinder.BindModel的过程 MVC中路由的使用 MVC 5中的Acti ...
- 编写简单的spring mvc程序,在tomcat上部署
编写简单的spring mvc程序,在tomcat上部署 1 用java 配置spring mvc ,可以省去web.xmlpackage hello;import org.springframewo ...
- ASP.NET Core MVC 中两种路由的简单配置
1.全局约定路由 这种方式配置优先级比较低,如果控制器或者方法上标记了特性路由那么优先走特性路由. 当建立好一个mvc项目里,路由都是默认配置好的. 如果建立的是空项目那么需要手动配置: 1.需要在C ...
- MVC架构模式分析与设计(一)---简单的mvc架构
首先 我要感谢慕课网的老师提供视频资料 http://www.imooc.com/learn/69 下面我来进行笔记 我们制作一个简单的mvc架构 制作第一个控制器 testController.cl ...
- MVC 插件化框架支持原生MVC的Area和路由特性
.NET MVC 插件化框架支持原生MVC的Area和路由特性 前面开放的源码只是简单的Plugin的实现,支持了插件的热插拔,最近晚上偶然想到,原生的MVC提供Areas和RouteAtrribut ...
- 简单的mvc之一:简单的开始
mvc学习到现在,相对所学到的一系列的知识做一个总结,于是就有了这个标题—简单的mvc.文如名,写的是简单的mvc的知识,目标群也不言而喻.这一篇来个简单的开始,从头建立一个web项目,比如hello ...
- [.NET] 一步步打造一个简单的 MVC 网站 - BooksStore(一)
一步步打造一个简单的 MVC 网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 简介 主 ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(二)
一步步打造一个简单的 MVC 电商网站 - BooksStore(二) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 前: ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一)
一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
随机推荐
- enq: SQ - contention
--每分钟操作 SQL> select sql_id, mi, count(mi) 2 from (select event, sql_id, to_char(sample_time ...
- POJ1719- Shooting Contest(二分图最大匹配)
题目链接 题意:给定一个矩阵,每列有两个白点,其它都是黑点,如今要求每列选一个白点,使得每一行至少包括一个白点被选中 思路:利用白点所在的位置用行指向列建图,用行去匹配列,最大匹配数假设不等于行数的话 ...
- APMServ—我用过的最优秀的PHP集成环境工具
原文:APMServ-我用过的最优秀的PHP集成环境工具 经常折腾wordpress和各种cms,免不了要在本地测试一些程序,所以选择一款好的php集成环境就至关重要啦. 1. 我用过的php集成环境 ...
- CSS3 制作向左、向右及关闭图标的效果
先看一下效果 1.鼠标移入前的效果 2.鼠标移入的效果 3.制作步骤如下: 轮廓的CSS,就是利用圆角和宽度高度,制作出一个圆: <style> /*显示方式为 inline-block* ...
- Asp.net MVC + EF + Spring.Net 项目实践(四)
这篇写一写如何使用Spring.net来解耦各个项目 1. 在接口层添加IStudentBLL文件,里面有GetStudent和GetAllStudents两个方法:然后在StudentBLL类里实现 ...
- 【转】关于“ORA-01653: 表 SYS.AUD$ 无法通过 128 (在表空间 SYSTEM 中) 扩展”的错误
SQL*Plus: Release 11.1.0.6.0 - Production on 星期一 5月 17 18:31:08 2010 Copyright (c) 1982, 2007, Oracl ...
- [C#][ASP.net] 透过WebBrowser 取得AJAX 后的网页
原文[C#][ASP.net] 透过WebBrowser 取得AJAX 后的网页 今天 Shih-Min 问我说,假设网页一开始是AJAX 会载入一些资料,但是透过WebClient 去抓 抓到都是J ...
- Android设备连接Unity Profiler性能分析器
Unity提供两种方式让Developer的Android设备连接Profiler进行性能分析: 1.通过wifi,Android设备和计算机处于同一个Wlan中. 2.通过USB ADB 普通情况我 ...
- PHP 6:PHP 基本数据类型
原文:PHP 6:PHP 基本数据类型 本章将介绍PHP基本类型.相信我们已经熟悉了C/C++,C#或者Java里的任意一种语言.本章会以C#为比较语言.OK,如果你想学PHP,你最先考虑的是什么呢? ...
- jquery validate remote验证唯一性
jquery.validate.js 的 remote 后台验证 之前已经有一篇关于jquery.validate.js验证的文章,还不太理解的可以先看看:jQuery Validate 表单验证(这 ...