Wednesday 1 October 2008

ASP.NET MVC Routing and Parameters

As we've chosen now to go forth with ASP.NET MVC (which makes me happy to no end) I thought I'd do a little digging into the code and got caught by a hole in my own knowledge, Routing! If I had no other suggestion to you it would be that if you want to make an ASP.NET MVC application then you should understand the routing components very well as it will save you some time and effort for later days. First up, the global.asax file holds the routing information, or more correctly the global.asax.cs file. The default mapping has this code associated with it:

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

This means that if you're using the URL then they should look something like http://domain.com.au/Home/Index/1. This isn't a problem, but what I failed to understand is the naming is important. If I have a function in my own controller:

public ActionResult MyAction(int myParam)
{
  return View();
}

This will fail at runtime. Not only that, it will give me an error saying that my parameter doesn't match the type, specifically the error message would be:

The parameters dictionary does not contain a valid value of type 'System.Int32' for parameter 'firstItem'. To make a parameter optional its type should either be a reference type or a Nullable type.

Unfortunately this wasn't particularly helpful to me as I didn't really understand the routing properly. Specifically the routing required that my parameter be called "id" or it would not map to the correct object. If you're like me and you've done a few of the tutuorials on ASP.NET MVC you might have thought at this point that you were sure that you had created actions with different parameters before and not had this problem. I certainly had. As it turns out, if the variables are passed through as part of the query string then MVC is able to address the variables to their correct names. This makes sense really as the query string holds not only the values but the names of the parameters where as the URL syntax does not. So what's the solution, well it was either use the parameter name ID or implement my own routing. This was very simple in the end really, I just added this to the golbal.asax.cs file.

routes.MapRoute(
  "ViewStockNext",
  "Inventory/ViewStockNext/{firstItem}",
  new { controller = "Inventory", action = "ViewStockNext", firstItem = 1 }
);

And now for my inventory controller and my ViewStockNext action it will take a parameter for firstItem. This would also be necessary if I wanted to have multiple parameters and still use the URL instead of a form post. This may, or may not, be ase useful to know to you as it was to me. But if nothing else, if you're going to delve into ASP.NET MVC I highly recommend that you fully understanding the routing component first.

4 comments:

Armando MorĂ¡n said...

How would you post multiple, optional parameters to the controller? what kind of routing would be necessary for that?

Odd said...

You can do it two ways, you can either use form post variables or you can use the routing table. You can do optional parameters with the routing table as well.

So for a search page with an optional query and page you would have something like

RouteTable.Routes.Add(new Route
{
Url = "Search/[query]/[page]",
Defaults = new { controller="Search", action="Results", page=1 },
RouteHandler = typeof(MvcRouteHandler)
});

Unknown said...

Just wanted to say thank you for this. I was bashing my head against the wall with a URL parameter I was having a problem with.

Anonymous said...

I wish you could also map the url component to a positional parameter instead of a named parameter.

For example, I'd like the default route url to be specified using a notation like "{controller}/{action}/{0}",
mapping {0} to the first parameter of the action method, regardless of its name.

That way the default route would intuitively satisfy all single-parameter action methods without forcing them to artificially name their parameter "id".