【问题标题】:MVC Routing Constraint on Controller Names控制器名称上的 MVC 路由约束
【发布时间】:2012-05-11 13:12:49
【问题描述】:

我的路线设置如下:

        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 控制器。

如果我应该选择在我的一个控制器崩溃后为帐户命名,这让我感到很舒服,如果字符串无法找到相应的控制器,是否有办法进入下一个路由?每次修改控制器列表时,是否有某种方法可以使用反射并避免维护约束?

编辑

您知道不使用反射或至少包含对该区域的派生类型搜索的方法吗?我不喜欢在第二个路由参数与控制器名称匹配时产生两次开销的想法(传递约束,然后在构造控制器时再次搜索)。我希望有一种方法可以在构造控制器时捕获异常,然后备份并下降到下一条路线。

【问题讨论】:

    标签: c# asp.net asp.net-mvc routes asp.net-mvc-4


    【解决方案1】:

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

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

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

    更新

    由于{accountFriendlyId} 可能是一个有效的控制器操作名称,您可以做一些其他的事情:

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

    context.MapRoute(

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

    );

    【讨论】:

    • 如果没有前两条路由,Area/Controller/Action 将填充友好的 id 参数并将我发送到默认控制器和操作,Area/friendly/Controller/Action 将失败
    • 啊,抱歉,我没发现{accountFriendlyId} 是一个有效的操作名称。我会更新答案。
    • 如何解析这些字符串的约定是什么?我不知道你可以用文字内联(我可能不会特别使用它们)。
    • 它只是使用标准的正则表达式语法。
    • 不应该将new { controller = @"(Account|Profile|Maintenance)" } 移动到下一个constraints 参数,而不是defaults 的一部分吗?
    【解决方案2】:

    这是一个 URL 空间问题。你如何区分 accountFriendlyId、campaignFriendlyId 和 controller ?最简单的方法是将它们放在 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 以尝试使其工作。

    【讨论】:

    • 就过滤反射来获取该区域的控制器名称字符串而言?我应该在IController 和该区域的控制器之间粘贴一个空的IAreaController 吗?
    • @nik.shornikov 我不确定你在说什么,你想动态地构建约束吗?
    • 是的 -- Assembly.GetCallingAssembly().GetTypes().Where(type => type.IsSubclassOf(typeof(IController)))
    【解决方案3】:

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

    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<Type> GetSubClasses<T>()
        {
            return Assembly.GetCallingAssembly().GetTypes().Where(
                type => type.IsSubclassOf(typeof(T))).ToList();
        }
    
        public List<string> GetControllerNames()
        {
            List<string> controllerNames = new List<string>();
            GetSubClasses<Controller>().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

    【讨论】:

      猜你喜欢
      • 2012-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多