ASP.NET MVC動態二級域名及DNS(泛解析配置)


動態二級域名的實現:

應用場景:目前產品要實現SaaS功能,因為工作需要實現二級域名:www.{CompanyUrl}.xxx.com

假設產品主域名入口為:www.xxx.com

當a公司租戶登錄時:www.a.xxx.com

當b公司租戶登錄時: www.b.xxx.com

首先想到的是對Url的重寫:(網上有關於UrlRewrite的實現。在ASP.NET中這也是常用的手法。)

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 路由的代碼。

默認配置:

View Code
 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         }
12         protected void Application_Start()
13         {
14             AreaRegistration.RegisterAllAreas();
15 
16             RegisterGlobalFilters(GlobalFilters.Filters);
17             RegisterRoutes(RouteTable.Routes);
18         }

 

涉及類參考

說明
Route 表示 Web 窗體或 MVC 應用程序中的路由。
RouteBase 用作表示 ASP.NET 路由的所有類的基類。
RouteTable 存儲應用程序的路由。
RouteData 包含所請求路由的值。
RequestContext 包含有關對應於路由的 HTTP 請求的信息。
RouteValueDictionary 提供用於存儲路由 ConstraintsDefaultsDataTokens 對象的方法。
VirtualPathData 提供用於從路由信息生成 URL 的方法。

因為目前采用的是ASP.NET MVC 3進而可以利用擴展Route的方式實現。

首先定義DomainData、DomainRoute類

代碼如下:

DomainRoute類:

View Code
  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                     }
 70                 }
 71 
 72                 // 匹配域名路由
 73                 for (int i = 1; i < domainMatch.Groups.Count; i++)
 74                 {
 75                     Group group = domainMatch.Groups[i];
 76                     if (group.Success)
 77                     {
 78                         string key = domainRegex.GroupNameFromNumber(i);
 79 
 80                         if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
 81                         {
 82                             if (!string.IsNullOrEmpty(group.Value))
 83                             {
 84                                 data.Values[key] = group.Value;
 85                             }
 86                         }
 87                     }
 88                 }
 89 
 90                 // 匹配域名路徑
 91                 for (int i = 1; i < pathMatch.Groups.Count; i++)
 92                 {
 93                     Group group = pathMatch.Groups[i];
 94                     if (group.Success)
 95                     {
 96                         string key = pathRegex.GroupNameFromNumber(i);
 97 
 98                         if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
 99                         {
100                             if (!string.IsNullOrEmpty(group.Value))
101                             {
102                                 data.Values[key] = group.Value;
103                             }
104                         }
105                     }
106                 }
107             }
108 
109             return data;
110         }
111 
112         public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
113         {
114             return base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
115         }
116 
117         public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values)
118         {
119             // 獲得主機名
120             string hostname = Domain;
121             foreach (KeyValuePair<string, object> pair in values)
122             {
123                 hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString());
124             }
125 
126             // Return 域名數據
127             return new DomainData
128             {
129                 Protocol = "http",
130                 HostName = hostname,
131                 Fragment = ""
132             };
133         }
134 
135         private Regex CreateRegex(string source)
136         {
137             // 替換
138             source = source.Replace("/", @"\/?");
139             source = source.Replace(".", @"\.?");
140             source = source.Replace("-", @"\-?");
141             source = source.Replace("{", @"(?<");
142             source = source.Replace("}", @">([a-zA-Z0-9_]*))");
143 
144             return new Regex("^" + source + "$");
145         }
146 
147         private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
148         {
149             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_]*})*-?\.?\/?");
150             Match tokenMatch = tokenRegex.Match(Domain);
151             for (int i = 0; i < tokenMatch.Groups.Count; i++)
152             {
153                 Group group = tokenMatch.Groups[i];
154                 if (group.Success)
155                 {
156                     string key = group.Value.Replace("{", "").Replace("}", "");
157                     if (values.ContainsKey(key))
158                         values.Remove(key);
159                 }
160             }
161 
162             return values;
163         }
164     }
165     public class DomainData
166     {
167         public string Protocol { get; set; }
168         public string HostName { get; set; }
169         public string Fragment { get; set; }
170     }

DomainData 類:

View Code
1 public class DomainData
2     {
3         public string Protocol { get; set; }
4         public string HostName { get; set; }
5         public string Fragment { get; set; }
6     }

 

然后在Global中配置路由

   1:  routes.Add("DomainRoute", new DomainRoute( 
   2:  "www.{companyUrl}.wenthink.com",     // Domain with parameters 
   3:  "{controller}/{action}/{id}",    // URL with parameters 
   4:  new { companyUrl= "", controller = "Home", action = "Login", id = "" }  // Parameter defaults 
   5:  ));

 

效果圖:

a 公司用戶登錄時:

QQ截圖20130410014512 QQ截圖20130410014425

b公司用戶登錄時:

QQ截圖20130410014602 QQ截圖20130410014709

當用戶嘗試錯誤時:可自定義提示頁面

QQ截圖20130410014804

基礎支持:域名支持泛解析

然后要做的是:配置DNS服務,也就是讓你的域名支持泛解析 眨眼 (Windows Server 才會有,其他的Windows系統可以嘗試修改Host文件,便於測試)

Step By Step

1 2
3 4
5 6
7 8
9 10
反向查詢區域11 創建反向區域12
13 14
綁定指針  
15 16

完成上面的操作,基本可以實現DNS的泛解析了.當然如果沒有綁定域名的話,只能修改Host文件來進行操作;

本機測試的情況下需要把Host文件中添加當前IP地址 所映射的域名,如本文中的wenthink.com

備注:以上解決了域名訪問的問題,但是會存在Session跨域訪問的丟失的現象.本例中采用了CrossDomainCookie的方式解決.當然不是唯一的解決方案. 方法很簡單,這里就不多說明了,需要的可以自己Google一下 :-)

參考資料:http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx

            http://www.cnblogs.com/cyq1162/archive/2010/10/15/1851773.html

以上為個人學習摘要,如有錯誤,歡迎指正!!

下期:ASP.NET管道機制及IIS的工作原理淺析.

上個草圖先 :-P


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM