前段時間,一個朋友問我ASP.NET MVC下實現動態二級域名的問題。跟他聊了一些解決方案,這裏也總結一下,以供參考。html
相信你們都發現相似58同城這樣的網站,成都的網址是cd.58.com 上海的是sh.58.com相似的上千個網站,其實沒有那麼多個網站,域名前面那部分就是泛域名解析,至關因而傳遞一個參數,全部的域名實際上訪問的都是一個網站,僅僅是傳遞了不同的參數顯示不同的內容。服務器
好比網站主域名入口爲:www.58.comdom
當成都的用戶登陸時,解析到:cd.58.comide
當上海的用戶登陸時,則解析到:sh.58.com測試
首先想到的是對Url的重寫:(這在ASP.NET中也是經常使用的手法。網上有關於UrlRewrite的實現,這裏再也不重複。)網站
還有就是MVC 應用程序中的典型URL模式,這裏只討論MVC應用程序URL模式下的動態二級域名實現,測試實例下載。this
1.定義DomainData、DomainRoute類url
public class DomainRoute : Route { private Regex domainRegex; private Regex pathRegex; public string Domain { get; set; } public DomainRoute(string domain, string url, RouteValueDictionary defaults): base(url, defaults, new MvcRouteHandler()) { Domain = domain; } public DomainRoute(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler): base(url, defaults, routeHandler) { Domain = domain; } public DomainRoute(string domain, string url, object defaults): base(url, new RouteValueDictionary(defaults), new MvcRouteHandler()) { Domain = domain; } public DomainRoute(string domain, string url, object defaults, IRouteHandler routeHandler): base(url, new RouteValueDictionary(defaults), routeHandler) { Domain = domain; } public override RouteData GetRouteData(HttpContextBase httpContext) { // 構造 regex domainRegex = CreateRegex(Domain); pathRegex = CreateRegex(Url); // 請求信息 string requestDomain = httpContext.Request.Headers["host"]; if (!string.IsNullOrEmpty(requestDomain)) { if (requestDomain.IndexOf(":") > 0) { requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":")); } } else { requestDomain = httpContext.Request.Url.Host; } string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; // 匹配域名和路由 Match domainMatch = domainRegex.Match(requestDomain); Match pathMatch = pathRegex.Match(requestPath); // 路由數據 RouteData data = null; if (domainMatch.Success && pathMatch.Success) { data = new RouteData(this, RouteHandler); // 添加默認選項 if (Defaults != null) { foreach (KeyValuePair<string, object> item in Defaults) { data.Values[item.Key] = item.Value; } } // 匹配域名路由 for (int i = 1; i < domainMatch.Groups.Count; i++) { Group group = domainMatch.Groups[i]; if (group.Success) { string key = domainRegex.GroupNameFromNumber(i); if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0)) { if (!string.IsNullOrEmpty(group.Value)) { data.Values[key] = group.Value; } } } } // 匹配域名路徑 for (int i = 1; i < pathMatch.Groups.Count; i++) { Group group = pathMatch.Groups[i]; if (group.Success) { string key = pathRegex.GroupNameFromNumber(i); if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0)) { if (!string.IsNullOrEmpty(group.Value)) { data.Values[key] = group.Value; } } } } } return data; } public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { return base.GetVirtualPath(requestContext, RemoveDomainTokens(values)); } public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values) { // 得到主機名 string hostname = Domain; foreach (KeyValuePair<string, object> pair in values) { hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString()); } // Return 域名數據 return new DomainData { Protocol = "http", HostName = hostname, Fragment = "" }; } private Regex CreateRegex(string source) { // 替換 source = source.Replace("/", @"\/?"); source = source.Replace(".", @"\.?"); source = source.Replace("-", @"\-?"); source = source.Replace("{", @"(?<"); source = source.Replace("}", @">([a-zA-Z0-9_]*))"); return new Regex("^" + source + "$", RegexOptions.IgnoreCase); } private 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); for (int i = 0; i < tokenMatch.Groups.Count; i++) { Group group = tokenMatch.Groups[i]; if (group.Success) { string key = group.Value.Replace("{", "").Replace("}", ""); if (values.ContainsKey(key)) values.Remove(key); } } return values; } } public class DomainData { public string Protocol { get; set; } public string HostName { get; set; } public string Fragment { get; set; } }
2.修改RouteConfig,增長以下代碼spa
routes.Add( "DomainRoute", new DomainRoute( "{CityNameUrl}.weiz.com", "{controller}/{action}/{id}", new { CityNameUrl = "", controller = "City", action = "Index", id = "" } ));
3.增長CityController控制類code
public class CityController : Controller { public ActionResult Index() { var cityName = RouteData.Values["CityNameUrl"]; ViewBag.CityName = cityName; return View(); } }
4.發佈網站,並修改相關配置
方式一:修改host,咱們經過修改host文件,來實現對二級域名的,只能經過一個一個增長解析如:
#host文件
127.0.0.1 www.weiz.com
127.0.0.1 a.weiz.com
127.0.0.1 b.weiz.com
127.0.0.1 c.weiz.com
方式二:增長泛域名解析,配置DNS服務,也就是讓你的域名支持泛解析 (Windows Server 纔會有,其餘的Windows系統只能修改嘗試修改Host文件,便於測試) 請看個人另外一篇文章《域名泛解析設置》
5. 效果
須要注意:若是你的服務器上有多個站點,則主站不要綁定主機頭。其餘二級域名的子系統,須要綁定主機頭。