MVC项目不同域之间的UrlRouting

 一、DomainAction,方便生成不同域下的url

1、新建3个MVC项目,一个公用类库WebCore

Demo.WebApplication0

  绑定域名 www.demo.com demo.com t0.demo.com

Demo.WebApplication1(可不用建)

  绑定域名 t1.demo.com

Demo.WebApplication2(可不用建)

  绑定域名 t2.demo.com

Demo.WebCore

 

2、项目Demo.WebApplication0新增一个学生的Controller

 1     public class StudentController : Controller
 2     {
 3         public ActionResult Search(int gender, int degree, int pageSize, int page)
 4         {
 5             return Content(string.Format("查找学生,筛选条件。性别:{0},学历:{1},每页学生数量:{2},页码:{3}"
 6                 , (gender == 1 ? "" : (gender == 2 ? "" : "不限"))
 7                 ,degree, pageSize, page
 8                 ));
 9             //return View();
10         }
11 
12 
13     }

 

3、域名枚举

 1     public enum DemoDomain
 2     {
 3         /// <summary>
 4         /// www.demo.com
 5         /// demo.com
 6         /// t0.demo.com
 7         /// </summary>
 8         T0,
 9 
10         /// <summary>
11         /// t1.demo.com
12         /// </summary>
13         T1,
14 
15         /// <summary>
16         /// t2.demo.com
17         /// </summary>
18         T2,
19 
20         /// <summary>
21         /// demo.com
22         /// </summary>
23         Default,
24 
25     }

 

4、System.Web.Mvc.UrlHelper的静态扩展类DemoUrlExtensions

 1     public static class DemoUrlExtensions
 2     {
 3         public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName)
 4         {
 5             return urlHelper.DomainAction(actionName, controllerName, DemoDomain.Default);
 6         }
 7 
 8         public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues)
 9         {
10             return urlHelper.DomainAction(actionName, controllerName, routeValues, DemoDomain.Default);
11         }
12 
13         public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, DemoDomain Domain)
14         {
15             return urlHelper.DomainAction(actionName, controllerName, new { }, Domain);
16         }
17 
18         public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, DemoDomain Domain)
19         {
20             return DomainAction(urlHelper, actionName, controllerName, new RouteValueDictionary(routeValues), Domain);
21         }
22 
23         public static string DomainAction(this UrlHelper urlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, DemoDomain Domain)
24         {
25             string subDomain = string.Empty;
26             string HostName = string.Empty;
27             switch (Domain)
28             {
29                 case DemoDomain.Default:
30                     subDomain = string.Empty;
31                     break;
32                 case DemoDomain.T0:
33                     subDomain = "t0";
34                     break;
35                 case DemoDomain.T1:
36                     subDomain = "t1";
37                     break;
38                 case DemoDomain.T2:
39                     subDomain = "t2";
40                     break;
41                 default:
42                     subDomain = string.Empty;
43                     break;
44             }
45 
46             if (!string.IsNullOrEmpty(subDomain))
47             {
48                 HostName = subDomain + ".demo.com";
49             }
50             else
51             {
52                 HostName = "www.demo.com";
53             }
54             //核心的一行
55             string url = urlHelper.Action(actionName, controllerName, routeValues, "http", HostName);
56             if (url.Contains('?'))
57             {
58                 url = url.TrimEnd('/');
59             }
60             else
61             {
62                 if (url.EndsWith(".html/"))
63                 {
64                     url = url.TrimEnd('/');
65                 }
66             }
67             return url;
68         }
69 
70     }

 

5、修改Home/Index内容

1 <div>
2     <a href="@Url.DomainAction("Index","Home", DemoDomain.T1)">我要去t1.demo.com</a>
3     <a href="@Url.DomainAction("Index","Home", DemoDomain.T2)">我要去t2.demo.com</a>
4     <a href="@Url.DomainAction("Index", "Home", new { from = "www" }, DemoDomain.T2)">我要去t2.demo.com,我要带参数,表示我是从www过来的</a>
5 
6     <a href="@Url.DomainAction("Search", "Student", new { gender = 1, degree = 0, pageSize = 20, page = 1 }, DemoDomain.T0)">查找性别男的学生</a>
7 
8 </div>

对应的html源码

1 <div>
2     <a href="http://t1.demo.com/">我要去t1.demo.com</a>
3     <a href="http://t2.demo.com/">我要去t2.demo.com</a>
4     <a href="http://t2.demo.com/?from=www">我要去t2.demo.com,我要带参数,表示我是从www过来的</a>
5 
6     <a href="http://t0.demo.com/Student/Search?gender=1&amp;degree=0&amp;pageSize=20&amp;page=1">查找性别男的学生</a>
7 
8 </div>

 

二、注册路由,SEO优化

现在已经能做到

@Url.DomainAction("Search", "Student", new { gender = 1, degree = 0, pageSize = 20, page = 1 }, DemoDomain.T0)

对应的Url为

http://t0.demo.com/Student/Search?gender=1&degree=0&pageSize=20&page=1

,但这个链接不利于SEO,理想的情况可能是这样(请把t0当成student)

http://t0.demo.com/Search/1-0-20-1.html

定义路由

  1 public class DomainRoute : Route
  2     {
  3         private static 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_]*})*-?\.?\/?", RegexOptions.Compiled);
  4         private static Dictionary<string, Regex> domainRegDic = new Dictionary<string, Regex>();
  5         private static Dictionary<string, Regex> pathRegDic = new Dictionary<string, Regex>();
  6 
  7         /// <summary>
  8         /// 
  9         /// </summary>
 10         public Regex PathRegex
 11         {
 12             get
 13             {
 14                 if (!pathRegDic.Keys.Contains(Url))
 15                 {
 16                     switch (Url)
 17                     {
 18                         case WebCoreConsts.URL_T0_SearchStudent:
 19                             pathRegDic[Url] = new Regex(ConvertToPattern("search/{gender}-{degree}-{pagesize}-{page}.html"), RegexOptions.Compiled | RegexOptions.IgnoreCase);
 20                             break;
 21                         default:
 22                             pathRegDic[Url] = CreateRegex(Url);
 23                             break;
 24                     }
 25                 }
 26                 return pathRegDic[Url];
 27             }
 28         }
 29 
 30         /// <summary>
 31         /// 
 32         /// </summary>
 33         public Regex DomainRegex
 34         {
 35             get
 36             {
 37                 if (!domainRegDic.Keys.Contains(Domain))
 38                 {
 39 
 40                     domainRegDic[Domain] = CreateRegex(Domain);
 41                 }
 42                 return domainRegDic[Domain];
 43             }
 44 
 45         }
 46 
 47         public string Domain { get; set; }
 48 
 49         public DomainRoute(string domain, string url, RouteValueDictionary defaults)
 50             : base(url, defaults, new MvcRouteHandler())
 51         {
 52             Domain = domain;
 53         }
 54 
 55         public DomainRoute(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
 56             : base(url, defaults, routeHandler)
 57         {
 58             Domain = domain;
 59         }
 60 
 61         public DomainRoute(string domain, string url, object defaults)
 62             : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
 63         {
 64             Domain = domain;
 65         }
 66 
 67         //new
 68         public DomainRoute(string domain, string url, object defaults, object constraints)
 69             : base(url, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new MvcRouteHandler())
 70         {
 71             Domain = domain;
 72         }
 73 
 74         public DomainRoute(string domain, string url, object defaults, IRouteHandler routeHandler)
 75             : base(url, new RouteValueDictionary(defaults), routeHandler)
 76         {
 77             Domain = domain;
 78         }
 79 
 80         public override RouteData GetRouteData(HttpContextBase httpContext)
 81         {
 82             // 请求信息
 83             string requestDomain = httpContext.Request.Headers["host"];
 84             if (!string.IsNullOrEmpty(requestDomain))
 85             {
 86                 if (requestDomain.IndexOf(":") > 0)
 87                 {
 88                     requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":"));
 89                 }
 90             }
 91             else
 92             {
 93                 requestDomain = httpContext.Request.Url.Host;
 94             }
 95             string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
 96 
 97             // 匹配域名和路由
 98             Match domainMatch = DomainRegex.Match(requestDomain);
 99             Match pathMatch = PathRegex.Match(requestPath);
100 
101             // 路由数据
102             RouteData data = null;
103             if (domainMatch.Success && pathMatch.Success)
104             {
105                 data = new RouteData(this, RouteHandler);
106 
107                 // 添加默认选项
108                 if (Defaults != null)
109                 {
110                     foreach (KeyValuePair<string, object> item in Defaults)
111                     {
112                         data.Values[item.Key] = item.Value;
113                     }
114                 }
115 
116                 // 匹配域名路由
117                 for (int i = 1; i < domainMatch.Groups.Count; i++)
118                 {
119                     Group group = domainMatch.Groups[i];
120                     if (group.Success)
121                     {
122                         string key = DomainRegex.GroupNameFromNumber(i);
123 
124                         if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
125                         {
126                             if (!string.IsNullOrEmpty(group.Value))
127                             {
128                                 data.Values[key] = group.Value;
129                             }
130                         }
131                     }
132                 }
133 
134                 // 匹配域名路径
135                 for (int i = 1; i < pathMatch.Groups.Count; i++)
136                 {
137                     Group group = pathMatch.Groups[i];
138                     if (group.Success)
139                     {
140                         string key = PathRegex.GroupNameFromNumber(i);
141 
142                         if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
143                         {
144                             if (!string.IsNullOrEmpty(group.Value))
145                             {
146                                 data.Values[key] = group.Value;
147                             }
148                         }
149                     }
150                 }
151             }
152 
153             return data;
154         }
155 
156         public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
157         {
158 
159             VirtualPathData pathData = base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
160             if (pathData != null && !string.IsNullOrEmpty(pathData.VirtualPath))
161             {
162                 if (pathData.VirtualPath.EndsWith(".html/"))
163                 {
164                     pathData.VirtualPath = pathData.VirtualPath.TrimEnd('/');
165                 }
166                 else if (!pathData.VirtualPath.EndsWith("/") && !pathData.VirtualPath.EndsWith(".html"))
167                 {
168                     pathData.VirtualPath = pathData.VirtualPath + "/";
169                 }
170             }
171             return pathData;
172         }
173 
174         private Regex CreateRegex(string source)
175         {
176             try
177             {
178                 string src = source;
179                 // 替换
180                 source = source.Replace("/", @"\/?");
181                 source = source.Replace(".", @"\.?");
182                 source = source.Replace("-", @"\-?");
183                 source = source.Replace("{", @"(?<");
184                 source = source.Replace("}", @">([a-zA-Z0-9_]*))");
185                 return new Regex("^" + source + "$", RegexOptions.Compiled);
186             }
187             catch (Exception ex)
188             {
189                 //AppLogger.Error("创建正则出错:" + source + " 详情:" + ex.Message);
190             }
191             return new Regex("^" + source + "$", RegexOptions.Compiled);
192         }
193 
194         private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
195         {
196 
197             Match tokenMatch = tokenRegex.Match(Domain);
198             for (int i = 0; i < tokenMatch.Groups.Count; i++)
199             {
200                 Group group = tokenMatch.Groups[i];
201                 if (group.Success)
202                 {
203                     string key = group.Value.Replace("{", "").Replace("}", "");
204                     if (values.ContainsKey(key))
205                         values.Remove(key);
206                 }
207             }
208 
209             return values;
210         }
211 
212         private static string ConvertToPattern(string source)
213         {
214             try
215             {
216                 string src = source;
217                 // 替换
218                 source = source.Replace("/", @"\/?");
219                 source = source.Replace(".", @"\.?");
220                 source = source.Replace("-", @"\-?");
221                 source = source.Replace("{", @"(?<");
222                 source = source.Replace("}", @">([a-zA-Z0-9_]*))");
223                 return "^" + source + "$";
224             }
225             catch (Exception ex)
226             {
227             }
228             return source;
229         }
230 
231     }
View Code

注册路由

1             routes.LowercaseUrls = true;
2 
3             routes.Add("T0StudentSearchDomainRoute", new DomainRoute(
4                 "t0.demo.com",  // Domain with parameters
5                 WebCoreConsts.URL_T0_SearchStudent, // URL with parameters
6                 new { controller = "Student", action = "Search" }
7                         //, new { }
8             ));

可能还需要在web.config的system.webServer项中增加一行配置,否则.html不会进入路由,会直接映射成本地路径

    <modules runAllManagedModulesForAllRequests="true" />

这时候

    <a href="@Url.DomainAction("Search", "Student", new { gender = 1, degree = 0, pageSize = 20, page = 1 }, DemoDomain.T0)" target="_blank">查找性别男的学生</a>

就会对应成

    <a href="http://t0.demo.com/search/1-0-20-1.html" target="_blank">查找性别男的学生</a>

 

  附:源码下载

 

posted @ 2016-04-29 18:17  Frozen.Zhang  阅读(700)  评论(1编辑  收藏  举报