16

How can I tell if a string matches a particular named route?

I have a route like this:

routes.MapRoute(
    "FindYourNewRental",
    "find-your-new-rental/{market}/{community}.html",
    new { controller = "FindYourNewRental", action = "Community" }
    );

string url = "http://www.website.com/find-your-new-rental/northerncalifornia/sacramento.html"

How can I programmatically tell if the 'url' string matches that route? Something like this:

// matches url with the named route "FindYourNewRental"
if (IsRouteMatch(url, "FindYourNewRental")) 
{
    // do something
}

public bool IsRouteMatch(string url, string routeName)
{
     // How do I code this function
}
Scott
  • 13,735
  • 20
  • 94
  • 152
  • where do you intend to use this method? in a controller, filter or ...? – frennky Jan 20 '11 at 14:48
  • In a controller. I need to determine whether the route matches one of three particular routes and for each of those three I need to take a separate action. – Scott Jan 20 '11 at 14:51
  • i don't quite understand why would you do that? why don't you have three separate actions instead? – frennky Jan 20 '11 at 15:28
  • You're misunderstanding my use of the word action. I'm not firing off different MVC action methods. I just need to execute different logic within the same action method based on the referring URL. – Scott Jan 20 '11 at 15:34
  • you can access url within controller with this.Request.Url and than analyze it. is that what you need? – frennky Jan 20 '11 at 16:13
  • I know I can parse the url with regular expressions, etc. I'm trying to stick with MVC principles and compare my url against the collection of routes to see if it matches a specific route. – Scott Jan 20 '11 at 16:22

1 Answers1

15

I solved this by adding a custom RouteInfo class which creates a new HttpContext with the supplied url and application path and uses that to obtain an instance of RouteData based on the new HttpContext object. I can then evaluate the Controller and Action values to see which route was matched. I have this wired up to an extension method on the Uri class. It feels a bit hackish and I was hoping there was a cleaner way to do this so I'll leave the question open in case someone else has a better solution.

ROUTEINFO CLASS:

public class RouteInfo
    {
        public RouteInfo(RouteData data)
        {
            RouteData = data;
        }

        public RouteInfo(Uri uri, string applicationPath)
        {
            RouteData = RouteTable.Routes.GetRouteData(new InternalHttpContext(uri, applicationPath));
        }

        public RouteData RouteData { get; private set; }

        private class InternalHttpContext : HttpContextBase
        {
            private readonly HttpRequestBase _request;

            public InternalHttpContext(Uri uri, string applicationPath) : base()
            {
                _request = new InternalRequestContext(uri, applicationPath);
            }

            public override HttpRequestBase Request { get { return _request; } }
        }

        private class InternalRequestContext : HttpRequestBase
        {
            private readonly string _appRelativePath;
            private readonly string _pathInfo;

            public InternalRequestContext(Uri uri, string applicationPath) : base()
            {
                _pathInfo = ""; //uri.Query; (this was causing problems, see comments - Stuart)

                if (String.IsNullOrEmpty(applicationPath) || !uri.AbsolutePath.StartsWith(applicationPath, StringComparison.OrdinalIgnoreCase))
                    _appRelativePath = uri.AbsolutePath;
                else
                    _appRelativePath = uri.AbsolutePath.Substring(applicationPath.Length);
            }

            public override string AppRelativeCurrentExecutionFilePath { get { return String.Concat("~", _appRelativePath); } }
            public override string PathInfo { get { return _pathInfo; } }
        }
    }

URI EXTENSION METHOD:

    /// <summary>
    /// Extension methods for the Uri class
    /// </summary>
    public static class UriExtensions
    {
        /// <summary>
        /// Indicates whether the supplied url matches the specified controller and action values based on the MVC routing table defined in global.asax.
        /// </summary>
        /// <param name="uri">A Uri object containing the url to evaluate</param>
        /// <param name="controllerName">The name of the controller class to match</param>
        /// <param name="actionName">The name of the action method to match</param>
        /// <returns>True if the supplied url is mapped to the supplied controller class and action method, false otherwise.</returns>
        public static bool IsRouteMatch(this Uri uri, string controllerName, string actionName)
        {
            RouteInfo routeInfo = new RouteInfo(uri, HttpContext.Current.Request.ApplicationPath);
            return (routeInfo.RouteData.Values["controller"].ToString() == controllerName && routeInfo.RouteData.Values["action"].ToString() == actionName);
        }
    }

USAGE:

Uri url = new Uri("http://www.website.com/find-your-new-rental/northerncalifornia/sacramento.html");

if (url.IsRouteMatch("FindYourNewRental", "Community"))
{
    // do something
}

OR

if (Request.Url.IsRouteMatch("FindYourNewRental", "Community"))
    {
        // do something
    }

ADDED BONUS: Because the RouteInfo class gives me back an instance of RouteData, I can access the route parameters as well. This led to the creation of another Uri extension method:

public static string GetRouteParameterValue(this Uri uri, string paramaterName)
        {
            RouteInfo routeInfo = new RouteInfo(uri, HttpContext.Current.Request.ApplicationPath);
            return routeInfo.RouteData.Values[paramaterName] != null ? routeInfo.RouteData.Values[paramaterName].ToString() : null;
        }

Which can now be used like so:

string someValue = url.GetRouteParameterValue("ParameterName");
Jesse
  • 8,605
  • 7
  • 47
  • 57
Scott
  • 13,735
  • 20
  • 94
  • 152
  • mvccontrib has a test helper library which is meant for unit testing of routes. there is an extension method for string that creates RouteData object. since it is an open source project on codeplex i suggest you check the source code for some ideas. – frennky Jan 20 '11 at 19:16
  • That's essentially what I've done (except on the Uri object), but I've created an instance of HttpContext without having to use Rhino Mocks like the MvcContrib test helper does. – Scott Jan 20 '11 at 19:32
  • 2
    I was having some problems with query string parameters being double-encoded. I worked out this was due to the _pathInfo being set to the query string. This results in the last part of the url (in my case the action value) wrongly including the query string and thus being double encoded. I've corrected the above code to fix this. – stusherwin Feb 11 '11 at 17:11
  • I realize this is an old post but I have a similar need. Is this still the best method you found to mimic routing functionality against a URL? – Skowronek Mar 25 '14 at 17:14
  • So far, but I haven't had a need to revisit it since I posted this. – Scott Mar 25 '14 at 17:21
  • I think there's a bug: in `InternalRequestContext` if applicationPath is null it will throw `NullReferenceException` – hofnarwillie May 08 '14 at 09:38
  • @hofnarwillie You are correct - the `if` and `else` statements in `InternalRequestContext` are inverted. – Jesse Nov 19 '14 at 21:44