ASP.NET MVC动态二级域名及ASP.NET管道机制
ASP.NET MVC动态二级域名及ASP.NET管道机制 动态二级域名的实现:
应用场景:目前产品要实现SaaS功能,因为工作需要实现二级域名: HdhCmsTest{CompanyUrl}.xxx测试数据
假设产品主域名入口为: HdhCmsTestxxx测试数据
当a公司租户登录时: HdhCmsTesta.xxx测试数据
当b公司租户登录时: HdhCmsTestb.xxx测试数据
首先想到的是对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 模式——来自MSDNMVC 应用程序中用于路由的 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 提供用于存储路由 Constraints 、 Defaults 和 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: "HdhCmsTest{companyUrl}.wenthink测试数据" , // Domain with parameters
3: "{controller}/{action}/{id}" , // URL with parameters
4: new { companyUrl= "" , controller = "Home" , action = "Login" , id = "" } // Parameter defaults
5: ));
效果图:
a 公司用户登录时:
b公司用户登录时:
当用户尝试错误时:可自定义提示页面
基础支持:域名支持泛解析
然后要做的是:配置DNS服务,也就是让你的域名支持泛解析 (Windows Server 才会有,其他的Windows系统可以尝试修改Host文件,便于测试)
Step By Step
创建反向区域 绑定指针完成上面的操作,基本可以实现DNS的泛解析了.当然如果没有绑定域名的话,只能修改Host文件来进行操作;
本机测试的情况下需要把Host文件中添加当前IP地址 所映射的域名,如本文中的wenthink测试数据
备注:以上解决了域名访问的问题,但是会存在Session跨域访问的丢失的现象.本例中采用了CrossDomainCookie的方式解决.当然不是唯一的解决方案. 方法很简单,这里就不多说明了,需要的可以自己Google一下 :-)
参考资料: http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx
http://HdhCmsTestcnblogs测试数据/cyq1162/archive/2010/10/15/1851773.html
以上为个人学习摘要,如有错误,欢迎指正!!
下期:ASP.NET管道机制及IIS的工作原理浅析.
上个草图先 :-P
我们每个人都是梦想家,当离开家了,就只剩梦想了。。。 道路,或许坎坷,但你终会到达!!!
分类: ASP.NET , 个人随笔
标签: MVC , 二级域名 , DNS 泛解析
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于ASP.NET MVC动态二级域名及ASP.NET管道机制的详细内容...