参考:
参考:
适当使用反编译工具查看 Route 的源码
主要类
DomainData.cs
namespace DomainMvcTest.Core{ public class DomainData { public string Protocol { get; set; } public string HostName { get; set; } public string Fragment { get; set; } }}
DomainRoute.cs
using System.Collections.Generic;using System.Text.RegularExpressions;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace DomainMvcTest.Core{ 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 (KeyValuePairitem in Defaults) { data.Values[item.Key] = item.Value; if (item.Key.Equals("area") || item.Key.Equals("Namespaces")) { data.DataTokens[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; if (key.Equals("area")) { data.DataTokens[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; if (key.Equals("area")) { data.DataTokens[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 pair in values) { if (pair.Key == "area" && string.IsNullOrEmpty(pair.Value.ToString())) { hostname = hostname.Replace("{ " + pair.Key + "}", "www"); } else { hostname = hostname.Replace("{ " + pair.Key + "}", pair.Value.ToString()); } } //如果域名的area还没有被替换,说明路由数据里没有area,恢复为顶级域名 if (hostname.Contains("{area}")) { hostname = hostname.Replace("{area}", "www"); } RemoveDomainTokens(values); // 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 + "$"); } 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; } }}
LinkExtensions.cs
using System.Collections.Generic;using System.Linq;using System.Web.Routing;using DomainMvcTest.Core;namespace System.Web.Mvc.Html{ public static class LinkExtensions { public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, bool requireAbsoluteUrl) { return htmlHelper.ActionLink(linkText, actionName, null, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl); } public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, bool requireAbsoluteUrl) { return htmlHelper.ActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary(), requireAbsoluteUrl); } public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool requireAbsoluteUrl) { return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl); } public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, bool requireAbsoluteUrl) { return htmlHelper.ActionLink(linkText, actionName, null, routeValues, new RouteValueDictionary(), requireAbsoluteUrl); } public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes, bool requireAbsoluteUrl) { return htmlHelper.ActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes), requireAbsoluteUrl); } public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionaryhtmlAttributes, bool requireAbsoluteUrl) { return htmlHelper.ActionLink(linkText, actionName, null, routeValues, htmlAttributes, requireAbsoluteUrl); } public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, bool requireAbsoluteUrl) { return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes), requireAbsoluteUrl); } public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary htmlAttributes, bool requireAbsoluteUrl) { if (requireAbsoluteUrl) { HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current); RouteData routeData = RouteTable.Routes.GetRouteData(currentContext); routeData.Values["controller"] = controllerName; routeData.Values["action"] = actionName; //如果不需要变换area,则routeValues不传入area的值 if (routeValues.Keys.Contains("area")) { routeData.Values["area"] = routeValues["area"]; } DomainRoute domainRoute = routeData.Route as DomainRoute; if (domainRoute != null) { DomainData domainData = domainRoute.GetDomainData(new RequestContext(currentContext, routeData), routeData.Values); return htmlHelper.ActionLink(linkText, actionName, controllerName, domainData.Protocol, domainData.HostName, domainData.Fragment, routeData.Values, null); } } return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes); } }}
在 AreaRegistration 中注册路由
using System.Web.Mvc;using DomainMvcTest.Core;namespace DomainMvcTest.Areas.Haha{ public class HahaAreaRegistration : AreaRegistration { public override string AreaName { get { return "Haha"; } } public override void RegisterArea(AreaRegistrationContext context) { context.Routes.Add("hahaDomain", new DomainRoute( "{area}.chenwei.com", // Domain with parameters "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "", Namespaces = new[] { "DomainMvcTest.Areas.Haha.Controllers" }} // Parameter defaults )); //context.MapRoute( // "Haha_default", // "Haha/{controller}/{action}/{id}", // new { action = "Index", id = UrlParameter.Optional } //); } }}
View 中生成Url连接
@Html.ActionLink("前往首页", "Index", "Home",new{area=""},null,true) @Html.ActionLink("前往Hehe", "Index", "Home", new { area = "Hehe" }, null, true) @Html.ActionLink("前往Haha", "Index", "Home", new { area = "Haha" }, null, true)