(1)控制器和动作方法的名字不区分大小写。
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}", new { Controller = "Home", Action = "Index"}); }
对于测试
[TestMethod] public void TestIncomingRoutes() { TestRouteMatch("~/", "home", "index"); //通过 }
TestRouteMatch("~/AAA/BBB", "aaa", "bbb"); //通过
TestRouteMatch("~/AAA", "aaa", "Index"); //通过
(2)可以使用静态URL片段,使路由只匹配地址中某一部分为固定值的URL
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("", "Public/{controller}/{action}", new { Controller = "Home", Action = "Index" }); }
对于测试:
[TestMethod] public void TestIncomingRoutes() { TestRouteMatch("~/Public/AAA/BBB", "AAA", "BBB"); TestRouteMatch("~/Public/AAA", "AAA", "Index"); TestRouteMatch("~/Public", "Home", "Index"); }
上面三种形式能通过测试,除此以外都不行。
(3)使用静态URL片段时要注意路由顺序
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}", new { Controller = "Home", Action = "Index"});
routes.MapRoute("", "Public/{controller}/{action}", new { Controller = "Home", Action = "Index" }); }
上面有两个路由,因此URL进来匹配的时候要注意顺序,从上往下,依次匹配。
这个时候,对于如下URL
"~/Public"
就匹配不了第二个路由了,因为按照从上往下的顺序,在第一个路由匹配的时候就被拦截。
对于测试:
[TestMethod] public void TestIncomingRoutes() { TestRouteMatch("~/Public/AAA/BBB", "AAA", "BBB"); //通过,第一个路由不匹配3段式,漏下来匹配第二个路由 TestRouteMatch("~/Public/AAA", "AAA", "Index"); //出错,两段式、一段式或"~/"都被第一个路由拦截。控制器应匹配为Public,动作为AAA TestRouteMatch("~/Public", "Home", "Index"); //出错,被第一个路由拦截。控制器应匹配为Public,动作为Index }
(4)使用静态URL片段将指定的旧地址重新映射到新的控制器和动作方法上
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("ShopSchema2", "Shop/OldAction", //注意没有大括号是静态URL new { Controller = "Home", Action = "NewAction"}); routes.MapRoute("ShopSchema", "Shop/{action}", new { Controller = "Home", Action = "Index" }); }
对于测试:
[TestMethod] public void TestIncomingRoutes() { TestRouteMatch("~/Shop/OldAction", "Home", "NewAction"); TestRouteMatch("~/Shop", "Home", "Index"); TestRouteMatch("~/Shop/AAA", "Home", "AAA"); TestRouteFail("~/AAA/BBB"); }
"~/Shop/OldAction" 将原来指定的这种URL重新映射到控制器Home、动作方法NewAction上。
"~/Shop"第一个路由不匹配,漏下来,匹配第二个路由,控制器默认为Home,动作方法没有给出来,默认为Index。
"~/Shop/AAA"第一个路由不匹配,匹配第二个路由,控制默认为Home,动作方法指定为AAA。
"~/AAA/BBB"没有匹配的路由,只定义了两个路由,都必须由静态地址~/Shop开头。
(5)URL上并不是只匹配controller和action变量,也可以定义自己的变量。
例如:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { Controller = "Home", Action = "Index", id = "DefaultId" }); }
通过使用RouteData.Values属性,可以在一个动作方法中访问任何一个片段变量。
下面添加HomeController类,编写Index动作方法:
public ViewResult Index() { ViewBag.id = RouteData.Values["id"]; return View(); }
这里使用RouteData.Values["id"]将URL里对应到路由中的自定义变量id的值读取出来,再使用ViewBag就可以把它传递给视图。编写/Views/Home文件夹中的Index.cshtml:
@{ ViewBag.Title = "Index"; } <h2>ID: @ViewBag.id</h2>
这样,对于:
"~/"
"~/Home"
"~/Home/Index"
"~/Home/Index/DefaultId"
都是匹配到"~/Home/Index"页面,id变量的值都是DefaultId,在页面上显示出来的结果都为ID:DefaultId
如果在浏览器上输入的地址是"~/Home/Index/AAA",则匹配到页面"~/Home/Index",id变量的值为AAA,页面显示出来的结果为ID:AAA
在例如,保持路由定义不变,增加一个叫做CustomVariable的动作方法到HomeController类中:
public ViewResult CustomVariable() { ViewBag.CustomVariable = RouteData.Values["id"]; return View(); }
编写该动作方法对应的视图,即在/Views/Home文件夹中的CustomVariable.cshtml
@{ ViewBag.Title = "CustomVariable"; } <h2>CustomVariable: @ViewBag.CustomVariable</h2>
这样,对于:
"~/Home/CustomVariable"
"~/Home/CustomVariable/DefaultId"
都是匹配到"~/Home/CustomVariable"页面,id变量的值都是DefaultId,在页面上显示出来的结果都为ID:DefaultId
如果要对该路由定义:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { Controller = "Home", Action = "Index", id = "DefaultId" }); }
进行测试,则下面的测试可以通过:
[TestMethod] public void TestIncomingRoutes() { TestRouteMatch("~/", "Home", "Index", new { id = "DefaultId"}); TestRouteMatch("~/Customer", "Customer", "Index", new { id = "DefaultId" }); TestRouteMatch("~/Customer/List", "Customer", "List", new { id = "DefaultId" }); TestRouteMatch("~/Customer/List/All", "Customer", "List", new { id = "All" }); TestRouteFail("~/Customer/List/All/Delete"); }
(6)使用跟自定义变量相同的名字来作为动作方法参数,以此来传递URL中对应到自定义变量的值
例如,对于上面的例子,有路由定义:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { Controller = "Home", Action = "Index", id = "DefaultId" }); }
如果对HomeController类的Index动作方法:
public ViewResult Index(string id) //跟路由定义中的自定义变量id同名 { ViewBag.id = id; return View(); }
这里就不再使用RouteData.Values["id"]来将URL里对应到路由中的自定义变量id的值读取出来了。而是直接在动作方法上使用了跟路由定义中自定义变量id相同的名字作为参数,直接通过名字匹配后传递。
假设,跟上面的例子一样编写该动作方法对应的视图,即在/Views/Home文件夹中的Index.cshtml
@{ ViewBag.Title = "Index"; } <h2>ID: @ViewBag.id</h2>
这样,对于:
"~/"
"~/Home"
"~/Home/Index"
"~/Home/Index/DefaultId"
都是匹配到"~/Home/Index"页面,id变量的值都是DefaultId,在页面上显示出来的结果都为ID:DefaultId
如果在浏览器上输入的地址是"~/Home/Index/AAA",则匹配到页面"~/Home/Index",id变量的值为AAA,页面显示出来的结果为ID:AAA
注意,这里参数中的id,定义的是string类型,你也可以根据需要定义成int、DateTime等其他类型,根据名字id在URL模式上匹配后,会自动将URL中对应的自定义变量的值转换为参数中的指定类型。
(7)定义可选的URL片段
对于URL模式中的自定义变量,如果用户给出的访问地址URL中没有该自定义变量的值,即没有这个片段,但是我们又不想在定义路由的时候指定它的默认值,这时就可以用UrlParameter.Optional来指定可选URL片段。
例如:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional }); }
在路由定义中,指定了URL模式中的自定义变量id是可选片段。
如果有HomeController类的Index动作方法:
public ViewResult Index(string id) //跟路由定义中的自定义变量id同名 { ViewBag.id = id; return View(); }
跟上面的例子一样编写该动作方法对应的视图,即在/Views/Home文件夹中的Index.cshtml
@{ ViewBag.Title = "Index"; } <h2>ID: @ViewBag.id</h2>
那么,对于
这样,对于:
"~/"
"~/Home"
"~/Home/Index"
都没有匹配的id的值,因为url中没有id对应的片段,因此url模式中的自定义变量id根本没有定义,那么,对于动作方法Index的参数也就找不到对应的的值。所以显示的结果都是:
ID:
但是,如果输入的url是"~/Home/Index/ABC"
那么此时显示结果为
ID:ABC
这样做有一个好处,就是强制关注分离,使id的默认值不要设置在url的路由定义中。如果需要对id设置默认值,可以把它设置在函数参数中。
例如,对于HomeController类的Index动作方法:
public ViewResult Index(string id = "DefaultId") //跟路由定义中的自定义变量id同名 { ViewBag.id = id; return View(); }
在测试含有UrlParameter.Optional的url片段时,需要注意的是,如果用户给出的url中未含有这个片段变量对应的值,那么,这个片段变量就不会被添加到RouteData.Values集合中。
例如,对于路由定义:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional }); }
可以通过以下测试:
[TestMethod] public void TestIncomingRoutes() { TestRouteMatch("~/", "Home", "Index); TestRouteMatch("~/Customer", "Customer", "Index"); TestRouteMatch("~/Customer/List", "Customer", "List"); TestRouteMatch("~/Customer/List/All", "Customer", "List", new { id = "All" }); TestRouteFail("~/Customer/List/All/Delete"); }
(8)定义可变长路由,使用{*catchall}
有路由定义:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional }); }
路由定义的后面使用了{*catchall},那么这个路由中的url模式匹配的片段数目没有上限。{*catchall}捕获的片段是以“片段/片段/片段”的形式表示的,不包括前面和后面的斜线“/”字符,程序员要自己负责处理这个字符串,自行拆分。
可以通过以下测试:
[TestMethod] public void TestIncomingRoutes() { TestRouteMatch("~/", "Home", "Index"); TestRouteMatch("~/Customer", "Customer", "Index"); TestRouteMatch("~/Customer/List", "Customer", "List"); TestRouteMatch("~/Customer/List/All", "Customer", "List", new { id = "All" }); TestRouteMatch("~/Customer/List/All/Delete", "Customer", "List", new { id = "All", catchall = "Delete" }); TestRouteMatch("~/Customer/List/All/Delete/Perm", "Customer", "List", new { id = "All", catchall = "Delete/Perm" }); }
(9)按命名空间区分控制器优先顺序
假设在当前解决方案中有主项目11-3URLTestDemo,另在解决方案中通过file->add->new project,在项目类型上选择了Visula C#->web->asp.net mvc3 web application,添加了一个新项目叫做AdditionalController。假设在两个项目的Controllers中都有HomeController控制器。并且主项目添加了对AdditionalController项目的引用(注意顺序),那么当访问" ~/Home" 时,由于两个命名空间中都有HomeController,所以会产生冲突,出现错误。这时可以在主项目的路由定义中指定优先匹配的命名空间:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { Controller = "Home", Action = "Index", id = UrlParameter.Optional }, new[] { "_11_3URLTestDemo.Controllers" }); }
-lyj