ASP.NET MVC routing conflict - null value for input variable

nathanchere picture nathanchere · Feb 9, 2010 · Viewed 8.6k times · Source

I'm at a loss as to why my routes are conflicting. I have these in my Global.asax file:

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

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

So far everything has worked fine except when I created a controller action like so:

    public ActionResult MyAction(int id)
    {
        //Do stuff here
        return View();
    }

When I try viewing it through http://mydomain/MyController/MyAction/5 I get:

Server Error in '/' Application.

The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Track(Int32)' in 'InTouch.Controllers.OrderController'. To make a parameter optional its type should be either a reference type or a Nullable type. Parameter name: parameters

suggesting to me that the id value is not being read properly. Sure enoguh, when I swap the order of the routes around it works fine. My (admittedly limited) understanding so far was that, if a variable name specified in a route matches that specified in a controller action definition, it will assume that one regardless of order. Apparently I was wrong. Swapping the order causes other controller actions to break. What is the right way to handle my routes in this instance?

Answer

Michael Paladino picture Michael Paladino · Feb 9, 2010

The problem with your example is that the match is happening on the first route and it's seeing "5" as the username parameter. You can use constraints to limit what values are accepted for each parameter to accomplish what you're wanting. Since the "Default" route that accepts an Id is more restrictive than the "CustomerView" route, I would list the "Default" route first with a constraint on the Id parameter:

routes.MapRoute(
    "Default", "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "0" },
    new { id = @"\d+" } 
    );

This will cause the first route to only match if Id is an integer value. All other requests would then fall through to the "CustomerView" route that would pick up any other requests that didn't have integers as that third parameter.

Check out Creating a Route Constraint for an explanation of constraints.