控制器名称的MVC路由约束

我的路线设置如下:

context.MapRoute( name: "Area", url: "Area/{controller}/{action}", defaults: new { controller = "Home", action = "Dashboard" } ); context.MapRoute( name: "AccountArea", url: "Area/{accountFriendlyId}/{controller}/{action}", defaults: new { controller = "Home", action = "Dashboard", accountFriendlyId = RouteParameter.Optional } ); context.MapRoute( name: "AccountCampaignArea", url: "Area/{accountFriendlyId}/{campaignFriendlyId}/{controller}/{action}", defaults: new { controller = "Home", action = "Dashboard", accountFriendlyId = RouteParameter.Optional, campaignFriendlyId = RouteParameter.Optional } ); 

我有一个强烈的愿望,让Area/friendlyAccountName/Home带我到Dashboard()但这不起作用(404)。 我认为原因是我们去寻找一个friendlyAccountName控制器。

知道如果我应该选择在我的一个控制器之后命名一个帐户,一切都崩溃了,有没有办法在字符串无法找到相应的控制器的情况下进入下一个路径? 有没有办法在每次修改控制器列表时使用reflection并避免维护约束?

编辑

你知道一种不使用reflection的方法,或者至少包含派生类型搜索到这个区域的方法吗? 当第二个路径参数与控制器名称匹配时,我不喜欢产生两次开销的想法(传递约束然后在构造控制器时再次搜索)。 我希望有一种方法可以在构建控制器然后备份并进入下一个路径时捕获exception。

为什么你需要第一条路线? 如果{accountFriendlyId}是可选的,您应该能够省略它并获得与您的第一个注册路由相同的路由默认值。

这样它首先匹配AccountArea命名路由,这是你想要的,然后如果没有指定{accountFriendlyId}它会将该区域后面的第一个令牌视为控制器。

事实上,我觉得你应该能够完全删除前两条路线并坚持使用最后一条路线,因为前两条路线参数是可选的,默认值是相同的。

UPDATE

由于{accountFriendlyId}可能是有效的控制器操作名称,因此您可以执行以下其他操作:

  1. {accountFriendlyId}移动到路径的末尾,而不是在开头。 这遵循更自然的URL样式,即最广泛的资源,以及资源中的特定细节。
  2. 使用路线约束 。 理论上,您可以使用reflection生成正则表达式以匹配自定义约束中的控制器名称,或者您可以手动将它们写出来。 像这样的东西:

context.MapRoute(

  name: "Area", url: "Area/{controller}/{action}", defaults: new { controller = "Home", action = "Dashboard", new { controller = @"(Account|Profile|Maintenance)" } } 

);

最终,为了方便我想要的东西(这取决于让应用程序动态区分任意字符串和控制器名称),我设置了这样的路线:

 public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( name: "AccountCampaignArea", url: "Area/{accountFriendlyId}/{campaignFriendlyId}/{controller}/{action}", defaults: new { controller = "Home", action = "Dashboard", accountFriendlyId = RouteParameter.Optional, campaignFriendlyId = RouteParameter.Optional, id = UrlParameter.Optional }, constraints: new { accountFriendlyId = new ControllerNameConstraint(), campaignFriendlyId = new ControllerNameConstraint() } ); context.MapRoute( name: "AccountArea", url: "Area/{accountFriendlyId}/{controller}/{action}", defaults: new { controller = "Home", action = "Dashboard", accountFriendlyId = RouteParameter.Optional, id = UrlParameter.Optional }, constraints: new { accountFriendlyId = new ControllerNameConstraint() } ); context.MapRoute( name: "Area", url: "Area/{controller}/{action}", defaults: new { controller = "Home", action = "Dashboard" } ); } 

并设置这样的约束(约束也可以称为NotControllerNameContraint ):

 public class ControllerNameConstraint : IRouteConstraint { private static List GetSubClasses() { return Assembly.GetCallingAssembly().GetTypes().Where( type => type.IsSubclassOf(typeof(T))).ToList(); } public List GetControllerNames() { List controllerNames = new List(); GetSubClasses().ForEach( type => controllerNames.Add(type.Name)); return controllerNames; } public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { if (values.ContainsKey(parameterName)) { string stringValue = values[parameterName] as string; return !GetControllerNames().Contains(stringValue + "Controller"); } return true; } } 

致谢: https : //stackoverflow.com/a/1152735/938472

这是一个URL空间问题。 你如何区分accountFriendlyId,campaignFriendlyId和控制器? 简单的方法是将它们放在URL的不同部分,但是使用您的路由,控制器可以是第二,第三或第四段。 您必须使用约束来消除歧义,并按如下顺序排序:

 context.MapRoute(null, "Area/{controller}/{action}", new { controller = "Home", action = "Dashboard" }, new { controller = "Foo|Bar" }); context.MapRoute(null, "Area/{accountFriendlyId}/{controller}/{action}", new { controller = "Home", action = "Dashboard" }, new { controller = "Foo|Bar" }); context.MapRoute(null, "Area/{accountFriendlyId}/{campaignFriendlyId}/{controller}/{action}", new { controller = "Home", action = "Dashboard" }); 

你建议的想法,如果找不到控制器然后尝试下一个匹配的路由,它不会那样工作,一旦路由匹配它,你将不得不修改UrlRoutingModule以尝试使其工作。