ASP.NET MVC 4路由用户名/操作问题

时间:2021-07-14 04:13:25

I am currently working on an asp.net mvc 4 application and I have the need for the following type of urls:

我目前正在开发一个asp.net mvc 4应用程序,我需要以下类型的URL:

Urls that need to be routed

需要路由的网址

  1. http://www.mysite.com/foo/user1 <------- {username}
  2. http://www.mysite.com/foo/user1 <------- {username}

  3. http://www.mysite.com/foo/edit
  4. http://www.mysite.com/foo/delete/1
  5. http://www.mysite.com/bar/user1 <------- {username}
  6. http://www.mysite.com/bar/user1 <------- {username}

  7. http://www.mysite.com/bar/edit
  8. http://www.mysite.com/bar/delete/1

The issue I'm having is that currently {username} gets treated as an action so to work around the problem I implemented the following routes, but this would mean that every time I want to implement a new action, or have a controller that needs {username}, I would have to update my routes:

我遇到的问题是当前{username}被视为一个动作,所以为了解决这个问题我实现了以下路由,但这意味着每次我想实现一个新动作,或者有一个需要的控制器{username},我必须更新我的路线:

Only Foo routes shown

仅显示Foo路线

routes.MapRoute("FooSomeAction", "foo/someaction", new { controller = "Food", action = "SomeAction" });            
routes.MapRoute("FooDelete", "foo/delete/{id}", new { controller = "Food", action = "Delete" });            


routes.MapRoute(
    "FooProfile",
    "foo/{username}",
    new { controller = "Foo", action = "Index", username = "" }
);


// Default route
routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

2 Questions

1) Is there any way I can achieve the above urls without hardcoding all the routes?

1)有没有办法在没有硬编码所有路由的情况下实现上述网址?

2) What is the best way to handle a situation where someone uses a username that happens to be the same name as a controller or action name?

2)处理某人使用恰好与控制器或操作名称相同的用户名的情况的最佳方法是什么?

DotnetShadow

3 个解决方案

#1


3  

You could create a custom route constraint that would check if the username exists in the possible actions for the controller. If it finds an action match, it fails and will use your default route (Edit for example). You may want to cache the list for performance reasons, but I leave that up to you.

您可以创建自定义路由约束,以检查控制器的可能操作中是否存在用户名。如果找到动作匹配,它将失败并将使用您的默认路径(例如,编辑)。出于性能原因,您可能希望缓存列表,但我将此归结为您。

    private static List<Type> GetSubClasses<T>()
    {
        return Assembly.GetCallingAssembly().GetTypes().Where(
            type => type.IsSubclassOf(typeof(T))).ToList();
    }

    public static  List<string> GetActionNames(string controllerName)
    {
        controllerName = controllerName + "Controller";
        var controller = GetSubClasses<Controller>().FirstOrDefault(c => c.Name == controllerName);

        var names = new List<string>();
        if (controller != null)
        {
            var methods = controller.GetMethods(BindingFlags.Public | BindingFlags.Instance);
            foreach (var info in methods)
            {
                if (info.ReturnType == typeof(ActionResult))
                {
                    names.Add(info.Name);
                }
            }

        }
        return names;
    }


    public class UsernameNotAction : IRouteConstraint
    {
        public bool Match
            (
                HttpContextBase httpContext,
                Route route,
                string parameterName,
                RouteValueDictionary values,
                RouteDirection routeDirection
            )
        {
            int i = 0;
            var username = values["username"];
            var actionList =  GetActionNames(values["controller"].ToString());

            return !actionList.Any(a => a.ToUpper() == username.ToString().ToUpper());
        }
    }


    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "FooProfile",
            "{controller}/{username}",
            new { controller = "Home", action = "Index2", username = "" },
            new { IsParameterAction = new UsernameNotAction() }
        );
        // Default route
        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

#2


2  

This isn't really the answer you're looking for, sorry.

这不是你想要的答案,对不起。

1) There's no way to route that way. There's nothing to differentiate those routes from one another, other than what you've done. I have to question why this is even necessary, I'm sure you have a good reason, but it makes no sense to me. You're still using the Index action, so why not just /foo/index/username. All I can come up with, is you have no control over the url for some reason.

1)没办法那条路。除了你所做的之外,没有什么可以区分这些路线。我不得不质疑为什么这甚至是必要的,我相信你有充分的理由,但这对我来说毫无意义。你还在使用Index操作,为什么不只是/ foo / index / username。我能想到的是,由于某种原因,你无法控制网址。

2) If you use the default route, there's no problem. With your routing, problem. Your only real option is to make your controller and action names reserved words (prevent users from being created with those usernames in the database).

2)如果您使用默认路由,则没有问题。随着您的路由,问题。您唯一真正的选择是使您的控制器和操作名称成为保留字(阻止在数据库中使用这些用户名创建用户)。

Sorry I couldn't really help you.

对不起,我真的不能帮到你。

#3


1  

You can't do it like that unless you route every single route and that is not the best way to go.

你不能这样做,除非你路由每一条路线,这不是最好的方式。

What's so wrong in having the Action name in it?

在其中包含Action名称有什么不对?

#1


3  

You could create a custom route constraint that would check if the username exists in the possible actions for the controller. If it finds an action match, it fails and will use your default route (Edit for example). You may want to cache the list for performance reasons, but I leave that up to you.

您可以创建自定义路由约束,以检查控制器的可能操作中是否存在用户名。如果找到动作匹配,它将失败并将使用您的默认路径(例如,编辑)。出于性能原因,您可能希望缓存列表,但我将此归结为您。

    private static List<Type> GetSubClasses<T>()
    {
        return Assembly.GetCallingAssembly().GetTypes().Where(
            type => type.IsSubclassOf(typeof(T))).ToList();
    }

    public static  List<string> GetActionNames(string controllerName)
    {
        controllerName = controllerName + "Controller";
        var controller = GetSubClasses<Controller>().FirstOrDefault(c => c.Name == controllerName);

        var names = new List<string>();
        if (controller != null)
        {
            var methods = controller.GetMethods(BindingFlags.Public | BindingFlags.Instance);
            foreach (var info in methods)
            {
                if (info.ReturnType == typeof(ActionResult))
                {
                    names.Add(info.Name);
                }
            }

        }
        return names;
    }


    public class UsernameNotAction : IRouteConstraint
    {
        public bool Match
            (
                HttpContextBase httpContext,
                Route route,
                string parameterName,
                RouteValueDictionary values,
                RouteDirection routeDirection
            )
        {
            int i = 0;
            var username = values["username"];
            var actionList =  GetActionNames(values["controller"].ToString());

            return !actionList.Any(a => a.ToUpper() == username.ToString().ToUpper());
        }
    }


    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "FooProfile",
            "{controller}/{username}",
            new { controller = "Home", action = "Index2", username = "" },
            new { IsParameterAction = new UsernameNotAction() }
        );
        // Default route
        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

#2


2  

This isn't really the answer you're looking for, sorry.

这不是你想要的答案,对不起。

1) There's no way to route that way. There's nothing to differentiate those routes from one another, other than what you've done. I have to question why this is even necessary, I'm sure you have a good reason, but it makes no sense to me. You're still using the Index action, so why not just /foo/index/username. All I can come up with, is you have no control over the url for some reason.

1)没办法那条路。除了你所做的之外,没有什么可以区分这些路线。我不得不质疑为什么这甚至是必要的,我相信你有充分的理由,但这对我来说毫无意义。你还在使用Index操作,为什么不只是/ foo / index / username。我能想到的是,由于某种原因,你无法控制网址。

2) If you use the default route, there's no problem. With your routing, problem. Your only real option is to make your controller and action names reserved words (prevent users from being created with those usernames in the database).

2)如果您使用默认路由,则没有问题。随着您的路由,问题。您唯一真正的选择是使您的控制器和操作名称成为保留字(阻止在数据库中使用这些用户名创建用户)。

Sorry I couldn't really help you.

对不起,我真的不能帮到你。

#3


1  

You can't do it like that unless you route every single route and that is not the best way to go.

你不能这样做,除非你路由每一条路线,这不是最好的方式。

What's so wrong in having the Action name in it?

在其中包含Action名称有什么不对?