LA.NET [EN]

Jun 09

The MVC routing assembly – part III

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

In the last post, we’ve talked about several of the properties exposed by the Route class. Unfortunately, the post grew beyond my initial expectations, which meant that the writing time was exceeded before completing the analysis of this class.

Today we’ll cover its last important feature: constraints. When a route is associated with a pattern url, you can also set up several constraints and apply them to any of the parameters defined on those urls. The constraints follow the same approach as the one used by the Defaults property: you need to pass a dictionary where each constraint is identified by the parameter name and its value (ie, the constraint) is defined through an object.

Currently, you have two options: a constraint can be defined through a regular express or through an object that implements the IRouteConstraint. Before presenting examples of how you can define constraints, it’s important to know when they’ll be evaluated.

In the latest release, constraints are always evaluated when you call the GetRouteData and the GetVirtualPath methods defined by the Route class. Internally, the class will always start by trying to convert an existing constraint to an element of type IRouteConstraint. When that conversion is possible, the Route class ends up calling the Match method (which is the only method introduced by the URouteConstraint interface) over that instance; when that conversion isn’t possible, the Route class tries to convert the dictionary entry to a string and then uses a regular expression to see if the parameter is valid.

Lets run some tests to see how this works. We’ll start with the regular expression approach…Let’s suppose that we have the following pattern:

forms/{language}-{locale}/somepage

And we want to make sure that the parameters language and locale have exactly 2 chars each. With constraints,you can achieve this by using the following route:

var route =

new Route("forms/{language}-{locale}/somepage",
                                 new RouteValueDictionary(), //defaults
                                 new RouteValueDictionary(new { language = "\w{2}", locale = "\w{2}" }), //constraints
                                 new MvcRouteHandler() );

With the previous rule, you’ll get a not found exception if any of the language or locale parameters don’t match the predefined rules (which in this case, means you’ll need two chars – ok, numbers shouldn’t be accepted but this is only demo code). Notice that you don’t need to set use the ^and $ to delimit the regular expression: those chars are automatically appended to the expression before it is applied to the parameter.

If you need more control than the one you get from regular expressions, then you can implement the IRouteConstraint interface. Currently, this interface has the following members:

public interface IRouteConstraint
{
    bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
}

The Match method will be called to perform the validation. When that happens, it receives a reference to the current context, a reference to the current route, the name of the parameter, a dictionary  with a list of parameters and its current values and a value from the enumeration RouteDirection which indicates the current action.

When this last parameter has its value set to RouteDirection.IncomingRequest, it means that the validation is being called from the Route.GetRouteData instance method. On the other hand, when it’s set to RouteDirection.UrlGeneration means that validation is being started from the Route.GetVirtualPath instance method (in practice this means that you can validate your method by using different strategies depending on the context of the call).

In the current release, the routing assembly has only a predefined route constraint called HttpMethodConstraint. Its main objective is to let you define the type of HTTP calls that are allowed. For instance, the following code will only allow POST HTTP calls:

new Route("forms/{language}-{locale}/somepage",
                                 new RouteValueDictionary(), //defaults
                                 new RouteValueDictionary(new { httpMethod= new HttpMethodConstraint( “POST”) }), //constraints
                                 new MvcRouteHandler() );

Notice the parameter name…in this case, you’re not really interested in applying a constraint to one of the url parameters. So, it’s safe to call it httpMethod, even though there really isn’t any parameter called with that name. Btw, it’s also important to mention that the constructor of this class accepts a variable number of parameters (we’re talking about a params parameter here!). So, nothing prevents you from passing several types of HTTP methods to its constructor.

And that’s it. On the next post we’ll talk about the routing table. Keep tuned.