廊坊门户网站,重庆建设工程信息查询,可以做设计赚钱的网站,专业建网站平台深入理解Asp.net MVC路由 吴剑 2012-10-22 原创文章#xff0c;转载必需注明出处#xff1a;http://www.cnblogs.com/wu-jian/ 前言 从.Net Framework 1.0时代开始写WebForm#xff0c;直到最近断断续续看到Razor的语法风格#xff0c;然后搜了Asp.net MVC的一些介绍#… 深入理解Asp.net MVC路由 吴剑 2012-10-22 原创文章转载必需注明出处http://www.cnblogs.com/wu-jian/ 前言 从.Net Framework 1.0时代开始写WebForm直到最近断断续续看到Razor的语法风格然后搜了Asp.net MVC的一些介绍于是想把自己的一个项目从WebForm转换成MVC。边摸索边Coding花了两周时间差不多大功告成。项目中有用到多级域名代码写到这部分突然就被卡住了一直没深入了解Asp.net Routing被卡住理所当然感觉关于Asp.net MVC底层知识的文章很少大多是一些应用层面或底层原理的部分片段不能帮助我系统的理解。代码是最好的学习和交流方式于是结合自己的项目实例反编译了部分微软的类库来参考将过程整理和表述如能给人予帮助不甚荣幸同时个人能力有限不足之处还请及时指正。 “静态路由表” 在Asp.net中需要将请求的URL根据我们设置的匹配规则交给相应的处理程序去处理从Url到处理程序的过程称之为路由如下图所示 图1 在Asp.net中命名空间 System.Web.Routing 包含了路由的所有功能从命名上可以看出它与 System.Web.Mvc 是平级的。没错Asp.net Routing是一个独立组件你可以用它来路由MVC路由Web Form甚至路由自己的Web框架。 在深入Routing之前我们暂且按最简单的方式来想像路由在内存中有一张表这张表记录了Url的匹配规则和处理程序当一个Url请求到达时遍历这张表如发现Url与规则匹配交由到对应的处理程序。 OK按这个简单逻辑我们首先需要找到这张表。它位于 System.Web.Routing.RouteTable.Routes 静态属性中类型为 RouteCollection RouteCollection为 Route 的集合当我们要添加一条路由信息时即向这个集合中添加一个Route对象。为方便描述本文中姑且我们把这个存放Route集合的静态属性称之为“静态路由表”吧。 public class RouteTable
{public RouteTable();//静态属性获取所有路由的对象public static RouteCollection Routes { get; }
} 向“静态路由表”中添加记录 如何向“静态路由表”中添加记录有如下两种方式 一、直接调用System.Web.Routing.RouteCollection.Add()方法 public void Add(string name, RouteBase item)
{//...
} 二、调用System.Web.Mvc.RouteCollectionExtensions.MapRoute()扩展方法 扩展方法是C#3.0的新特性比如假设微软的团队A开发了System.Web.Routing团队B开发了System.Web.Mvc。团队B需要使用团队A开发的RouteCollection.Add()同时还需要再对它进行一些扩展此时团队B使用了扩展方法而无需关心团队A的代码 反编译 System.Web.Mvc.RouteCollectionExtensions.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);}Route item new Route(url, new MvcRouteHandler()) {Defaults new RouteValueDictionary(defaults),Constraints new RouteValueDictionary(constraints),DataTokens new RouteValueDictionary()};if ((namespaces ! null) (namespaces.Length 0)){item.DataTokens[Namespaces] namespaces;}routes.Add(name, item);return item;
} 对扩展方法总结 1、扩展方法与方法所在的类都必须为静态的。 2、第1个参数为待扩展的类型并且前面添加this关键字。 “静态路由表”的数据结构 System.Web.Routing.Route 类 public class Route : RouteBase
{/// summary/// 使用指定的 URL 模式和处理程序类初始化 System.Web.Routing.Route 类的新实例/// /summary/// param nameurl路由的 URL 模式/param/// param namerouteHandler处理路由请求的对象/parampublic Route(string url, IRouteHandler routeHandler);/// summary/// 使用指定的 URL 模式、默认参数值和处理程序类初始化 System.Web.Routing.Route 类的新实例/// /summary/// param nameurl路由的 URL 模式/param/// param namedefaults用于 URL 中缺失的任何参数的值/param/// param namerouteHandler处理路由请求的对象/parampublic Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);/// summary/// 使用指定的 URL 模式、默认参数值、约束和处理程序类初始化 System.Web.Routing.Route 类的新实例/// /summary/// param nameurl路由的 URL 模式/param/// param namedefaults要在 URL 不包含所有参数时使用的值/param/// param nameconstraints一个用于指定 URL 参数的有效值的正则表达式/param/// param namerouteHandler处理路由请求的对象/parampublic Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);/// summary/// 使用指定的 URL 模式、默认参数值、约束、自定义值和处理程序类初始化 System.Web.Routing.Route 类的新实例/// /summary/// param nameurl路由的 URL 模式/param/// param namedefaults要在 URL 不包含所有参数时使用的值/param/// param nameconstraints一个用于指定 URL 参数的有效值的正则表达式/param/// param namedataTokens传递到路由处理程序但未用于确定该路由是否匹配特定 URL 模式的自定义值。 这些值会传递到路由处理程序以便用于处理请求/param/// param namerouteHandler处理路由请求的对象/parampublic Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);/// summary/// 获取或设置为 URL 参数指定有效值的表达式的词典/// 返回一个包含参数名称和表达式的对象/// /summarypublic RouteValueDictionary Constraints { get; set; }/// summary/// 获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值/// 返回一个包含自定义值的对象/// /summarypublic RouteValueDictionary DataTokens { get; set; }/// summary/// 获取或设置要在 URL 不包含所有参数时使用的值/// 返回一个包含参数名称和默认值的对象/// /summarypublic RouteValueDictionary Defaults { get; set; }/// summary/// 获取或设置处理路由请求的对象/// 返回处理请求的对象/// /summarypublic IRouteHandler RouteHandler { get; set; }/// summary/// 获取或设置路由的 URL 模式/// 返回用于匹配路由和 URL 的模式/// /summarypublic string Url { get; set; }/// summary/// 返回有关所请求路由的信息/// /summary/// param namehttpContext一个对象封装有关 HTTP 请求的信息/param/// returns返回一个对象其中包含路由定义中的值/returnspublic override RouteData GetRouteData(HttpContextBase httpContext);/// summary/// 返回与路由关联的 URL 的相关信息/// /summary/// param namerequestContext一个对象封装有关所请求的路由的信息/param/// param namevalues一个包含路由参数的对象/param/// returns返回一个包含与路由关联的 URL 的相关信息的对象/returnspublic override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);/// summary/// 确定参数值是否与该参数的约束匹配/// /summary/// param namehttpContext一个对象封装有关 HTTP 请求的信息/param/// param nameconstraint用于测试 parameterName 的正则表达式或对象/param/// param nameparameterName要测试的参数的名称/param/// param namevalues要测试的值/param/// param namerouteDirection一个指定 URL 路由是否处理传入请求或构造 URL 的值/param/// returns如果参数值与约束匹配则为 true否则为 false。/returnsprotected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
} 静态路由表的数据结构即为Route类的结构它遵循一定规则存放数据如上代码所示将设置的Url规则存放于Url属性中将处理程序存放于RouteHandler属性中。 将用户设置的其它数据存放于字典RouteValueDictionary中并将字典分为3个类别 Defaults描述Url规则的默认值 Constraints描述Url规则的约束 DataTokens其它自定义数据如处理程序的命名空间 为结合项目应用更直观的演示静态路由表的数据结构我编写了一个简单的DEMO 向“静态路由表”中添加数据 public class RouteConfig
{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute({resource}.axd/{*pathInfo});routes.MapRoute(name: Default,url: {controller}/{action}/{id},defaults: new { controller Home, action Index, id UrlParameter.Optional },constraints: new { controller [a-zA-Z] },namespaces: new string[] { WebRoutingDemo.Controllers.HomeController } );}
} 打印“静态路由表”中所有数据 protected void Page_Load(object sender, EventArgs e)
{//静态路由表System.Web.Routing.RouteCollection routeItems System.Web.Routing.RouteTable.Routes;//遍历静态路由表foreach (System.Web.Routing.Route routeItem in routeItems){//UrlResponse.Write(strongUrl/strongbr /);Response.Write(string.Format(nbsp;nbsp;nbsp;nbsp;{0}br /, routeItem.Url));//DefaultsResponse.Write(strongDefaults/strongbr /);if (routeItem.Defaults ! null){foreach (KeyValuePairstring, object defaultItem in routeItem.Defaults){Response.Write(string.Format(nbsp;nbsp;nbsp;nbsp;{0}{1}br /, defaultItem.Key, defaultItem.Value));}}//ConstraintsResponse.Write(strongConstraints/strongbr /);if (routeItem.Constraints ! null){foreach (KeyValuePairstring, object constraintsItem in routeItem.Constraints){Response.Write(string.Format(nbsp;nbsp;nbsp;nbsp;{0}{1}br /, constraintsItem.Key, constraintsItem.Value));}}//DataTokens Response.Write(strongDataTokens/strongbr /);if (routeItem.DataTokens ! null){foreach (KeyValuePairstring, object dataTokensItem in routeItem.DataTokens){Response.Write(string.Format(nbsp;nbsp;nbsp;nbsp;{0}{1}br /, dataTokensItem.Key, dataTokensItem.Value));}}Response.Write(br /);}
} 打印结果 Url api/{controller}/{id}Defaults idConstraintsDataTokensUrl {resource}.axd/{*pathInfo}DefaultsConstraintsDataTokensUrl {controller}/{action}/{id}Defaults controllerHome actionIndex idConstraints controller[a-zA-Z]DataTokens NamespacesSystem.String[] 路由的核心逻辑 路由的核心逻辑是将请求的URL与“静态路由表”中配置的URL规则进行比较找到第一条匹配的记录并将请求交由其处理程序RouteHandler进行后续处理。 请求的URL如何与“静态路由表”进行匹配我猜想过程应该是这样首先一个URL请求到达然后遍历“静态路由表”将表中的URL表达式与请求的URL进行比较如请求URL符合该表达式获取RouteHandler并中止遍历。当然这只是我的猜想实际微软把这个过程封装在了System.Web.Routing.ParsedRoute类中感兴趣的朋友可通过反编译查看详细过程。 在应用层面微软为开发者提供了一个扩展点以实现对路由的自定义处理System.Web.Routing.RouteBase.GetRouteData()方法。这是一个抽象方法也就是说只要是“静态路由表”中的记录都必须实现这个方法。如下代码 System.Web.Routing.RouteBase 类 public abstract class RouteBase
{protected RouteBase();/// summary/// 当在派生类中重写时会返回有关请求的路由信息/// /summary/// param namehttpContext一个对象封装有关 HTTP 请求的信息/param/// returns一个对象包含路由定义的值如果该路由与当前请求匹配或 null如果该路由与请求不匹配/returnspublic abstract RouteData GetRouteData(HttpContextBase httpContext);/// summary/// 当在派生类中重写时会检查路由是否与指定值匹配如果匹配则生成一个 URL然后检索有关该路由的信息/// /summary/// param namerequestContext一个对象封装有关所请求的路由的信息/param/// param namevalues一个包含路由参数的对象/param/// returns一个对象包含生成的 URL 和有关路由的信息或 null如果路由与 values 不匹配/returnspublic abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
} 通过GetRouteData()方法判断请求的URL是否与“静态路由表”中的记录匹配如返回一个RouteData对象表示匹配成功循环中止如返回null表示匹配失败循环继续直到遍历完整个“静态路由表”。大致逻辑如下图所示 图2 System.Web.Routing.RouteData类 public class RouteData
{public RouteData();public RouteData(RouteBase route, IRouteHandler routeHandler);/// summary/// 获取在 ASP.NET 路由确定路由是否匹配请求时传递到路由处理程序但未使用的自定义值的集合。/// 属性Namespaces表示辅助Controller类型的解析而设置的命名空间列表该属性值从DataTokens字典中提取对应的Key为namespaces/// 返回一个包含自定义值的对象。/// /summarypublic RouteValueDictionary DataTokens { get; }/// summary/// 获取或设置表示路由的对象。/// 返回一个表示路由定义的对象。/// /summarypublic RouteBase Route { get; set; }/// summary/// 获取或设置处理所请求路由的对象。/// 返回一个处理路由请求的对象。/// /summarypublic IRouteHandler RouteHandler { get; set; }/// summary/// 获取路由的 URL 参数值和默认值的集合。/// 直接从请求地址解析出来的变量/// Controller和Action名称的同名属性直接从Values字典中提取对应的Key分别为controller和action/// 返回一个对象其中包含根据 URL 和默认值分析得出的值。/// /summarypublic RouteValueDictionary Values { get; }/// summary/// 使用指定标识符检索值。/// /summary/// param namevalueName要检索的值的键。/param/// returns其键与 valueName 匹配的 System.Web.Routing.RouteData.Values 属性中的元素。/returnspublic string GetRequiredString(string valueName);
} 总结 我们设置的URL规则通过System.Web.Routing.RouteCollection.Add()方法添加进“静态路由表”每条规则以一个Route对象存放对象的Defaults属性存放了我们设置的URL默认值Constraints属性存放了对URL的一些约束DataTokens属性存放其它数据。 当用户浏览网页时请求的URL与静态路由表的记录进行匹配匹配逻辑和过程包括在实现了RouteBase.GetRouteData的一个方法中微软的Route.GetRouteData为我们提供了一个默认实现当然我们也可以在自定义的方法中实现如DEMO代码所示。 URL与“静态路由表”是否匹配取决于GetRouteData方法是否返回RouteData对象如果RouteData为null表示未匹配。如果RouteData不为null则表示URL与“静态路由表”相匹配并将RouteHandler的值传递到RouteData中供后续逻辑使用。 DEMO DEMO参考了DomainRoute的源代码示例了一个自定义的路由处理过程它实现了GetRouteData方法演示了路由处理的一些细节包括URL规则与请求URL的匹配过程、将URL中的名称参数、Defaults、DataTokens还原为RouteData对象。 开发环境.Net Framework 4.0Visual Studio 2012 下载地址http://files.cnblogs.com/wu-jian/WebRoutingDemo.rar 全文完 如果您觉得本文对您有所帮助可扫描两侧的二维码向作者打赏。您的支持是原创的源动力 作者吴剑 出处http://www.cnblogs.com/wu-jian/ 本文版权归作者所有欢迎转载但必需注明出处并且在转载页面明显位置给出原文连接否则保留追究法律责任的权利。 转载于:https://www.cnblogs.com/wu-jian/archive/2012/10/22/2579668.html