第二节:各种路由约束(动态路由、静态路由、组合路由、正则约束、命名空间约束、区域内路由)
一. 什么是路由
路由是约束URL的一组规范,那么什么是URL呢?通俗的来说URL是一个地址,通过该地址,用户可以访问Web网站或者下载服务器上的文件。
比如下面就是两组URL:
http://www.cnblogs.com/yaopengfei/p/7828441.html
http://www.cnblogs.com/yaopengfei/p/7828441
显然我们喜欢第二组,省略.html,会使该地址看起来更加简洁,更加友好,利于SEO优化。
那么我们怎么实现这个简单的需求呢?
答案是:通过【路由】配置,所以现在我们似乎有点清晰了,路由可以规定URL的特殊格式,使其达到特殊效果。
在ASP.NET MVC框架中,通过路由配置URL,使用户的URL请求可以映射到Controller下的action方法中,执行相应操作,并接受URL中传过来的参数,在MVC5框架中,在【RouteConfig.cs】类中进行路由规则的配置,如下图:
二. 从源码的角度分析路由
我们进入【RouteConfig】类中,发现核心代码是调用【RouteCollection】类下的MapRoute方法,F12看源码得知,MapRoute方法是RouteCollectionExtensions类的给【RouteCollection】类扩展方法的方法,并且有多个重载,如下图:
下面分析一下参数的含义:
(1) name: 要映射的路由的名称。
(2) url: 路由的 URL 模式,可以自定义路由的格式,可以写静态路由,也可以写动态路由、组合路由等。
(3) defaults: 一个包含默认路由值的对象,书写路由的默认值。
(4) constraints: 一组表达式,可以使用正则指定 url 参数值的约束。
(5) namespaces: 应用程序的一组命名空间,可以缩小检索路由对象匹配的范围。
底层源码,有兴趣可以看一下:
1 public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) 2 { 3 if (routes == null) 4 { 5 throw new ArgumentNullException("routes"); 6 } 7 if (url == null) 8 { 9 throw new ArgumentNullException("url"); 10 } 11 Route route = new Route(url, new MvcRouteHandler()) { 12 Defaults = CreateRouteValueDictionaryUncached(defaults), 13 Constraints = CreateRouteValueDictionaryUncached(constraints), 14 DataTokens = new RouteValueDictionary() 15 }; 16 ConstraintValidation.Validate(route); 17 if ((namespaces != null) && (namespaces.Length > 0)) 18 { 19 route.DataTokens["Namespaces"] = namespaces; 20 } 21 routes.Add(name, route); 22 return route;
三. MVC中的几类路由及其规则
1. 动态路由
1 routes.MapRoute( 2 name: "Default", //路由名称 3 url: "{controller}/{action}/{id}", //路由规则 4 defaults: new { controller = "First", action = "Index1", id = UrlParameter.Optional } //默认值,当Controller或action为空(省略)的时候调用 5 );
分析:路由规则为 {controller}/{action}/{id} ,其中 {controller}、{action}、{id}为三个参数,/ 为格式分割符号,defaults中声明的是默认路由,所以下面的测试结果:
http://localhost:7559/
http://localhost:7559/First
http://localhost:7559/First/index1
http://localhost:7559/First/Index1/2
都会跳转到Index1的页面。
变种:将上面的代码的URL分割符号稍微调整一下
1 routes.MapRoute( 2 name: "Default9", //路由名称 3 url: "{controller}/{action}-{id}", //路由规则 4 defaults: new { controller = "First", action = "Index1", id = UrlParameter.Optional } //默认值,当Controller或action为空(省略)的时候调用 5 );
请求地址就变成了:
http://localhost:7559/ 【无法访问】
http://localhost:7559/First 【404找不到】
http://localhost:7559/First/index1 【404找不到】
http://localhost:7559/First/Index1-1 【可以访问】
2. 静态路由
1 routes.MapRoute( 2 name: "Default2", //路由名称 3 url: "Ypf", //路由规则,不区分大小写,当输入“ypf”时,会自动跳转到下面的地址 4 defaults: new { controller = "First", action = "Index1", id = UrlParameter.Optional } //默认值,当Controller或action为空的时候调用 5 );
静态路由:url中是一个静态值,访问的URL只有输入这个静态值,才能访问下面default中的默认值。
测试地址如下:
http://localhost:7559/ 【无法访问】
http://localhost:7559/ypf 【 跳转到index1页面】
http://localhost:7559/First/index1 【无法访问】
补充一下:MapRoute方法是可以不需要写参数名的,就像正常的调用方法一样,所以上面的代码可以改写:
1 routes.MapRoute( 2 "Default3", //路由名称 3 "Ypf", //路由规则,不区分大小写,当输入“Ypf”时,会自动跳转到下面的地址 4 new { controller = "First", action = "Index1", id = UrlParameter.Optional } //默认值,当Controller或action为空的时候调用 5 );
3. 组合路由
1 routes.MapRoute( 2 "Default4", //路由名称 3 "Ypf/{action}", //路由规则,不区分大小写,规则相符的时候,会自动跳转到下面的地址 4 new { controller = "First", action = "Index1" } 5 );
所谓的组合路由,就是静态路由和动态路由相互组合使用,测试地址如下:
http://localhost:7559/ 【无法访问】 (分析:因为不满足路由规则,没有输入ypf)
http://localhost:7559/ypf 【 跳转到index1页面】
http://localhost:7559/ypf/index1 【 跳转到index1页面】
http://localhost:7559/Ypf/hh 【404找不到】 (满足路由规则,但是没有hh这个action,所以404)
http://localhost:7559/ypf/First/index1 【404找不到】 (满足路由规则,但这里把First当做action,并没有这个action,所以404)
4. 正则约束
1 routes.MapRoute( 2 "Default5", 3 "{controller}/{action}_{Year}_{Month}_{Day}", 4 new { controller = "First", action = "Index1", id = UrlParameter.Optional }, 5 new { Year = @"^\d{4}", Month = @"\d{2}", Day = @"\d{2}" } 6 );//正则路由
所谓的正则约束,是指可以对URL中的参数使用正则表达式进行约束,上述代码约束了Year必须是四位数字,Month和Day必须是两位数字。
测试地址:
http://localhost:7559/first/index1_2018_09_01 【跳转到index1页面】
http://localhost:7559/first/index1 【无法访问】 (分析:因为不满足路由规则,没有输入{Year}_{Month}_{Day} 的参数)
http://localhost:7559/first/ 【无法访问】 (分析:因为不满足路由规则,没有输入{Year}_{Month}_{Day} 的参数)
http://localhost:7559/ 【无法访问】 (分析:因为不满足路由规则,没有输入{Year}_{Month}_{Day} 的参数)
5. 命名空间约束
1 routes.MapRoute( 2 name: "Default6", 3 url: "{controller}/{action}/{id}", 4 defaults: new { controller = "Third", action = "Index", id = UrlParameter.Optional }, 5 namespaces: new string[] { "Ypf.MVC5" } 6 );
所谓的命名空间约束,即限定匹配范围路由的检索范围,提高检索速度。
特别注意:不能从外层控制器直接跳转到内层Area内的控制器!!
测试地址:以下三个访问地址,都会跳转到index1页面
http://localhost:7559/
http://localhost:7559/First
http://localhost:7559/First/index1
6. Area区域内的路由
1 public override void RegisterArea(AreaRegistrationContext context) 2 { 3 //原路由 4 //context.MapRoute( 5 // "TestOne_default", 6 // "TestOne/{controller}/{action}/{id}", 7 // new { action = "Index", id = UrlParameter.Optional } 8 //); 9 10 //结合命名空间进行路由改造 11 context.MapRoute( 12 this.AreaName + "_default", 13 this.AreaName + "/{controller}/{action}/{id}", 14 new { area = this.AreaName, controller = "Sys_Admin", action = "Index", id = UrlParameter.Optional }, 15 new string[] { "Ypf.MVC5.Areas." + this.AreaName + ".Controllers" } 16 ); 17 18 }
指MVC5框架中Area区域单独的一套路由规则,我们可以结合区域内的原路由,进行改造一番,如上述代码。