ASP.Net MVC Route Constraints
ASP.Net MVC Route Constraints
ASP.Net MVC has a powerful Routing Engine that allows mapping between URL Routes and Controller Actions. The default route that is created with an empty ASP.Net MVC application is:
{controller}/{action}/{id}
But, according to application requirements, other routes can be used, with parameters not necessarily of type string and int, and may require certain constraints to take place.
For example, my previous post about Anatomy of an ASP.Net MVC Application can be found at: http://blogs.microsoft.co.il/blogs/bursteg/archive/2008/12/21/anatomy-of-an-asp-net-mvc-application.aspx, and has a route similar to:
/blogs/{blogName}/archive/{year}/{month}/{day}/{*permalink}
Each one of the parameters can be checked for validity of format, values and if its value exists in the DB.
Defining Routes and Default Values
If we take a look at the Global.asax file in out ASP.Net MVC application, we can see that the default route is defined using the RegisterRoutes method:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
The first parameter is the name of the route, and it is important for debugging – in order to know which route matches an input url. The second is the route URL with parameters, and the third parameter is default values for the above parameters.
Notice this short way of defining a dictionary with values. An anonymous type that has properties and values is being passed to the constructor of RouteValueDictionary class and using the following code begin transformed into a dictionary:
public class RouteValueDictionary : ...
{
public RouteValueDictionary(object values)
{
this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
object value = descriptor.GetValue(values);
this._dictionary.Add(descriptor.Name, value);
}
}
}
We can see this form of passing a dictionary of values a lot in the ASP.Net MVC code and usage.
Defining Route Constraints
The MapRoute method has an additional overload that takes the route constrains as an object, just like the defalut values above. For example, if we want to make sure that the controller is from a list of values, and the id has an email format, we can use the following constraints:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "guyb@microsoft.com" },
new
{
controller = new FromValuesListConstraint("Home", "Account"),
id = new EmailFormatConstraint()
}
);
This call assigns two constraints: a FromValuesListConstraint on the controller value, and a EmailFormatConstraint to the id value. Each of those constrains implements the interface IRouteConstraint that has a single method Match that returns true of the value matches the constraint:
public interface IRouteConstraint
{
bool Match(HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection);
}
Implementing a Custom Route Constraint
The ASP.Net URL Routing Engine ships only with a single Route Constraint called HttpMethodConstraint, that validates the Http Method against a list of valid method for an action. This means that this is a place for extensibility and the above 2 samples are custom Route Constraints I created.
public class FromValuesListConstraint : IRouteConstraint
{
public FromValuesListConstraint(params string[] values)
{
this._values = values;
}
private string[] _values;
public bool Match(HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
// Get the value called "parameterName" from the
// RouteValueDictionary called "value"
string value = values[parameterName].ToString();
// Return true is the list of allowed values contains
// this value.
return _values.Contains(value);
}
}
In order to implement a Custom Route Constraint, you should create a class that inherits from IRouteConstraint, and implement the Match method. In the implementation, get the value from the dictionary by the provided parameter name, and validate id according to your scenario. Don’t forget that you have access to the HttpContext, and you can use the Request, Response and other Http Properties.
Enjoy!