RegularExpression Routing Class for MVC

I am working with a ASP.NET WebForms website trying to convert it over to ASP.NET MVC 1.0 and need to make sure all the old URLs route correctly. We were using a combination of an HTTPHandler and Custom 404 redirects to give the web site some fancy URLs -- now I need to mimic that functionality with MVC in order not to lose the Google links that the site depends on for revenue. We really only care about the name part of the URI and the hint that it ends in .htm (the .htm extension is mapped to the ASP.NET ISAPI extension handler). So the route I tried to add looked like this:
            routes.Add(new Route(
                "{*path}/{name}.htm",
                new NameRouteHandler()));
However, I got this error message: A catch-all parameter can only appear as the last segment of the route URL. Which was an issue; the path which I tossed away had a lot of optional subdirectories which didn't map well to the MVC Route syntax. What I really wanted was to treat the routing syntax like a regular expression. To make this happened I created my Route class subclassed from RouteBase that took a regular expression. So now route declaration looks like this:
            routes.Add(new RegexRoute(
                new Regex(@".*/(.*)\.htm"),
                new String[] {"all", "name"},
                new NameRouteHandler()));
The second parameter isn't the defaults, it is the group name. Regular Expression have the concept of groups within a match and the parenthesizes in the regular expression tell what to group up -- in this case the name. With group the complete match is always the first group. The RegexRoute class trys to make the AbsoluteUri of the request with the regular expression if it can't it returns null. If it can it fills out the RouteData class and returns it. For all the groups it creates a value entry in RouteData that gets passed to the controller. The value name is the name listed in the second parameter. I haven’t implemented GetVirtualPath, which I will do as I learn more about MVC. It is used for the TDD testing to generate URLs and I am guessing will be the tricky part of this class to implement. Here is what the class looks like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using System.Text.RegularExpressions;

namespace WebUtility
{
    public class RegexRoute : RouteBase
    {
        public Regex Regex { get; private set; }
        public String[] Groups { get; private set; }
        private IRouteHandler RouteHandler { get; set; }

        /// <summary>
        /// Creates A Regular Expression Route
        /// </summary>
        /// <param name="regex">Regular Expression To Use Against
        /// the AbsoluteUri of the request.</param>
        /// <param name="groups">An Array Of gorup names to use 
        /// for Value namdes of the RouteData</param>
        /// <param name="routeHandler">The name of the Handler 
        /// to use for this route.</param>
        public RegexRoute(Regex regex,
            String[] groups, 
            IRouteHandler routeHandler)
        {
            Regex = regex;
            Groups = groups;
            RouteHandler = routeHandler;
        }

        public override RouteData GetRouteData(
            System.Web.HttpContextBase httpContext)
        {
            MatchCollection matchCollection = 
                Regex.Matches(httpContext.Request.Url.AbsoluteUri);

            switch (matchCollection.Count)
            {
                case 0:
                    // WWB: There Is No Match --
                    //  This Route Doesn't Handle This URI
                    return (null);
                case 1:

                    // MSDN:The GroupCollection object returned
                    //      by the Match.Groups property
                    //      always has at least one member.

                    if (matchCollection[0].Groups.Count != Groups.Length)
                        throw (new Exception(String.Format(
                            "{0} contains {1} groups when matching {2}, however " + 
                            "there are only {3} mappings.  There needs to be an " +
                            "equal number of mappings to groups, note that " +
                            "there is always one group the whole string.",
                            httpContext.Request.Url.AbsoluteUri,
                            matchCollection[0].Groups.Count,
                            Regex.ToString(),
                            Groups.Length)));

                    // WWB: FillOut The Route Data
                    RouteData routeData = new RouteData();
                    routeData.Route = this;
                    routeData.RouteHandler = RouteHandler;

                    // WWB: No Group Names, No Values Outputted.
                    if (Groups != null)
                    {
                        // WWB: Map All The groups into the values for the RouteData 
                        for (Int32 index = 0;
                            index < matchCollection[0].Groups.Count - 1;
                            index++)
                        {
                            routeData.Values.Add(Groups[index].ToString(),
                                matchCollection[0].Groups[index + 1]);
                        }
                    }

                    return (routeData);
                default:
                    throw (new Exception(
                        String.Format("There Multiple Matches For {0} on {1}," +
                        "which means that the regular expression has more " +
                        "then one non-overlapping match.", 
                        Regex.ToString(),
                        httpContext.Request.Url.AbsoluteUri)));
            }
        }

        public override VirtualPathData GetVirtualPath(
            RequestContext requestContext,
            RouteValueDictionary values)
        {
            throw new NotImplementedException();
        }
    }
}
{6230289B-5BEE-409e-932A-2F01FA407A92}

Comments

Popular posts from this blog

Yet once more into the breech (of altered programming logic)

Simple WP7 Mango App for Background Tasks, Toast, and Tiles: Code Explanation

How to convert SVG data to a Png Image file Using InkScape