A A
RSS

Tricky Asp.net MVC URL Rewriting

Thu, Apr 10, 2008

ASP.NET, ASP.NET MVC

I am working on an asp.net mvc application and I wanted to make the user’s main page (profile) be www.domain.com/username.  This is a problem because the routing engine in MVC treats the first item after the domain as a controller.  Ofcourse I could do a {*catchall} and do my own parsing but why re-invent the wheel – plus I suck at regular expression.  What I ended up doing is registering all the routes manually.  So if I wanted to go to /message/create, I would register a route like this:

routes.Add(
    new Route("Message/Create", new MvcRouteHandler())
        {
            Defaults = new RouteValueDictionary(new
                                                    {
                                                        controller = "Message",
                                                        action = "Create"
                                                    })
        }
    );

So far so good, nothing earth shattering.  So I basically did this for all my routes and the last route, I added was:

routes.Add(
    new Route("{*username}", new MvcRouteHandler())
       {
           Constraints = new RouteValueDictionary(new {username = "^(?!content/).*"}),
           Defaults = new RouteValueDictionary(new {Controller = "User", action = "View"})
       });

This basically will catch any route that has not been defined before it and will treat it as a username so /eibrahim would go to the view action on the user’s controller.  Also, note that I added a constraint to prevent the catch-all route from catching any url in the content subfolder.  Important: All your scripts, images, css files and so on have to be in the content folder otherwise they will be handled by the catch-all route and not be accessible to your application.

The signature for the view action look like this:

public void View(string username, int? page)
{

}

Now the tricky part is that you want to make sure that your users don’t signup with a username matching the controller.  So if a user decided to register with the username “message”, he will never be able to go to his profile at /message because it will be handled by the generic message route “message/{action}”.  A quick workaround is to prevent the user from registering with those names.  In my signup process I call a method to check if the selected username is valid.  The validation process loops through all the routes and makes sure the username doesn’t match any controller or action.  It works out pretty well so far.

foreach (Route route in RouteTable.Routes)
{
    if (username == route.Defaults["controller"].ToString().ToLower())
        return false;
    if (username == route.Defaults["action"].ToString().ToLower())
        return false;
}

I don’t know if this is the best solution, so if you know of a better way to do it, let me know.

Tags: , , , , ,

  • danatkinson
    Have you thought about what happens if you decide to create a controller which happens to be the same as a user's name?!

    I'm not sure this is a very future-proof way of working...
  • You are absolutely right but I have worked around that by forcing users to
    use usernames with 6 or more characters and I also keep track of a list of
    banned usernames in the config file e.g. administrator, account, etc...
    It is not perfect but it works otherwise, you will be stuck with URLs that
    look like /user/eibrahim instead of just /eibrahim
  • Marco
    @Net framework programmer,
    Read this http://chriscavanagh.wordpress.com/2008/03/11/aspnet-routing-goodbye-url-rewriting/ for routing with Web Forms
  • @Net framework programmer, I am not sure how to do that. I haven't used the routing library with non-MVC projects. Good luck and let us know what you find out.
  • @Nik, you are absolutely right which is why I force user names to be 4 characters or longer. This way, worst-case-scenario, I can create a 3 letter controller. Not ideal, but a workaround.
  • hello,

    how i can route to main page without using MVC (We are using System.Web.Routing assembly)
  • Nik
    I've done exactly this for a project. It isnt ideal in that if you have a live system, and you subsequently need to add controllers, you need to check that *existing* users havent already used that controller name.
blog comments powered by Disqus
Advertise Here
The Most Intelligent Add-In To Visual Studio Happy fan of

What I'm Doing...

Yonkly Open Source

Sign up for my newsletter




* = required field

powered by MailChimp!

megree Widget

Apparently, I am connected to Obama. Check this out...
My path to Obama

Cyber Identity