路由书写规则的总结
概念:Routing System由一组路由组成,每一个路由规则可以匹配一种类型的URL,在请求过来的时候,Ruting ystem 就用它来处理这个URL,路由的任务就是匹配URL,并且将其中的参数值取出来,路由组册的方法在app_Start 的时候就调用了。
路由中Segment的概念:
上面这个URL的路由规则可以是:routes.MapRoute("MyRoute", "{controller}/{action}");
路由匹配的一般规则:
1、默认情况下我们写的路由的Segment的个数与被匹配的URL的Segment的个数是一致的,否者不会被匹配。
2、匹配原则: 当我们有多条路由的时候,路由的匹配是有顺序的,一般情况下先定义的路由先进行匹配,成功了以后,后面的路由就不匹配了,如果没有成功就接着匹配后面的路由,因此我们定义路由的时候最好,顺序最好遵循这样的规则:将特殊情况都放在前面定义,范围广德路由放在后面匹配。
3、路由的默认值比路由的约束条件先执行(这两者后面会讲到)
4、默认情况下会先在磁盘上找与URL匹配的静态文件,如果找到就不会匹配路由了。
注:如果我们的路由系统只有上面这个一个路由的规则的话,我们运行程序的时候就会报错,因为我们的默认页面请求的URL是没有Segmen的,因此不会被匹配到,这个就涉及到我们后面会讲到的默认值的问题了。
路由默认值 :
有2个Segments的pattern想要匹配有1个或没有Segments的URL的时候,可以为pattern加上默认值,如下:
routes.MapRoute("MyRoute", "{controller}/{action}", new {controller="Home", action="Index"});
MapRoute的第三个参数就是在设置默认值,这时候就可以将默认页面设置为Home/Index
带有静态segment的路由:
routes.MapRoute("MyRoute", "Hu/{controller}/{action}", new {controller="Home", action="Index"});
这个pattern中的开头的Hu没有用{}包起来,说明 Hu 是静态的,这样的结果是:
这个URL必须是以Hu开头的
Segment中混有静态部分的路由:
routes.MapRoute("MyRoute", "Hu{controller}/{action}");
带有自定义Segment的路由:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home", action = "Index",id = "DefaultId" });
上面这个pattern中有一个自定义的Segment——id,同样,我们也可以为他添加默认值。
带有可选的Segment变量的路由:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home", action = "Index",id = UrlParameter.Optional });
在我们为Segment设置默认值以后我们的URL中没有这个Segment也会被匹配到,但是这时候id还是会被添加一个默认的值,有些人特别注重模块之间解耦,那么我们就可以Segment设置为可选的Segment,这样的话即使URL即使没有该Segment也可以被匹配到,而且不会被设置默认值。
可变长度的路由:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",new { controller = "Home", action = "Index",id = UrlParameter.Optional });
前面3个Segment会分别匹配给controller/action/id,剩下的部分会全部匹配给catchcall,这样就可以匹配任意长度的URL了。
有同名的Controller的解决办法:
在URL匹配成功以后,应用程序会到项目中查找符合名称的Controller,如果找到两个同名的符合条件的Controller就会报错,解决如下:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = "1" }, new[]{"Route_Study.AdditionalControllers"});
条件:两个同名的controller在不同的目录下,以上处理的实质是在调用 Maproute方法时,再加一个参数,参数是在给方法指定一个命名空间(一个文件夹),制定以后的效果是:
URL匹配成功后,程序先到指定命名空间中查找controller,查找到以后就不会继续查找了,如果查找不到,就会到其他的命名空间这中查找。
只在制定的文件夹中查找controller:
有一种情况是:我们只需要在特定的命名空间下查找,即使找不到,我们也不会到其他的命名空间查找,这时的pattern为:
Route myRoute = routes.MapRoute("AddContollerRoute", //路由名
"Home/{action}/{id}/{*catchall}", //路由规则
new { controller = "Home", action = "Index",id = UrlParameter.Optional }, //默认值的设置
new[] { "URLsAndRoutes.AdditionalControllers" }); //设置命名空间
myRoute.DataTokens["UseNamespaceFallback"] = false; //只在其他命名空间中查找
带有条件约束的路由:
带有正则表达式约束的路由:
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", //路由名、基本路由规则
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, //默认值
new { controller = "^H.*", action = "^Index$|^About$"}, //约束
new[] { "URLsAndRoutes.Controllers"}); //指定命名空间
}
我们首相要知道的是默认值是在约束的前面的执行的,所以当有一个URL是"/"过来的话,首先是各个Segment得到默认值,然后是看是否符合约束,
对HTTP 请求的Method属性的限定:
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = "^H.*", action = "Index|About",
httpMethod = new HttpMethodConstraint("GET") }, //对URL中Method进行约束
new[] { "URLsAndRoutes.Controllers" });
}
路由的自定义的约束规则:
磁盘文件请求 与 路由匹配的顺序:
在MVC中,并不是所有的请求都是在请求一个controller中的一个Action。
在默认情况下,当一个请求过来的时候,不会马上将URL与路由进行匹配,而是判断磁盘上是否有与其对应的物理文件,如果有就返回物理文件,而路由就不会用到了,我们通过将routes.RouteExistingFiles属性设置成true,来改变磁盘文件匹配的顺序,如下:
public static void RegisterRoutes(RouteCollection routes) {
routes.RouteExistingFiles = true; //改变顺序
routes.MapRoute("ChromeRoute", "{*catchall}",
new { controller = "Home", action = "Index" },
new {customConstraint = new UserAgentConstraint("Chrome")},
new[] { "UrlsAndRoutes.AdditionalControllers" });
要使URL先与路由进行匹配,然后与磁盘上的文件进行匹配,我们还需要对IIS进行配置,具体方法
请看《pro_asp.net_mvc_4_4th_edition》的P358,电子版在www.it-ebooks.info 上可以下载到
在我们将静态文件的匹配放到后面以后,有两种方式来访问静态文件,第一种,为静态文件写路由第二种,用IgnoreRoute方法来访问静态问价
为磁盘上的文件写路由规则:《pro_asp.net_mvc_4_4th_edition》的P359
写这种路由要非常小心,因为这个路由是在最后才会被匹配,它很有可能在前面就已经被匹配掉了,因此这个做法不是首选
更好的解决方案是用 IgnoreRoute方法访问静态文件:
public static void RegisterRoutes(RouteCollection routes) {
routes.RouteExistingFiles = true;
routes.IgnoreRoute("Content/{filename}.html"); 访问静态文件的路由
routes.MapRoute("DiskFile", "Content/StaticContent.html",
new {controller = "Customer",action = "List",});
… ...
IgnoreRoute的语义是忽略路由,但它的作用并没有很好的体现他的语义,我们可以简单的把他看成是在为静态文件写路由,但是这个路由要放在其他路由的前面,这样就不会被其他的路由覆盖了