MVC动态二级域名解析
动态二级域名的实现:
应用场景:目前产品要实现block功能,因为工作需要实现二级域名:www.{CompanyUrl}.xxx.com
假设产品主域名入口为:www.xxx.com
公司员工管理:www.a.xxx.com
公司产品管理: www.b.xxx.com
Route简介:ASP.NET路由可以不用映射到网站特定文件的URL.由于该 URL 不必映射到文件,因此可以使用对用户操作进行描述因而更易于被用户理解的 URL。.NET Framework 3.5 SP1已经包含了ASP.NET Routing引擎。现在微软已经在ASP.NET WebForms 4.0中增加了对Routing引擎更好的支持,它使用表达式构造器进行双向Routing。
MVC 应用程序中的典型 URL 模式——来自MSDN
MVC 应用程序中用于路由的 URL 模式通常包括 {controller} 和 {action} 占位符。
当收到请求时,会将其路由到 UrlRoutingModule 对象,然后路由到 MvcHandler HTTP 处理程序。 MvcHandler HTTP 处理程序通过向 URL 中的控制器值添加后缀“Controller”以确定将处理请求的控制器的类型名称,来确定要调用的控制器。URL 中的操作值确定要调用的操作方法。
MVC项目中添加路由,Global.asax 文件默认的MVC 路由的代码。
默认配置:
1 public static void RegisterRoutes(RouteCollection routes) 2 { 3 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 4 5 routes.MapRoute( 6 "Default", // Route name 7 "{controller}/{action}/{id}", // URL with parameters 8 new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults 9 ); 10 11 }
1 protected void Application_Start() 2 { 3 AreaRegistration.RegisterAllAreas(); 4 5 RegisterGlobalFilters(GlobalFilters.Filters); 6 RegisterRoutes(RouteTable.Routes); 7 }
涉及类参考
类 | 说明 |
Route | 表示 Web 窗体或 MVC 应用程序中的路由。 |
RouteBase | 用作表示 ASP.NET 路由的所有类的基类。 |
RouteTable | 存储应用程序的路由。 |
RouteData | 包含所请求路由的值。 |
RequestContext | 包含有关对应于路由的 HTTP 请求的信息。 |
RouteValueDictionary | 提供用于存储路由 Constraints、Defaults 和 DataTokens 对象的方法。 |
VirtualPathData | 提供用于从路由信息生成 URL 的方法。 |
因为目前采用的是ASP.NET MVC 3进而可以利用扩展Route的方式实现。
首先定义DomainData、DomainRoute类
代码如下:
DomainRoute类:
1 public class DomainRoute : Route 2 { 3 private Regex domainRegex; 4 private Regex pathRegex; 5 6 public string Domain { get; set; } 7 8 public DomainRoute(string domain, string url, RouteValueDictionary defaults) 9 : base(url, defaults, new MvcRouteHandler()) 10 { 11 Domain = domain; 12 } 13 14 public DomainRoute(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler) 15 : base(url, defaults, routeHandler) 16 { 17 Domain = domain; 18 } 19 20 public DomainRoute(string domain, string url, object defaults) 21 : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler()) 22 { 23 Domain = domain; 24 } 25 26 public DomainRoute(string domain, string url, object defaults, IRouteHandler routeHandler) 27 : base(url, new RouteValueDictionary(defaults), routeHandler) 28 { 29 Domain = domain; 30 } 31 32 public override RouteData GetRouteData(HttpContextBase httpContext) 33 { 34 // 构造 regex 35 domainRegex = CreateRegex(Domain); 36 pathRegex = CreateRegex(Url); 37 38 // 请求信息 39 string requestDomain = httpContext.Request.Headers["host"]; 40 if (!string.IsNullOrEmpty(requestDomain)) 41 { 42 if (requestDomain.IndexOf(":") > 0) 43 { 44 requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":")); 45 } 46 } 47 else 48 { 49 requestDomain = httpContext.Request.Url.Host; 50 } 51 string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; 52 53 // 匹配域名和路由 54 Match domainMatch = domainRegex.Match(requestDomain); 55 Match pathMatch = pathRegex.Match(requestPath); 56 57 // 路由数据 58 RouteData data = null; 59 if (domainMatch.Success && pathMatch.Success) 60 { 61 data = new RouteData(this, RouteHandler); 62 63 // 添加默认选项 64 if (Defaults != null) 65 { 66 foreach (KeyValuePair<string, object> item in Defaults) 67 { 68 data.Values[item.Key] = item.Value; 69 if (item.Key.Equals("area") || item.Key.Equals("namespaces")) 70 { 71 data.DataTokens[item.Key] = item.Value; 72 } 73 } 74 } 75 76 // 匹配域名路由 77 for (int i = 1; i < domainMatch.Groups.Count; i++) 78 { 79 Group group = domainMatch.Groups[i]; 80 if (group.Success) 81 { 82 string key = domainRegex.GroupNameFromNumber(i); 83 84 if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0)) 85 { 86 if (!string.IsNullOrEmpty(group.Value)) 87 { 88 data.Values[key] = group.Value; 89 if (key.Equals("area")) 90 { 91 data.DataTokens[key] = group.Value; 92 } 93 } 94 } 95 } 96 } 97 98 // 匹配域名路径 99 for (int i = 1; i < pathMatch.Groups.Count; i++) 100 { 101 Group group = pathMatch.Groups[i]; 102 if (group.Success) 103 { 104 string key = pathRegex.GroupNameFromNumber(i); 105 106 if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0)) 107 { 108 if (!string.IsNullOrEmpty(group.Value)) 109 { 110 data.Values[key] = group.Value; 111 if (key.Equals("area")) 112 { 113 data.DataTokens[key] = group.Value; 114 } 115 } 116 } 117 } 118 } 119 } 120 121 return data; 122 } 123 124 public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 125 { 126 return base.GetVirtualPath(requestContext, RemoveDomainTokens(values)); 127 } 128 129 //public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values) 130 //{ 131 // // 获得主机名 132 // string hostname = Domain; 133 // foreach (KeyValuePair<string, object> pair in values) 134 // { 135 // hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString()); 136 // } 137 138 // // Return 域名数据 139 // return new DomainData 140 // { 141 // Protocol = "http", 142 // HostName = hostname, 143 // Fragment = "" 144 // }; 145 //} 146 147 private Regex CreateRegex(string source) 148 { 149 // 替换 150 source = source.Replace("/", @"\/?"); 151 source = source.Replace(".", @"\.?"); 152 source = source.Replace("-", @"\-?"); 153 source = source.Replace("{", @"(?<"); 154 source = source.Replace("}", @">([a-zA-Z0-9_]*))"); 155 156 return new Regex("^" + source + "$"); 157 } 158 159 private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values) 160 { 161 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_]*})*-?\.?\/?"); 162 Match tokenMatch = tokenRegex.Match(Domain); 163 for (int i = 0; i < tokenMatch.Groups.Count; i++) 164 { 165 Group group = tokenMatch.Groups[i]; 166 if (group.Success) 167 { 168 string key = group.Value.Replace("{", "").Replace("}", ""); 169 if (values.ContainsKey(key)) 170 values.Remove(key); 171 } 172 } 173 174 return values; 175 } 176 }
然后注册我的产品管理系统
并且注册区域路由规则
1 public class BlockAreaRegistration : AreaRegistration 2 { 3 public override string AreaName 4 { 5 get 6 { 7 return "Block"; 8 } 9 } 10 11 public override void RegisterArea(AreaRegistrationContext context) 12 { 13 context.Routes.Add(new DomainRoute( 14 "us.block.com", // Domain with parameters 需要配置host文件 15 "{controller}/{action}/{id}", // URL with parameters 16 new 17 { 18 area = "Block", 19 controller = "Default", 20 action = "Index", 21 id = UrlParameter.Optional, 22 namespaces = new[] { "Notify.Solution.WebMvc.Areas.Block.Controllers" } 23 })); 24 } 25 }
当用户访问www.Block.com 时候就会访问主站点
当用户访问us.Block.com 时候就会访问二级域名站点