Fork me on GitHub
ASP.NET MVC动态二级域名及ASP.NET管道机制

ASP.NET MVC动态二级域名及ASP.NET管道机制

动态二级域名的实现:

应用场景:目前产品要实现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 提供用于存储路由 ConstraintsDefaults 和 DataTokens 对象的方法。
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

 

我们每个人都是梦想家,当离开家了,就只剩梦想了。。。 道路,或许坎坷,但你终会到达!!!
 
posted on 2013-04-10 14:26  HackerVirus  阅读(421)  评论(0编辑  收藏  举报