本文章将关注定义路由,并使用它们去处理URL,使用户能够到达控制器和动作。

文章非常长,可以对路由机制有较初步的了解。首先创建示例项目,项目名为UrlAndRoutes,如下图所示:

然后是创建示例控制器和示例视图,有三个控制器,分别为Admin控制器,Home控制器,Customer控制器,一个命名为ActionName示例视图。这三个控制器都返回ActionName视图。代码如下图所示:

namespace UrlsAndRoutes.Controllers
{public class AdminController : Controller{// GET: Adminpublic ActionResult Index(){ViewBag.Controller = "Admin";ViewBag.Action = "Index";return View("ActionName");}}
}
namespace UrlsAndRoutes.Controllers
{public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}}
}
namespace UrlsAndRoutes.Controllers
{public class CustomerController : Controller{// GET: Customerpublic ActionResult Index(){ViewBag.Controller = "Customer";ViewBag.Action = "Index";return View("ActionName");}public ActionResult List(){ViewBag.Controller = "Customer";ViewBag.Action = "List";return View("ActionName");}}
}

ActionName.cshtml视图的代码很简单,就是返回调用它的control和ActionName的名称,如下图所示:


@{Layout = null;
}<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>ActionName</title>
</head>
<body><div> the control is @ViewBag.Controller</div><div> the action is @ViewBag.Action</div>
</body>
</html>

测试示例代码,启动默认路由,显示页面没有问题,如下图所示:


URL模式简介

路由系统由一组路由来实现它的功能。这些路由共同组成了应用程序的URL架构。这种URL架构是应用程序能够识别并能对之做出响应的一组URL。

URL可以分成几个片段。除主机名和查询字符串之外,这些URL的组成部分都用"/"字符进行分割,如下所示:

第一个片段含有单词“Admin”,第二个片段含有单词“Index”。很显然,第一个片段和控制器有关,第二个片段和动作有关。以下是做这件事的一个URL模式:{ controller } / { action }

当处理一个输入请求时,路由系统的工作是将这个请求URL与一个模式进行匹配,然后从此URL为这个模式中定义的片段变量提取出相应的值。片段变量用花括号 “ { ” 和 “ } ”字符表示。上述示例模式有两个片段变量,其名称分别为“controller”和“action”,因此,controller片段的值是Admin,而action片段的值是Index。

所谓“与一个模式”匹配是指,一个MVC应用程序通常会有几条路由,而路由系统会把输入URL逐一与每条路由的URL模式相比较,直到能找到一条匹配的路由为止。

默认情况下,一个URL模式将匹配具有正确片段的的任何URL。例如,模式{controller} / {action}将匹配任何具有两个片段的URL,如下图所示:

URL匹配
请求URL 片段变量
http://mysite.com/Admin/Index controller = Admin  action = Index
http://mysite.com/Index/Admin controller = Index  action = Admin
http://mysite.com/Apples/Orange controller = Apples action = Orange
http://mystie.com/Admin 不匹配,片段太少
http://mysite/Admin/Index/Soccer 不匹配,片段太多

上图突出了URL模式的两个关键行为:

1、URL模式是保守的,因此只匹配与模式具有相同片段的URL。你可以从表中第四个,第五个例子看到这种情况(片段数不同就是不匹配)。

2、URL模式是宽松的,如果一个URL正好是具有正确的片段数,该模式就用来为片段变量提取值,而不管这个值是什么。

创建并注册一条简单路由

路由是在RouteConfig.cs文件中进行定义的,该文件位于项目的App_start文件夹中。 代码如下图所示:

namespace UrlsAndRoutes
{public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute(name: "Default",url: "{controller}/{action}/{id}",defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });}}
}

先删掉上图中RegisterRoutes方法中其他代码,以便更加关注重点,如下图所示:

namespace UrlsAndRoutes
{public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","{controller}/{action}");}}
}

运行代码,可以看到如下报错界面:

但是如果导航到一个与{controller} / {action } 匹配的URL时,将看到正确显示。如下图,就导航到了/Home/Index:

定义默认值

当请求应用程序的默认值时,出现错误的原因是它不匹配已经定义的路由。前面说过,URL是保守的,他们只匹配指定片段的URL。改变这种行为的一个方式是使用默认值。修改RegisterRoutes方法如下图所示:

namespace UrlsAndRoutes
{public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","{controller}/{action}",new { action = "Index"});}}
}

上述代码中为action片段提供了一个默认值,该路由也将匹配单片段URL。当处理单片段URL时,路由系统将从唯一的URL片段中提取controller的值,并对action变量使用默认值。于是,可以请求http://localhost:29802/home ,系统会自动调用home控制器上的Index动作方法。运行效果如下图所示:

当然,也可以更直接一点,对controller也赋默认值,代码如下图所示:

namespace UrlsAndRoutes
{public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","{controller}/{action}",new {controller = "home", action = "Index"});}}
}

这就是为什么刚新建了一个项目,没有任何片段 就导航到home控制器下的Index动作的原因,因为RegisterRoute方法里默认控制器是home,动作是Index,运行效果如下图所示:

使用静态URL片段

并不是一个URL模式中的所有片段都需要是可变的。也可以创建具有静态片段的模式。假设希望匹配一下这种URL,以支持带有public前缀的URL:http://localhost:29802/Public/Home/Index 可以通过修改如下代码来实现这个效果:

    public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute","public/{controller}/{action}",new {controller = "home", action = "Index"});}}

这个全新的URL模式将只匹配含有三个片段的URL,第一个必须是public,其他两个片段含有任何值,并将被用于controller和action变量。如果省略后两个片段,那么将使用默认值。 运行效果如下图所示:


还可以创建既有静态变量也有可变元素片段的URL模式,如下图代码所示:

        public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("", "X{controller}/{action}");//第一条路由routes.MapRoute("MyRoute","public/{controller}/{action}",//第二条路由new {controller = "home", action = "Index"});}

第一条路由的模式是匹配任意两个片段URL,而第一个片段是以“X”打头。用于controller的值取自第一个片段除“X”以外的部分,Action的值取自第二个片段。如果启动程序并到导航到  /XHome/Index ,可以看到如下图所示:

注意:路由系统根据最先定义的路由模式来匹配一个输入URL,并且只有在不匹配的时候,才会继续对下一条路由进行处理。路由依次被尝试,直到找出匹配的一条,或这组路由被尝试完。所以,必须首先定义教具体的路由。如果将上图中两条路由改变定义的顺序,那么X{controller}这条路由将永远无法到达,路由系统回去找 名为“XHome” 的控制器,因为这个控制器是不存在的,所以会报404——未找到的错误。


可以结合静态变量片段和默认值为特定的路由创建一个别名。如果已经公开发布了URL方案,而且它与你的用户形成了一种契约,那么,创建这种别名可能是有用的。如果在这种情况下(指已经于用户形成契约)重构应用程序,则需要保留以前的URL格式。设想以前用的是一个Shop控制器,现在要有Home控制器来代替。修改代码,下图演示了如何才能创建一个保留旧式URL方案的路由。

        public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("ShopSchem","Shop/{action}",  //新增路由new { controller = "Home" });routes.MapRoute("", "X{controller}/{action}");routes.MapRoute("MyRoute","public/{controller}/{action}",new {controller = "home", action = "Index"});}

添加这条路由匹配第一个片段是“Shop”的任意两片段URL,action的值取自第二个URL片段。这个URL模式不含controller的可变片段,因而会使用所提供的默认值。这意味着对Shop控制器上一个动作请求会被转换成Home控制器的请求,启动程序,并导航到/Shop/Index 网址。如下图所示:

而且可以更进一步,被重构且不再出现在控制器中的动作方法创建别名,为此,只要简单的创建一个静态URL,并提供controller和action的默认值,如下图所示:

        {routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("ShopSchem2", "Shop/OldAction",  //新增路由new { controller = "Home",action = "Index"});routes.MapRoute("ShopSchem","Shop/{action}", new { controller = "Home" });routes.MapRoute("", "X{controller}/{action}");routes.MapRoute("MyRoute","public/{controller}/{action}",new {controller = "home", action = "Index"});}

再一次提醒,要注意放置新路由的位置,以使它被首先定义。这是因为新路由比后面的路由更具体。例如,如果一个对Shop/OldAction 的请求被下一条路由定义来处理,就会得到与想要的不同的结果。这个请求将被处理成一个“404——未找到”错误,并注意,路由名称必须唯一。运行结果,如下图所示:

定义自定义片段变量

contorller和action片段变量对MVC框架而言有特殊的含义,显然,它们对应于请求进行服务的控制器和动作方法。 但是也可以自己定义变量。修改代码如下图所示:

        public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home",action = "Index",id = "DefaultId"});}

该路由的URL模式定义了标准的controller 和 action 变量,以及一个名为“id”的自定义变量。这条路由将匹配任何0~3个片段的URL。第三个片段的内容将被赋值给id变量,而且如果没有第三个片段,将采用默认值。

通过使用RouteData.Values属性,能够在一个动作方法中访问任何一个片段变量。如下图所示,对Home控制器添加了以一个名为“CustomVariable”的动作方法:

namespace UrlsAndRoutes.Controllers
{public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(){ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = RouteData.Values["id"];return View();}}
}

并添加CustomVariable.cshtml视图与之匹配,代码如下图所示:

@{Layout = null;
}<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>CustomVariable</title>
</head>
<body><div> The controller is : @ViewBag.Controller</div><div> The action is @ViewBag.Action</div><div> The custom varibale is @ViewBag.CustomVariable</div>
</body>
</html>

运行程序,并导航到 /Home/CustomVariable/Hello 网址,结果如下图所示:

以为给变量id提供了一个默认值DefaultId,所以导航到  /Home/CustomVariable 网址,结果如下图所示:

用自定义变量作为动作方法参数

使用RouteData.Values属性只是访问自定义路由变量的一种方式。另一种方式要优雅的多。如果以URL模式中的变量相匹配的名称,来定义动作方法的参数,MVC框架将把从URL获得的值作为参数传递给该动作方法。 代码如下图所示:

namespace UrlsAndRoutes.Controllers
{public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(string id){ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = id;return View();}}
}

当路由系统根据上图所定义的路由来匹配一个URL时,URL中第三个片段的值被赋值给了自定义变量id。MVC框架会将片段变量列表与动作方法参数列表进行比较,如果名称匹配,便将URL的值传递给该方法。

这里将id参数定义成一个string,但MVC框架会尝试将URL的值转化为所定义的任何参数类型,如果将id参数声明为int 或者 DateTime,那么,从URL模式接收到的将是一个被解析成该类型示例的值。

定义可选URL片段

可选URL片段是指,用户不需要指定,但又未指定默认值的片段。通过将默认值设置为“UrlParameter.Optional”,便指明了一个片段变量是可选的,如下图所示:

    public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home", action = "Index", id = UrlParameter.Optional});}}

以上的路由将匹配不管是否提供id的URL,下表演示了它对不同URL的工作方式:

匹配带有可选片段变量的URL
片段 示例URL 映射成
0 mydomain.com controller = Home  action = Index
1 mydomain.com/Customer controller = Customer action = Index
2 mydomain.com/Customer/List controller = Customer action = List
3 mydomain.com/Customer/List/All controller = Customer action = List id = All
4 mydomain.com/Customer/List/All/Delete 不匹配,片段太多

由上表可见,只有当输入URL中存在相应片段时,id变量才会被添加到变量集合中。 在下图代码中,对控制器做了修改,以相应无对应值的id片段变量。

namespace UrlsAndRoutes.Controllers
{public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(string id){ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = id ?? "<no value>";return View();}}
}

运行效果如下图所示: 


使用可选URL片段强制关注分离

如果开发人员十分注重MVC模式中的关注分离,他们不喜欢将片段变量的默认值放在应用程序的路由中。如果是因为这样,可以使用C#中的可选参数,以及路由中的可选片段变量,来定义动作方法的参数的默认值。

    public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Home";ViewBag.Action = "Index";return View("ActionName");}public ActionResult CustomVariable(string id = "DefaultId")//可选参数{ViewBag.Controller = "Home";ViewBag.Action = "CustomVariable";ViewBag.CustomVariable = id;return View();}}

以上代码和下面的路由是等价的:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}",new { controller = "Home", action = "Index", id = "DefaultId" });

定义可变长路由

改变URL模式默认保守性的另一种方式是接收可变数目的URL片段。这让你能够以一个单一的路由,对任意长度的URL进行路由。通过指定一个叫做“全匹配(catch)”的片段变量,并以星号(*)作为其前缀,便可以定义对可变片段数的支持。代码如下图所示:

    public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",new { controller = "Home", action = "Index", id = UrlParameter.Optional });}}

现在,这条路由将匹配任何URL,无论URL包含多少个片段数,也不管这些片段的值是什么。前三个片段分别用于设置controller、action和id变量的值。如果URL含有更多的片段,则它们全部被赋给catchall 变量。如下图所示:

匹配带有catchall片段变量的URL
片段数 示例URL 映射成
0 / controller = Home action = Index
1 /Customer controller = Customer action = Index
2 /Customer/List controller = Customer action = List
3 /Customer/List/All controller = Customer action = List id = All
4 /Customer/List/All/Delete controller = Customer action = List id = All catchall = Delete
5 /Customer/List/All/Delete/Perm controller = Customer action = List id = All catchall = Delete/Perm

以上路由中的URL模式所匹配的片段数量是没有上限的。注意:有catchall捕获的片段是以“片段/片段/片段”的形式表示的。你要对这个字符串进行处理,把它分解为一个个片段。


按命名空间区分控制器优先顺序

当一个输入请求URL与一条路由进行匹配时,MVC框架取得controller变量的值,并查找相应的控制器的名称。例如,当controller变量名称是“Home”时,那么,MVC框架会查找名称为“HomeController”的控制器。这是一个不合格的类名,如果两个或者多个名为“HomeController”的控制器,MVC框架将不知道怎么做。

为了测试,在项目的根目录下创建一个名为AdditionalControllers的文件夹,并添加一个Home控制器,如下图所示:

namespace UrlsAndRoutes.AdditionalControllers
{public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Controller = "Additional Controllers  --- Home";ViewBag.Action = "Index";return View("ActionName");}}
}

运行程序,会发现报了如下错误:

很明显可以看到是错误提示是控制器重复了,这个问题比想象的更会经常出现,尤其是在一些大的MVC项目中, 遇到命名冲突只是时间问题。为了解决这一问题,可以告诉MVC框架,在试图解析控制器名称的时候,对某些命名空间给予优先处理,如下图所示:

namespace UrlsAndRoutes
{public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",new { controller = "Home", action = "Index", id = UrlParameter.Optional},new[] { "UrlsAndRoutes.AdditionalControllers" });}}
}

上述代码中把命名空间表示成一个字符串数组,告诉MVC框架,在考察其他命名空间之前,先考察UrlsAndRoutes.AdditionalControllers 命名空间。

如果在这个命名空间中找不到合适控制器,那么MVC框架会默认回到正常行为,并考察所有可用的命名空间。 运行结果如下图所示:

注意:添加到一条路由的命名空间具有同等的优先级,MVC框架不会先检查第一命名空间,然后第二、第三命名空间。也就是说,同一个条路由中的命名空间不是按顺序进行检查的,而是会被同等对待的。如下图中,如果把两个项目的命名空间都加到这条路由里来,还是会报错:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional},
new[] { "UrlsAndRoutes.AdditionalControllers" , "UrlsAndRoutes.Controllers" });

如果希望对一个命名空间的某个控制器给与优先级,但是又要解析另一个命名空间中的所有其他控制器,就需要创建多条路由,如下图所示:

routes.MapRoute("AddControllerRoute", "Home/{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "UrlsAndRoutes.AdditionalControllers" });routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional},
new[] { "UrlsAndRoutes.Controllers"});

当用户明确的请求一个片段为Home的URL时,会运用第一条路由,并且会以AdditionalControllers文件夹中的Home控制器为目标。所有的其他请求,包括未指定第一片段的那些请求,会以controllers文件夹中的控制器去处理。

也可以告诉MVC框架,只考察指定的命名空间。如果找不到一个匹配的控制器,那么框架不会搜索其他地方。代码如下所示:

Route myRoute = routes.MapRoute("AddControllerRoute", "Home/{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "UrlsAndRoutes.AdditionalControllers" });myRoute.DataTokens["UseNamespaecFallback"] = false;//禁止搜索其他命名空间

用则表达式约束路由

    public class RouteConfig{public static void RegisterRoutes(RouteCollection routes){routes.IgnoreRoute("{resource}.axd/{*pathInfo}");routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",new { controller = "Home", action = "Index", id = UrlParameter.Optional},new { controller = "^C.*"},new[] { "UrlsAndRoutes.Controllers"});}}

以上代码用正则表达式形成了一个约束,它只匹配controller变量值已“C”字母的大头的URL。

默认值是是在约束检查之前运用的,因此,如果请求的URL是“/”,会将以默认值“Home”运用于controller,然后才会检查约束。而且,如果此时controller的值是以“C”,则会报错,如下图所示:

如果把约束改成以H开头,则运行正常:

将一条路由约束到一组指定的值

可以用正则表达式来约束一条路由,以便对于一个URL片段,只有指定的一些值才能形成匹配。可以用竖线“|”字符来做这件事。代码如下图所示:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional},
new { controller = "^H.*",action = "^Index$|^About$"},
new[] { "UrlsAndRoutes.Controllers"});

上面这条约束将允许这条路由值匹配 action 片段的值是 “Index” 或 “About”的URL,controller的值必须是H打头。

使用HTTP方法约束路由

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","POST")},//限制请求类型
new[] { "UrlsAndRoutes.Controllers"});

上述代码将这条路由限制到GET和POST请求。

使用类型和值约束

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","POST"),
id = new RangeRouteConstraint(10,20)},//值约束
new[] { "UrlsAndRoutes.Controllers"});

在System.Web.Mvc.Routing.Constraints 命名空间的约束类中,检查片段变量是否是不同的C#类型值,并执行基本的检查。在上图中,使用了RangeRouteConstraint类,它检查提供给片段的变量的值是在两个边界之间的一个有效的int类型。下图描述了完整的约束集合。他们并不接受参数,由于它们将用于配置路由,所以仅显示类名,暂且忽略了属性约束列。

路由属性类
名称 描述 属性约束
AlphaRouteConstraint() 匹配字母字符 主要是(A~Z,a~z) alpha
BoolRouteConstraint() 匹配一个可以解析成bool类型的值 bool
DateTimeRouteConstraint() 匹配一个可以解析成DateTime类型的值 datetime
DecimalRouteConstraint() 匹配一个可以解析成deciaml类型的值 decimal
DoubleRouteConstraint() 匹配一个可以解析成double类型的值 double
FloatRouteConstraint() 匹配一个可以解析成float类型的值 float
IntRouteConstraint() 匹配一个可以解析成int类型的值 int

LengthRouteConstraint(len)

LengthRouteConstraint(min,max)

匹配一个指定字符个数的值,或匹配字符个数在min和max之间的值 length(len)     length(min,max)
LongRouteConstraint() 匹配一个可以解析成long类型的值 long
MaxRouteConstraint(val) 匹配一个值小于val的int值 max(val)
MaxLengthRouteConstraint(len) 匹配一个长度不超过len的字符值 maxlength(len)
MinRouteConstraint(val) 匹配一个值大于val的int值 min(val)
MinLengthRouteConstraint(len) 匹配一个长度至少为len字符串 minlength(len)
RangeRouteConstraint(min,max) 匹配一个值在min和max之间的值 range(min,max)

使用CompoundRouteConstraint类,该类接受一个约束数组作为它构造器的参数,可以为一个单一的片段变量组合不同的约束。如下图所示,同时将AlphaRouteConstraint 和 MinLengthRouteConstraint运用到id片段变量,以确保路由将仅匹配包含字母字符并且至少含有6个字符的字符串值:

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","POST"),
id = new CompoundRouteConstraint(
new IRouteConstraint[] {new AlphaRouteConstraint(),
new MinLengthRouteConstraint(6) })},
new[] { "UrlsAndRoutes.Controllers"});

定义自定义约束

如果标准约束不满足你的需求,可以通过实现IRouteConstraint接口,来定义自己的自定义约束。在示例项目中添加一个Infrastructrue文件夹,并创建新的类UserAgentConstrainst.cs,如下所示:

namespace UrlsAndRoutes.Infrastructure
{public class UserAgentConstraint:IRouteConstraint{private string requiredUserAgent;public UserAgentConstraint(string agentParam){this.requiredUserAgent = agentParam;}public bool Match(HttpContextBase httpContext,Route route,String parameterName,RouteValueDictionary values,RouteDirection routeDirection){return httpContext.Request.UserAgent != null && httpContext.Request.UserAgent.Contains(requiredUserAgent);}}
}

IRouteConstraint 接口定义了 Match方法,它的实现可以用来对路由系统指示它的约束是否已经得到满足。Match方法的参数提供了对以下对象的访问:客户端请求,待评估路由,约束的参数名,从URL提取的片段变量,以及该请求要检查的是输入URL还是输入的URL的细节。对于上述示例,要检查的是客户端请求的UserAgent属性的值,看它是否含有一个被传递给构造器的值。

namespace UrlsAndRoutes.Infrastructure
{public class UserAgentConstraint:IRouteConstraint{private string requiredUserAgent;public UserAgentConstraint(string agentParam){this.requiredUserAgent = agentParam;}public bool Match(HttpContextBase httpContext,Route route,String parameterName,RouteValueDictionary values,RouteDirection routeDirection){return httpContext.Request.UserAgent != null && httpContext.Request.UserAgent.Contains(requiredUserAgent);}}
}

并增加一条路由:

routes.MapRoute("ChromeRoute", "{*catchall}",
new { controller = "Home", action = "Index" },
new { customConstraint = new UserAgentConstraint("Chrome") },
new[] { "UrlsAndRoutes.AdditionalControllers" });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"),
id = new CompoundRouteConstraint(
new IRouteConstraint[] {
new AlphaRouteConstraint(),
new MinLengthRouteConstraint(6) })},
new[] { "UrlsAndRoutes.Controllers"});

第一条路由使他之匹配来自用户代理字符串含有Chrome的浏览器的请求,并指向UrlsAndRoutes.AdditionalControllers。该路由是有一个片段,意味着controller 和 action 总是取自默认值,而不是URL本身。

第二条路由将匹配其他所有请求,并以controller文件夹中的控制器为目标,这两条路由的情况是,有一种浏览器最终只能访问程序的同一个位置。第二条路由动用了约束,第三个片段只能包含6个或者以上的字母字符,以使第二个路由匹配。运行结果如下图所示:

精通ASP.NET MVC ——路由相关推荐

  1. 《精通 ASP.NET MVC 4》----1.3 ASP.NET MVC的关键优点

    本节书摘来自异步社区<精通 ASP.NET MVC 4>一书中的第1章,第1.3节,作者: [美]Adam Freeman ,译者: 李萍 , 徐燕萍 , 林逸 , 更多章节内容可以访问云 ...

  2. 《精通 ASP.NET MVC 3 框架(第三版)》----第2章 准备工作 2.1 准备工作站

    本节书摘来自异步社区<精通 ASP.NET MVC 3 框架(第三版)>一书中的第2章,第1节,作者: [美]Adam Freeman , Steven Standerson,译者: 林逸 ...

  3. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(7.2) 模块管理,模块的添加、修改、删除...

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目 目录索引 简述 今天我们来做模块管理的 添加.修改.删除 项目准备 我们用的工具是:VS 2013 + SqlSer ...

  4. 《精通 ASP.NET MVC 5》----1.8 本书所需的软件

    本节书摘来自异步社区<精通 ASP.NET MVC 5>一书中的第1章,第1.8节,作者: [美]Adam Freeman(弗瑞曼 A.),译者: 张成彬 , 徐燕萍 , 李萍 , 林逸 ...

  5. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(8) 权限管理,自定义权限,扩展权限...

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目 目录索引 简述 今天我们来做权限的管理,这篇比较多 希望新手朋友慢慢消化 项目准备 我们用的工具是:VS 2013 ...

  6. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(5.5) 登录功能的实现,完善登录功能...

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目 目录索引 简述 今天我们来完善我们的登录功能 项目准备 我们用的工具是:VS 2013 + SqlServer 20 ...

  7. ASP.NET MVC路由扩展:路由映射

    上周我写了三篇文章(一.二.三)详细地介绍了ASP.NET的路由系统.ASP.NET的路由系统旨在通过注册URL模板与物理文件之间的映射进而实现请求地址与文件路径之间的分离,但是对于ASP.NET M ...

  8. 《精通 ASP.NET MVC 5》----1.5 本书的结构

    本节书摘来自异步社区<精通 ASP.NET MVC 5>一书中的第1章,第1.5节,作者: [美]Adam Freeman(弗瑞曼 A.),译者: 张成彬 , 徐燕萍 , 李萍 , 林逸 ...

  9. 深入理解Asp.net MVC路由

    深入理解Asp.net MVC路由 吴剑 2012-10-22 原创文章,转载必需注明出处:http://www.cnblogs.com/wu-jian/ 前言 从.Net Framework 1.0 ...

最新文章

  1. AI金融若不解决这些问题,等于在制造新的不可解问题
  2. 通过源代码研究ASP.NET MVC中的Controller和View(二)
  3. ubunu16.04 TensorFlow object detection API 应用配置
  4. 基于投影仪的定位技术
  5. CSS样式:position: absolute
  6. DengAI —数据预处理
  7. Express入门 模板引擎hbs 服务端渲染
  8. c语言 正整数 几位 逆序,c语言经典例题:正整数求位数and求顺、逆序位数
  9. 使用history.back()出现警告: 网页已过期的解决办法
  10. 学习C++项目——select模型,poll模型和epoll模型
  11. 套料软件XSuperNEST
  12. 大学英语四新视野 课后习题+答案翻译 Unit1~Unit8
  13. 软件分享,PicPick中文版 v5.1.4 滚动截图
  14. module java.base does not “opens java.lang“ to module spring.core
  15. 高数曲线,心形线,摆线,等曲线大全
  16. 北大核心2020_2020年北大核心论文如何发表
  17. Using R Language to Plot Diagrams
  18. .NET OCX开发
  19. 《大长今》人物系列:长今心理第二课——申主簿
  20. display:table-cell布局

热门文章

  1. 如何通过BBED找回删除数据
  2. 阿里巴巴右侧6滑块VS雅虎右侧6滑块VS自定义6滑块
  3. Oracle触发器和MySQL触发器之间的区别
  4. 快学Scala习题解答—第三章 数组相关操作
  5. mkdir-yum-tree命令应用案例
  6. Linux安装MySQL的两种方法
  7. python闰年多一天_记一个 python datetime 闰年问题
  8. 极光推送指定用户推送_干货|SpringBoot集成极光推送完整实现代码(建议收藏)...
  9. 郁金香汇编代码注入怎么写看雪_世界黑客编程大赛冠军的汇编代码 你见过吗?...
  10. c语言 个位,如何才能给C语言增加几个位操作函数