I had the pleasure of visiting some guys in Munich this week to talk about ASP.NET MVC. Well, the time with them was a pleasure, but I did have two awful airport experiences due to snow in 48 hours!

Something we discussed that I’ve come across before is using different routes depending upon the domain that the end user is browsing. That might be different based on language (browsing your “.co.uk” site gets English URLs, browsing “.fr” gets French), or perhaps based on brand. Maybe some functionality on your web site is only available to certain sub-brands. Or perhaps you want to direct users that visit uat.mydomain.com to new functionality so they can test it.

All of this becomes possible, and even easy, with a Route Constraint. These are simply used to validate whether a route should match a particular incoming URL or ActionLink construction.

HostConstraint

To apply this kind of filtering based on the host in the URL (i.e. the domain the user is browsing) is such a simple piece of code;

public class HostConstraint : IRouteConstraint
{
    private readonly Regex _host;
 
    public HostConstraint(string pattern)
    {
        _host = new Regex(pattern, 
                RegexOptions.Compiled | RegexOptions.IgnoreCase);
    }
 
    public bool Match(
        HttpContextBase httpContext, 
        Route route, 
        string parameterName, 
        RouteValueDictionary values, 
        RouteDirection routeDirection)
    {
        return _host.IsMatch(httpContext.Request.Url.Host);
    }
}

You can see this constraint takes a Regular Expression pattern to describe host names that it should match, and simply checks the Url.Host on the current HttpContext for success.

Using HostConstraint

Using our new class is really simple - we add it to an anonymous type in the MapRoute call;

routes.MapRoute(
    "Default_us",
    "ussitemap",
    new { controller = "Home", action = "SiteMap" },
    new { host = new HostConstraint("\\.com$") }
);
 
routes.MapRoute(
    "Default_fr",
    "frenchsitemap",
    new { controller = "Home", action = "SiteMap" },
    new { host = new HostConstraint("\\.fr$") }
);

In this example I’ve used two different routes depending upon whether the user is browsing the dot com site, or the French site.

When we construct the anonymous type;

new { host = new HostConstraint("\\.fr$") }

Specifying “host” as the member name to assign our HostConstraint to is largely irrelevant in this case, as long as you don’t have any other routing data token named host. You can, however apply regular expressions to any data tokens specified in your route here too, without any additional constraint. For example, we could limit the French site map to users browsing any French Language version of the site that is hosted on an “fr” domain;

routes.MapRoute(
    "Default_local",
    "{language}/frenchsitemap",
    new { controller = "Home", action = "SiteMap" },
    new { host = new HostConstraint("\\.fr$"), 
          language = "^fr-[a-z][a-z]$" }
);

Here, the “language” specifier in the constraints anonymous type is matched against the “language” data token in the URL pattern, so the regular expression applies to that token.

Wrap Up

Easy huh? Route Constraints add yet more functionality to routing and really open up interesting yet incredibly easy to implement options. Enjoy!