控制器名称的MVC路由约束

nik*_*kov 5 c# asp.net asp.net-mvc routes asp.net-mvc-4

我的路线设置如下:

        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
                }
        );
Run Code Online (Sandbox Code Playgroud)

而且我强烈希望Area/friendlyAccountName/Home带我去,Dashboard()但这不起作用(404).我认为原因是我们去寻找一个friendlyAccountName控制器.

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

编辑

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

Bra*_*ton 7

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

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

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

UPDATE

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

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

context.MapRoute(

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

);


nik*_*kov 6

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

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"
            }
        );
    }
Run Code Online (Sandbox Code Playgroud)

并设置这样的约束(也可以调用约束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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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