I currently work on a project that will be multi-tenant, and our routes use this template http://www.appName.com/clients/{clientName}/ and I was wondering what it would take to have the client name as a subdomain instead of being a parameter in the route template. So I fired up Visual Studio and started a quick project (throwaway of course…) using MVC 5 and WebApi 2.


Creating the route

There are many path available for this scenario, a new route, a constraints, controller inheritance,… I decided to create my own route for this since I wanted the client subdomain to be explicit in the declaration.

The basic code for this is as follow:

public class SubdomainRoute : Route
    public SubdomainRoute(string domain, string url, RouteValueDictionary defaults)
        : this(domain, url, defaults, new MvcRouteHandler())

    public SubdomainRoute(string domain, string url, object defaults)
        : this(domain, url, new RouteValueDictionary(defaults), new MvcRouteHandler())

    public SubdomainRoute(string domain, string url, object defaults, IRouteHandler routeHandler)
        : this(domain, url, new RouteValueDictionary(defaults), routeHandler)

    public SubdomainRoute(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
        : base(url, defaults, routeHandler)
        this.Domain = domain;

    public string Domain { get; set; }

    public override RouteData GetRouteData(HttpContextBase httpContext)
        var routeData = base.GetRouteData(httpContext);
        routeData.Values.Add("client", httpContext.Request.Url.Host.Split('.').First());
        return routeData;

The interesting part is in the override of the GetRouteData, we simply call the base class to get all the route data from the template, then we'll check in the Header for the Host param (or directly from the httpContext.Request.Url.Host) . From the host value we can now access the subdomain for this URL, based on this we can do some basic (or complex) logic based on that value….check to see if it's a www value, or if the client's name exists and so on. We also add it the the route data values with the key client.

Registering the routes

In order for this route to exist it must be registered as a route in the route config, so in the routeconfig.cs class I removed the default route registration and added this registration instead:

routes.Add("Subdomain", new SubdomainRoute(
                "{client}.test.local", //of course this should represent the real intent…like I said throwaway demo project in local IIS
                new { controller = "Home", action = "Index", id = UrlParameter.Optional }));

And voilà, now my route is getting picked up by the route config and I can have access to the client value, nice !!!

Oh wait it's not working..but you wrote voilà

Yeah about that… since this a demo project some tinkering is needed, first make sure it is hosted in local IIS and also make sure that your host file will be able to resolve the URL. I havent' found a way to make my host accept a wildcard so I basically just added to entries in it, like if I had two clients (I know business is booming!)    test.local    client1.test.local    client2.test.local

On the other hand if you have access to a staging deployment (hum hum Azure maybe), you could try it as long as you DNS is propertly configured.

  • Ok that's good and all but we are using the latest and greatest!!! Only route attributes for us….. This is in an upcoming post
  • What about WebApi… This is in an upcoming post