路由匹配的几点小结

(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

原文地址:https://www.cnblogs.com/brown-birds/p/3738040.html