Created
February 1, 2011 16:10
-
-
Save rarous/806068 to your computer and use it in GitHub Desktop.
Domain routing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| namespace Rarous.Web.Routing | |
| { | |
| public class DomainData | |
| { | |
| public string Protocol { get; set; } | |
| public string HostName { get; set; } | |
| public string Fragment { get; set; } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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