Defining Routes using Regular Expressions in ASP.NET MVC

An entry about asp.net mvc Publication date 11. March 2008 20:25

Having played about with the recently released beta 2 of the ASP.NET MVC framework, I was kind of disappointed by the still fairly restrictive routing abilities of the framework. Currently, setting up a route for a request is done using a tokenized string, allowing each token to map to a controller, action and parameters on the action. This works fine in most situations, but can be frustratingly restrictive in others.

In a post detailing the routing system of the framework, Scott Guthrie also answered a comment which asked why they weren't using regular expressions for defining routes by claiming that only a subset of people really know how to use them. He did however allow for the possibility that it might be added as an option later - but why wait when we can implement it ourselves? Here's my quick and dirty RegexRoute class:

public class RegexRoute : Route
{
    private readonly Regex _urlRegex;
 
    public RegexRoute(string urlPattern, IRouteHandler routeHandler)
        : this(urlPattern, null, routeHandler)
    {}
 
    public RegexRoute(string urlPattern, RouteValueDictionary defaults, IRouteHandler routeHandler)
        : base(null, defaults, routeHandler)
    {
        _urlRegex = new Regex(urlPattern, RegexOptions.Compiled);
    }
 
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        string requestUrl = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
 
        Match match = _urlRegex.Match(requestUrl);
 
        RouteData data = null;
 
        if(match.Success)
        {
            data = new RouteData(this, this.RouteHandler);
 
            // add defaults first
            if (null != this.Defaults)
            {
                foreach (KeyValuePair<string, object> def in this.Defaults)
                {
                    data.Values[def.Key] = def.Value;
                }
            }
 
            // iterate matching groups
            for (int i = 1; i < match.Groups.Count; i++)
            {
                Group group = match.Groups[i];
 
                if (group.Success)
                {
                    string key = _urlRegex.GroupNameFromNumber(i);
 
                    if (!String.IsNullOrEmpty(key) && !Char.IsNumber(key, 0)) // only consider named groups
                    {
                        data.Values[key] = group.Value;
                    }
                }
            }
        }
 
        return data;
    }
}

 

Incidentally, a great "side effect" of having the route defined as a regular expression is that it allows us to perform validation in-place, which kind of renders the concept of Constraints unnecessary.

Here's a quick example route defined as a regular expression:

routes.Add(new RegexRoute(@"^Books/((?<ssn>[\d]{3}(-?)[\d]{2}\1[\d]{4})|(?<query>.+)?)$", new MvcRouteHandler())
   {
       Defaults = new RouteValueDictionary(new { controller = "Book", action = "Find" })
   });

 

Now the route above could, and possibly should, have been split into two different routes; one for finding books by SSN and a different one for searching book titles, but I wanted to demonstrate the flexibility gained by using regular expressions. With the above route, urls like "mysite.com/Books/Last+Argument+Of+Kings" and "mysite.com/Books/0575077905" both map to the Find action on the BookController class, with the appropriate parameter initialized:

public class BookController : Controller
{
    public void Find(string query, int? ssn)
    {
        // ... gets the book by ssn if present, otherwise searches using the query
    }
}

 

Even with regular expression support, I'm still not 100% happy with the routing; it won't allow me to overload controller actions. In the above example, for instance, I would much rather have two separate Find(string query) and Find(int ssn) methods, and then have the correct one invoked depending on whether the query or SSN parameter was present in the route - but the MvcRouteHandler doesn't know how to resolve that and just throws an exception. Look out for another post investigating the implementation of a custom route handler for working around that, soon :)

Update (28th of March 2008): Check out this post for a discussion on the problem with outbound URLs and regular expression routes.

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

NDC 2010

The conference to attend this summer happens June 16th-18th in Oslo, Norway. Are you going? Be sure to catch my talk on AOP while you're there!

 

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS