本篇文章从基础到深入的介绍ASP.NET MVC中的Routing组件. Routing翻译过来是"路由选择", 负责ASP.NET MVC的第一个工作:识别URL, 将一个Url请求"路由"给Controller.
第一篇文章中我们已经学会了如何使用ASP.NET MVC, 虽然其中还有很多的细节没有深入了解, 但是对基本的处理流程已经有了认识:来了一个Url请求, 从中找到Controller和Action的值, 将请求传递给Controller处理. Controller获取Model数据对象, 并且将Model传递给View, 最后View负责呈现页面.
而Routing的作用就是负责分析Url, 从Url中识别参数, 如图:
这一讲就让我们细致的了解System.Web.Routing及其相关的扩展知识.
第一讲中实例的首页地址是: localhost/home/index
我们发现访问上面的地址, 最后会传递给 HomeController中名为index的action(即HomeController类中的index方法).
当然服务器端不会自己去实现这个功能, 关键点就是在Global.asax.cs文件中的下列代码:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults ); } protected void Application_Start() { RegisterRoutes(RouteTable.Routes); }
回来看我们的Url: localhost/home/index
localhost是域名, 所以首先要去掉域名部分: home/index
对应了上面代码中的这种URL结构: {controller}/{action}/{id}
因为我们建立了这种Url结构的识别规则, 所以能够识别出 Controller是home, action是index, id没有则为默认值"".
这就是Routing的第一个作用:
1.从Url中识别出数据.比如controller,action和各种参数.
如果跟踪程序, 接下来我们会跳转到HomeController中的Index()方法. 这是Routing内部为实现的第二个作用:
2.根据识别出来的数据, 将请求传递给Controller和Action.
但从实例中我们并不知道Routing如何做的这部份工作.第五部分我做了深入讲解.
在分析Routing的实现原理前, 先学习如何使用Routing为ASP.NET MVC程序添加路由规则.
这是最简单的为ASP.NET MVC添加识别规则的方法.此方法有如下重载:
MapRoute( string name, string url); MapRoute( string name, string url, object defaults); MapRoute( string name, string url, string[] namespaces); MapRoute( string name, string url, object defaults, object constraints); MapRoute( string name, string url, object defaults, string[] namespaces); MapRoute( string name, string url, object defaults, object constraints, string[] namespaces);
规则名称, 可以随意起名.当时不可以重名,否则会发生错误:
路由集合中已经存在名为“Default”的路由。路由名必须是唯一的。
url获取数据的规则, 这里不是正则表达式, 将要识别的参数括起来即可, 比如: {controller}/{action}
最少只需要传递name和url参数就可以建立一条Routing(路由)规则.比如实例中的规则完全可以改为:
routes.MapRoute( "Default", "{controller}/{action}");
url参数的默认值.如果一个url只有controller: localhost/home/
而且我们只建立了一条url获取数据规则: {controller}/{action}
那么这时就会为action参数设置defaults参数中规定的默认值. defaults参数是Object类型,所以可以传递一个匿名类型来初始化默认值:
new { controller = "Home", action = "Index" }
实例中使用的是三个参数的MapRoute方法:
routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults );
用来限定每个参数的规则或Http请求的类型.constraints属性是一个RouteValueDictionary对象,也就是一个字典表, 但是这个字典表的值可以有两种:
用于定义正则表达式的字符串。正则表达式不区分大小写。
一个用于实现 IRouteConstraint 接口且包含 Match 方法的对象。
通过使用正则表达式可以规定参数格式,比如controller参数只能为4位数字:
new { controller = @"\d{4}"}
通过第IRouteConstraint 接口目前可以限制请求的类型.因为System.Web.Routing中提供了HttpMethodConstraint类, 这个类实现了IRouteConstraint 接口. 我们可以通过为RouteValueDictionary字典对象添加键为"httpMethod", 值为一个HttpMethodConstraint对象来为路由规则添加HTTP 谓词的限制, 比如限制一条路由规则只能处理GET请求:
httpMethod = new HttpMethodConstraint( "GET", "POST" )
完整的代码如下:
routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" }, // Parameter defaults new { controller = @"\d{4}" , httpMethod = new HttpMethodConstraint( "GET", "POST" ) } );
当然我们也可以在外部先创建一个RouteValueDictionary对象在作为MapRoute的参数传入, 这只是语法问题.
此参数对应Route.DataTokens属性. 官方的解释是:
获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值。
我目前不知道如何使用. 请高手指点
下面通过实例来应用MapRoute方法. 对于一个网站,为了SEO友好,一个网址的URL层次不要超过三层:
localhost/{频道}/{具体网页}
其中域名第一层, 频道第二层, 那么最后的网页就只剩下最后一层了. 如果使用默认实例中的"{controller}/{action}/{其他参数}"的形式会影响网站的SEO.
假设我们的网站结构如下:
下面以酒店频道为例, 是我创建的Routing规则:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); #region 酒店频道部分 // hotels/list-beijing-100,200-3 routes.MapRoute( "酒店列表页", "hotels/{action}-{city}-{price}-{star}", new { controller = "Hotel", action = "list", city = "beijing", price="-1,-1", star="-1" }, new { city=@"[a-zA-Z]*",price=@"(\d)+\,(\d)+", star="[-1-5]"} ); //hotels/所有匹配 routes.MapRoute( "酒店首页", "hotels/{*values}", new { controller = "Hotel", action = "default", hotelid = "" } ); #endregion //网站首页. routes.MapRoute( "网站首页", "{*values}", new { controller = "Home", action = "index"} ); }
实现的功能:
(1)访问 localhost/hotels/list-beijing-100,200-3 会访问酒店频道的列表页,并传入查询参数
(2)访问 localhost/hotels 下面的任何其他页面地址, 都会跳转到酒店首页.
(3)访问 localhost 下面的任何地址, 如果未匹配上面2条, 则跳转到首页.
简单总结:
(1)Routing规则有顺序(按照添加是的顺序), 如果一个url匹配了多个Routing规则, 则按照第一个匹配的Routing规则执行.
(2)由于上面的规则, 要将具体频道的具体页面放在最上方, 将频道首页 和 网站首页 放在最下方.
(3) {*values} 表示后面可以使任意的格式.
MapRoute方法虽然简单, 但是他是本质也是通过创建Route类的实例, 为RouteCollection集合添加成员.
下载最新版本的MSDN-Visual Studio 20008 SP1, 已经可以找到Route类的说明.
创建一个Route类实例,最关键的是为以下几个属性赋值:
属性名称 | 说明 | 举例 | Constraints | 获取或设置为 URL 参数指定有效值的表达式的词典。 | {controller}/{action}/{id} | DataTokens | 获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值。 | new RouteValueDictionary { { "format", "short" } } | Defaults | 获取或设置要在 URL 不包含所有参数时使用的值。 | new { controller = "Home", action = "Index", id = "" } | RouteHandler | 获取或设置处理路由请求的对象。 | new MvcRouteHandler() | Url | 获取或设置路由的 URL 模式。 | new { controller = @"[^\.]*" } |