KISS Your ASP.NET MVC Routes

A little bit of thinking and compromise can remove unnecessary complexity from the routes in an MVC application.

For example:

Morgan’s web site has authenticated users,and Morgan decides the URLs for managing users should look like /morgan/detail. Morgan adds the following code to register the route:

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

Morgan runs some tests and it looks like there is a problem. The User controller is getting requests for /home/index. Oops, that request should have gone to the Home controller. Fortunately, Morgan knows there is all sorts of whizz-bang features you can add to a route, so Morgan creates a route constraint:

public class UserRouteConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, 
                      Route route, string parameterName, 
                      RouteValueDictionary values, 
                      RouteDirection routeDirection)
    {
        var username = values["username"] as string;
        return IsCurrentUser(username);
    }

    private bool IsCurrentUser(string username)
    {
        // ... look up the user
    }
}

The constraint prevents the User route from gobbing requests to other controllers. It only allows requests to reach the User controller when the username exists in the application.

Morgan tweaks the route registration code to include the constraint:

routes.MapRoute(
    "User",
    "{username}/{action}",
    new { controller ="User", action="Index"},
    new { user = new UserRouteConstraint() }
);

Morgan runs some tests and everything works!

Well …

Everything works until a user registers with the name “home”. Morgan goes off to tweak the code again...

STOP!

As a rule of thumb I try to:

  • Never use advanced routing features like constraints
  • Always stick to a URL like {controller}/{action}/{id} or {controller}/{id}/{action}

Sometimes you have to go break those rules, but let’s look at Morgan’s case. If Morgan was happy with a URL like /user/index/morgan, then Morgan wouldn’t need to register any additional routes. The default routing entry in a new MVC application would happily invoke the index action of the User controller and pass along the username as the id parameter. Morgan could also go with /user/morgan and use a simple route entry like this:

routes.MapRoute(
    "User",
    "User/{username}/{action}",
    new { controller ="User", action="Index", username="*"}
);

No constraints required!

The KISS principle (keep it simple stupid) is a good design principle for routes. You’ll have fewer moving parts, consistent URL structures, and make the code easier to maintain.

Print | posted @ Monday, January 25, 2010 9:12 AM

Comments on this entry:

Gravatar # re: KISS Your ASP.NET MVC Routes
by Ryan Rivest at 1/26/2010 12:53 AM

I agree completely. I try to avoid adding complex routes as much as possible, because they can have a lot of unexpected side effects.
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Cyril Gupta at 1/26/2010 2:21 AM

Hello Allen,

I agree with your KISS principle about routes. I have never used constraints as yet in my MVC apps. In fact what I am doing is building generic routes with {param1}, {param2}, type of structure. So I have only 9 routes configured and I manage to run my entire website on these 9 routes without any hassles.

Cheers!
Cyril Gupta
codebix.com
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Colin Jack at 1/26/2010 8:20 AM

Definitely agree, in the vast majority of cases I think simple URIs and routes are better.
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Rick Schott at 1/26/2010 9:19 AM

MVC Routes should be easily testable, so the more go against the grain, the harder they will be to test. Totally agree with you on KISS.
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Paul at 1/26/2010 11:15 AM

I agree too, but frustratingly when working with SEO in mind, those easy to identify in code URLs might not cut it with Marketing, etc.
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Danny Douglass at 1/27/2010 10:56 AM

In the vast majority of cases I would agree that more complex routing features such as Constraints are not best practice, readable, etc. I have come across a scenario at my company where we have a custom controller type that uses the same {controller}/{action}/{id} touring as our standard ASP.NET MVC controllers. I found constraints to be useful here as it did not require me to maintain a new route for each named instance of the ASP.NET MVC controllers (could end up being quite a few unique names). Using constraints I only need two routes to solve my problem!
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Danny Douglass at 1/27/2010 10:57 AM

oops...I meant "routing" not "touring"... dyslexic much?? =]
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Jarrett at 2/1/2010 10:54 AM

Mostly, I agree, with one little exception of resource-style routes and nested routes. Rails-style routes without the extra parts are really nice! (ex: A delete request automatically goes to /Users/1/Delete, but without the 'Delete'.)

Resources: jarrettmeyer.com/...
Nested Resources: jarrettmeyer.com/...
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Koistya `Navin at 2/3/2010 6:18 PM

Automatic route registration by using controller method's attributes:

http://maproutes.codeplex.com/

Example:

[Route("product/{name}")]
[RouteDefault("name", "")]
[RouteConstraint("name", "^[0-9]+_")]
public ActionResult Product(string name)
{
return View();
}
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Scott Allen at 2/3/2010 10:26 PM

@Koistya

I personally don't like the attribute approach. I think it scatters what should be simple declarations far and wide across the code base and only increases the chances for collisions.
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by rsenna at 2/25/2010 12:57 PM

I'll have to disagree.

Consider that

1) Controllers are a reflection of software design. They are related to the internal organization of your system, and are NOT created for the sake of "pretty urls".

2) On the other side, your final URLs should be friendly. URLs are created for users, and for SEO optimization, NOT for the sake of developers.

So there is a abyss here, right? Using ASP.NET MVC (and, in fact, many other MVC frameworks, like Castle Monorail or Cake PHP) my urls, by default, are a reflection of the way my controllers and actions were created, and are not necessarily the most "friendly", thinking at the user level.

I correct this impedance through the use of routes.

So no, i do not agree that you should avoid using routes. They should be kept as simple as possible, but NOT to the cost of not-so-friendly urls (which means bad usabillity), and definitely NOT to the cost of creating controllers just for the sake of nice urls (which means bad software design).

Sorry 'bout my english; I hope I made myself clear.

  
Gravatar # re: KISS Your ASP.NET MVC Routes
by RebeccaBENJAMIN27 at 2/26/2010 1:08 PM

Set your own life time more easy get the loan and everything you require.
  
Gravatar # re: KISS Your ASP.NET MVC Routes
by Dan Atkinson at 3/5/2010 6:11 PM

Whilst I agree with what you're saying about KISS in general, you could easily get around this problem by checking whether a controller exists with that name!

Although obviously this does inhibit future functionality if you want to create a new controller that happens to share the name of a user. :)
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 5 and 5 and type the answer here: