Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 4 route username / action issue

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

Urls that need to be routed

  1. http://www.mysite.com/foo/user1 <------- {username}
  2. http://www.mysite.com/foo/edit
  3. http://www.mysite.com/foo/delete/1
  4. http://www.mysite.com/bar/user1 <------- {username}
  5. http://www.mysite.com/bar/edit
  6. 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:

Only Foo routes shown

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?

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?

DotnetShadow

like image 399
DotnetShadow Avatar asked Feb 28 '13 12:02

DotnetShadow


1 Answers

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 }
        );
    }
like image 123
Mark Oreta Avatar answered Oct 20 '22 13:10

Mark Oreta