LA.NET [EN]

Jun 06

The MVC routing assembly – part II

Posted in ASP.NET MVC      Comments Off on The MVC routing assembly – part II

In my previous post, we’ve seen that all routes must implement the contract defined by the RouteBase class. As you might expect, the framework defines one implementation of that contract through the Route class. Currently, the Route class exposes the following properties:

public class Route : RouteBase
{
    public RouteValueDictionary Constraints { get; set; }
    public RouteValueDictionary DataTokens { get; set; }
    public RouteValueDictionary Defaults { get; set; }
    public IRouteHandler RouteHandler { get; set; }
    public string Url { get; set; }
}

Lets start with the Url property since it’s probably the most important property defined by the class. This property is used for setting up the url associated with a route. Here’s an example:

var route = new Route( “foo/bar”, new MvcRouteHandler() );

In this case, whenever you write http://yoursite/foo/bar and you’re using the url routing framework, that request will be associated with the previous route.

Even though you can use fixed urls, you’ll normally end up using patterns instead. A pattern lets you define a placeholder whose value depends on the requested url. Here’s another example that shows a simple pattern:

var route = new Route( “foo/{anything}”, new MvcRouteHandler() );

Generally, the placeholders defined in a url are called parameters. In this case, the following urls will be automatically mapped to the previous route by the UrlModule:

http://yoursite/foo/bar

http://yoursite/foo/anotherBar

In a future post we’ll discuss the route resolution mechanism in more detail. For now, just keep in mind that the previous urls match the pattern defined by the previous example route.

You’re not limited to a single parameter,ie,you can have several placeholders in a url. For instance, you can apply the following approach if you want to have localization (MSDN uses a similar strategy):

var route = new Route( “foo/{lang}-{locale}/{anything}”, new MvcRouteHandler() );

There’s even a catch all parameter. A catch all parameter starts with the * and you’re free to use any valid name (for instance, spaces aren’t allowed). Here’s an example of the its use:

var route = new Route( “foo/{*anything}”, new MvcRouteHandler() );

Again, I’m using the anything name because I’ve just copied the previous snippet. There’s really nothing forcing you to give a specific name to a parameter. The catch all parameter is a good option when, for example, you need to use the separator character for a parameter value.

The Route object keeps an internal dictionary that associates each parameter to a value that is passed in the url associated with the current request (we’ll see how this works in a future post).

If you’re using the MVC framework, your routes need to have 2 known parameters called controller and action. How do you define these parameters? Simple: just define an url pattern with parameters with those names. Here’s an example:

var route = new Route( “{controller}/{action}”, new MvcRouteHandler() );

Ok, now if you  use the following url:

http://yoursite/products/showproducts

You’ll end up setting the controller parameter to products and the action to show products. Ok, this works well in some scenarios…but what about those scenarios where you have a fixed url and you want to associate it to a controller? And when you want to have a default action and/or a default controller? Simple: in these cases you can set up the Defaults property of the route. Nothing better than a quick example:

var route = new Route( “myApp/{action}”,
                                      new RouteValueDictionary( new { controller = “Products”, action = “showproducts” } ),
                                      new MvcRouterHandler() );

This is how you set up the Defaults property. The easiest way to initialize it is to add an anonymous object which has properties with the same name as the parameters defined on the url. Since RouteValueDictionary is a string/object dictionary, you can also use the traditional Dictionary<T,K> API to add defaults to this dictionary.

Keep in mind that the values defined on the dictionary will only be used when the parameter value isn’t set up on the current url. In the previous example, controller will always be set to “Products” because the url pattern used doesn’t contain a parameter with the name controller.

When you have a route with parameters on the url, you might need to get the current value of one of those parameters. To do that, you’ll use the GetRequiredString method of the RouteData class. So, in the previous example, if you get a reference to the RouteData, you’ll be able to get the value of the controller parameter by running the following code:

var routeDataRef = get_reference_to_current_route_data;
var controller = routeDataRef.GetRequiredString( “controller” );

The DataTokens property is an “extra”. You can use it to add items to a dictionary (RouteValueDictionary, if you want to be specific about it) and those items will always be available whenever you get a reference to that route. Btw, you can also get a reference to this dictionary through the RouteData.DataTokens property (keep in mind that this is an instance property, not a static property).

This is becoming a really big post, so we’ll leave the Constraints property to another post.