Skip to content

Instantly share code, notes, and snippets.

@rarous
Created February 1, 2011 16:10
Show Gist options
  • Select an option

  • Save rarous/806068 to your computer and use it in GitHub Desktop.

Select an option

Save rarous/806068 to your computer and use it in GitHub Desktop.
Domain routing
using System;
namespace Rarous.Web.Routing
{
public class DomainData
{
public string Protocol { get; set; }
public string HostName { get; set; }
public string Fragment { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Routing;
namespace Rarous.Web.Routing
{
public class DomainRoute : Route
{
const string Host = "host";
readonly Regex domainRegex;
readonly Regex pathRegex;
public DomainRoute(string domain, string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler)
{
Domain = domain;
domainRegex = CreateRegex(Domain);
pathRegex = CreateRegex(Url);
}
public string Domain { get; set; }
public override RouteData GetRouteData(HttpContextBase httpContext)
{
string requestDomain = GetRequestDomain(httpContext);
string requestPath = GetRequestPath(httpContext);
Match domainMatch = domainRegex.Match(requestDomain);
Match pathMatch = pathRegex.Match(requestPath);
if (!(domainMatch.Success && pathMatch.Success))
{
return null;
}
// no idea how to properly plug it into; so doing it manually :)
if (Constraints.All(c =>
!domainMatch.Groups[c.Key].Success || Regex.IsMatch(domainMatch.Groups[c.Key].Value, c.Value.ToString()) &&
!pathMatch.Groups[c.Key].Success || Regex.IsMatch(pathMatch.Groups[c.Key].Value, c.Value.ToString())))
{
return CreateRouteData(domainMatch, pathMatch);
}
return null;
}
static string GetRequestDomain(HttpContextBase httpContext)
{
HttpRequestBase request = httpContext.Request;
string requestDomain = request.Headers[Host];
if (string.IsNullOrEmpty(requestDomain))
{
return request.Url.Host;
}
int portPart = requestDomain.IndexOf(":");
if (portPart > 0)
{
return requestDomain.Substring(0, portPart);
}
return requestDomain;
}
static string GetRequestPath(HttpContextBase httpContext)
{
HttpRequestBase request = httpContext.Request;
return request.AppRelativeCurrentExecutionFilePath.Substring(2) + request.PathInfo;
}
RouteData CreateRouteData(Match domainMatch, Match pathMatch)
{
var routeData = PrepareDefaultRouteData();
var domainData = ParseRouteData(domainRegex, domainMatch);
var pathData = ParseRouteData(pathRegex, pathMatch);
domainData.
Concat(pathData).ToList().
ForEach(SetRouteData(routeData));
return routeData;
}
RouteData PrepareDefaultRouteData()
{
var data = new RouteData(this, RouteHandler);
AddDefaultsFirst(data);
return data;
}
void AddDefaultsFirst(RouteData data)
{
if (Defaults == null)
{
return;
}
foreach (var item in Defaults)
{
data.Values[item.Key] = item.Value;
}
}
static IEnumerable<KeyValuePair<string, string>> ParseRouteData(Regex regex, Match match)
{
var groups = match.Groups.Cast<Group>();
var groupNames = regex.GetGroupNames();
var matchingPairs =
from item in groups.
Zip(groupNames, (grp, name) => new
{
Pair = CreatePair(name, grp.Value),
IsMatch = grp.Success,
})
where item.IsMatch && IsValidKeyAndValue(item.Pair)
select item.Pair;
return matchingPairs;
}
static KeyValuePair<string, string> CreatePair(string key, string value)
{
return new KeyValuePair<string, string>(key, value);
}
static bool IsValidKeyAndValue(KeyValuePair<string, string> pair)
{
return IsNamedKey(pair) && IsNotEmptyValue(pair);
}
static bool IsNamedKey(KeyValuePair<string, string> pair)
{
return !(string.IsNullOrEmpty(pair.Key) || char.IsNumber(pair.Key, 0));
}
static bool IsNotEmptyValue(KeyValuePair<string, string> pair)
{
return !string.IsNullOrEmpty(pair.Value);
}
static Action<KeyValuePair<string, string>> SetRouteData(RouteData data)
{
return pair => data.Values[pair.Key] = pair.Value;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
}
public DomainData GetDomainData(RouteValueDictionary values)
{
// Build hostname
string hostname = Domain;
foreach (var pair in values)
{
hostname = hostname.Replace(String.Format("{{{0}}}", pair.Key), pair.Value.ToString());
}
// Return domain data
return new DomainData
{
Protocol = "http",
HostName = hostname,
Fragment = string.Empty,
};
}
static Regex CreateRegex(string source)
{
bool isOpenedName = false;
bool isStarred = false;
var builder = new StringBuilder("^");
foreach (char ch in source)
{
switch (ch)
{
case '/':
builder.Append(@"\/?");
break;
case '.':
builder.Append(@"\.?");
break;
case '-':
builder.Append(@"\-?");
break;
case '{':
builder.Append(@"(?<");
isOpenedName = true;
break;
case '*':
if (isOpenedName) {
isStarred = true;
isOpenedName = false;
}
else {
builder.Append(ch);
}
break;
case '}':
if(isStarred) {
builder.Append(@">([a-zA-Z0-9_\/]*))");
isStarred = false;
}
else {
builder.Append(@">([a-zA-Z0-9_]*))");
isOpenedName = false;
}
break;
default:
builder.Append(ch);
break;
}
}
builder.Append("$");
return new Regex(builder.ToString());
}
RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
{
Regex tokenRegex = new Regex(@"({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?");
Match tokenMatch = tokenRegex.Match(Domain);
GroupCollection groups = tokenMatch.Groups;
for (int i = 0; i < groups.Count; i++)
{
Group group = groups[i];
if (!group.Success)
continue;
string key = group.Value.Replace("{", string.Empty).Replace("}", string.Empty);
if (values.ContainsKey(key))
values.Remove(key);
}
return values;
}
}
}
using System;
using System.Web.Mvc;
using System.Web.Routing;
namespace Rarous.Web.Routing
{
public static class DomainRoutesExtensions
{
public static void MapDomainRoute(this RouteCollection routes, string name, string domain, string url, object defaults = null, object constraints = null)
{
MapDomainRoute(routes, name, domain, url, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints));
}
public static void MapDomainRoute(this RouteCollection routes, string name, string domain, string url, RouteValueDictionary defaults, RouteValueDictionary constraints)
{
routes.Add(name, new DomainRoute(domain, url, defaults, constraints, new MvcRouteHandler()));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment