The importance of the order of routes in ASP.NET MVC

mvc-logo-landing-pageLast week I had the pleasure of listening to a presentation from Scott Guthrie here in Stockholm, http://weblogs.asp.net/scottgu/archive/2009/12/06/my-presentations-in-europe-december-2009.aspx. One of the most interesting parts of the all day event was his talk about ASP.NET MVC 2. Yesterday I had some time off and started to try things out using Visual Studio 2010 Beta 2 and the ASP.NET MVC 2 template. It was running smoothly for a while until I bumped into a problem I recognized from earlier. I didn’t remember the solution right away, but soon I figured it out what was the key point in making the routes, registered in Global.asax, work properly.

The order when adding the routes using the MapRoute method on a RouteCollection object is important to avoid getting a 404 page when browsing to pages that are supposed to work. I will present the problem and the solution for it below.

The problem

Let’s say that you want to present a list of customers, from either the Northwind database or the AdventureWorks database. This is a straight forward easy thing to accomplish by adding a controller, CustomersController, and an Index action to this. This action will return a list of customers from the database, where the customer type depends on the database connection in the project. I decided to use ADO.NET Entity Data Model, but that is not relevant for the problem stated here.

The action in the controller looks like this:

    public class CustomersController : Controller
    {

        MvcApplication3.Models.AdventureWorksLT2008R2Entities aw =
                new Models.AdventureWorksLT2008R2Entities();

        //
        // GET: /Customers/
        public ActionResult Index()
        {
            var customers = aw.Customer.ToList();
            return View(customers);
        }
    }

Of course there’s a View for presenting the list of customers using a ul-li list and the hyper link control created by using the Html.RouteLink method:

Html.RouteLink(
   item.FirstName,
   "Customer-details",
   new { Controller = "Customers", Action = "Details", customerID = item.CustomerID })

To reach this list of customers, let’s add a route to the Global.asax.cs RegisterRoutes method:

routes.MapRoute(
                "Customer-list",
                "Customers",
                new { controller = "Customers", action = "Index" });

This route will make it possible to reach the list of customers through

http://www.yourdomain.org/Customers

Browsing to this URL will trigger the CustomersController action Index shown above. The hyper link control created above using the Html.RouteLink will create a list of customers, hyper linked like this:

http://www.yourdomain.org/Customers/10

where the ’10′ is the ID of the customer clicked.

As seen above the RouteLink method will use a route called “Customer-details”, so let’s define that one in Global.asax.cs:

routes.MapRoute(
                "Customer-details",
                "Customers/{customerID}",
                new {
                    controller = "Customers",
                    action = "Details",
                    customerID = (int?)null });

This route will trigger the CustomersController action Details, but only if the route is added in the correct order. This is where the things started to go wrong for me, just like before when I bumped into the 404 page problem earlier.

Here goes the Details action in the CustomersController:

public ActionResult Details(int? customerID)
{
     var customers = from p in aw.Customer where p.CustomerID == customerID select p;
     return View(customers.FirstOrDefault());
}

The view added for this action is a standard details view in the ASP.NET MVC Framework.

Sadly this action is not triggered, but why? Here goes the faulty RegisterRoutes in the Global.asax.cs:

public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

            routes.MapRoute(
                "Customer-list",
                "Customers",
                new { controller = "Customers", action = "Index" }
            );

            routes.MapRoute(
                "Customer-details",
                "Customers/{customerID}",
                new {
                    controller = "Customers",
                    action = "Details",
                    customerID = (int?)null }
            );

        }

Some notes on the code above:

On line 8 the route added by the project template is added. On line 17 the customers list route is added and on line 23 the customer details route is added.

This RegisterRoutes method will cause a 404 page when browsing to

http://www.yourdomain.org/Customers/[customerID].

This is annoying!

The solution

The solution to this problem is really really simple. It is a one sentence description, so here goes:

Move the Default route added on line 8 in the listing above to be the last thing in the RegisterRoutes method.

That’s it! The Details action in the CustomersController will be properly trigged and the details for the selected customer will be presented. The RegisterRoutes method will then look like this, making the customer details action work:

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

            //
            //
            // Moved down (*)
            //
            // routes.MapRoute(
            //    "Default",
            //    "{controller}/{action}/{id}",
            //    new {
            //        controller = "Home",
            //        action = "Index",
            //        id = "" }
            //);
            //
            //

            routes.MapRoute(
                "Customer-list",
                "Customers",
                new { controller = "Customers", action = "Index" }
            );

            routes.MapRoute(
                "Customer-details",
                "Customers/{customerID}",
                new {
                    controller = "Customers",
                    action = "Details",
                    customerID = (int?)null }
            );

            // Moved from (*) to here
            // Route name, URL with parameters, Parameter defaults
            routes.MapRoute(
                "Default",
                "{controller}/{action}/{id}",
                new {
                    controller = "Home",
                    action = "Index",
                    id = "" }
            );
        }
Tagged with: , , , ,
Posted in .NET, ASP.NET MVC Framework, C#, Tips & tricks
6 comments on “The importance of the order of routes in ASP.NET MVC
  1. fredrik says:

    Great tutorial! Definitely a common problem out there!! :)

  2. Fredde says:

    Thanks! I realized that since I bumped into the same thing for the second time in a short period of time I just had to NOT make it a third time… ;)

  3. Adil says:

    Hi

    Thanks the info, the problem I am having is if I start using AJAX in my page, unless the Default route is at the top, the ajax does not work correctly.

    help would be appreciated.

    Thanks

  4. JTraining says:

    Interesting article! Come to JTraining for more e.g. Java blogs – Java knowledge community Java training.

  5. Rob says:

    Thanks a lot – this was driving me crazy as I couldn’t see the logical explanation for it!

  6. Learning says:

    Hi,

    I have this same very problem but in my case the default route is already setup as the last item. This same very route works under local development and results in not found for the qa site.

    Also, i have routes under area registrations and all of these works without any problem.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>