8

I'm trying to make a route so I can show the username in the URL like this:

http://localhost1234/john

Here Is my routeconfig:

 routes.MapRoute(
                name: "users", // Route name
                url: "{username}", // URL with parameters
                defaults: new { controller = "Home", action = "Index", username = "" } // Parameter defaults
            );

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

Here is my HomeController:

 public ActionResult Index(string username = "Test")
 {
   return View();
  }

First of all, the URL Is not changed. When I set username = "Test" inside my route-config, the URL is not changed.

Second, I can't navigate to my other controllers. If I change the URL to http://localhost123/Welcome, nothing happens. It should redirect me to a new page.

What am I doing wrong here?

If I change the order of the routes, I can navigate to other pages, but the username Is not displayed In the URL.

I have googled and all of the answers on this subject says that I should use a route like the one above.

halfer
  • 19,824
  • 17
  • 99
  • 186
Bryan
  • 3,421
  • 8
  • 37
  • 77
  • Possible duplicate of [How to change route to username after logged in?](http://stackoverflow.com/questions/36996021/how-to-change-route-to-username-after-logged-in) – NightOwl888 May 21 '16 at 09:41

2 Answers2

11

On its own, your routing will not work because if the url was .../Product meaning that you wanted to navigate to the Index() method of ProductController, it would match your first route (and assume "Product" is the username. You need to add a route constraint to your roue definitions that returns true if the username is valid and false if not (in which case it will try the following routes to find a match).

Assuming you have a UserController with the following methods

// match http://..../Bryan
public ActionResult Index(string username)
{
    // displays the home page for a user
}

// match http://..../Bryan/Photos
public ActionResult Photos(string username)
{
    // displays a users photos
}

Then you route definitions need to be

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            name: "User",
            url: "{username}",
            defaults: new { controller = "User", action = "Index" },
            constraints: new { username = new UserNameConstraint() }
        );
        routes.MapRoute(
            name: "UserPhotos",
            url: "{username}/Photos",
            defaults: new { controller = "User", action = "Photos" },
            constraints: new { username = new UserNameConstraint() }
        );
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Test", action = "Index", id = UrlParameter.Optional }
        );
    }

    public class UserNameConstraint : IRouteConstraint
    {
        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            List<string> users = new List<string>() { "Bryan", "Stephen" };
            // Get the username from the url
            var username = values["username"].ToString().ToLower();
            // Check for a match (assumes case insensitive)
            return users.Any(x => x.ToLower() == username);
        }
    }
}

If the url is .../Bryan, it will match the User route and you will execute the Index() method in UserController (and the value of username will be "Bryan")

If the url is .../Stephen/Photos, it will match the UserPhotos route and you will execute the Photos() method in UserController (and the value of username will be "Stephen")

If the url is .../Product/Details/4, then the route constraint will return false for the first 2 route definitions and you will execute the Details() method of ProductController

If the url is .../Peter or .../Peter/Photos and there is no user with username = "Peter" then it will return 404 Not Found

Note that the the sample code above hard codes the users, but in reality you will call a service that returns a collection containing the valid user names. To avoid hitting the database each request, you should consider using MemoryCache to cache the collection. The code would first check if it exists, and if not populate it, then check if the collection contains the username. You would also need to ensure that the cache was invalidated if a new user was added.

  • If I could upvote this twice I would. I keep forgetting about the constraint and feel foolish when ever I realize that. My mind immediately jumps to the crazy hack I had to do ages ago, which when ever I look back at it in hind sight is being over thought. – Nkosi Jun 13 '18 at 22:58
  • Exactly what I needed and gave good advice on future use, even the MemoryCache part, well done! – Zer0 Aug 11 '20 at 13:48
1

You need to categorize the url for different section of your website so that url pattern matching mechanism go smooth. For example in your case put a category 'profile' or anything other. Now your request url look like http://localhost1234/profile/john and route will be

 routes.MapRoute(
                name: "users", // Route name
                url: "Profile/{username}", // URL with parameters
                defaults: new { controller = "Home", action = "Index" } // Parameter defaults
            );

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

For more information follow link Routing in MVC

Rahul Maurya
  • 187
  • 5
  • And If I don't want to categorize? Have a look at facebook. When you are visiting your profile, the url are http://facebook/yourusername – Bryan May 21 '16 at 03:37
  • @Bryan , Actually in above example I preserve this for other static pages like aboutus, constactus. You can do this by removing Profile/ in the route as url: "{username}". In this case any request url in the form domain/abc will search same action for username="abc" – Rahul Maurya May 21 '16 at 03:55