自从微软发布 ASP.NET MVC 和routing engine (System.Web.Routing)以来,就设法让咱们明白你彻底能控制URL和routing,只要与你的application path相结合进行扩展,任何问题都迎刃而解。若是你须要在所处的域或者子域处理数据标记的话,强制使用Default。html
遗憾的是,ASP.NET MVC是基于虚拟目录的,在实际项目却有各类各样的需求方案。app
例如:dom
1:应用程序是多语言的,像cn.example.com应该被匹配到“www.{language}example.com”路由上。ide
2:应用程序是多用户的,像username.example.com应该被匹配到“www.{clientname}.example.com”路由上。post
3:应用程序是多子域的,像mobile.example.com应该被匹配到"www.{controller}.example.com/{action}....” 。ui
坐下来,深呼吸,开始咱们ASP.NET MVC的神奇之旅吧。this
定义routes
下面是咱们定义简单的route,不带任何controller控制的route:lua
routes.Add("DomainRoute", new DomainRoute(
"home.example.com", // Domain with parameters
"{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
));
另外一个例子是用咱们的controller控制域名:spa
Code
打算用controller 和action彻底控制域名?code
routes.Add("DomainRoute", new DomainRoute(
"{controller}-{action}.example.com", // Domain with parameters
"{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
));
接下来是多语言route:
routes.Add("DomainRoute", new DomainRoute(
"{language}.example.com", // Domain with parameters
"{controller}/{action}/{id}", // URL with parameters
new { language = "en", controller = "Home", action = "Index", id = "" } // Parameter defaults
));
HtmlHelper 扩展方法
由于咱们不但愿全部的URL所产生HtmlHelper ActionLink要使用full URLs,第一件事咱们会添加一些新的ActionLink,其中载有boolean flag是否要full URLs或没有。利用这些,如今您能够添加一个连接到一个Action以下:
<%= Html.ActionLink("About", "About", "Home", true)%>
跟你以往的习惯没有什么不一样,不是吗?
如下是一小段代码:
public static class LinkExtensions
{
public static string ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool requireAbsoluteUrl)
{
return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl);
}
// more of these
public static string ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> 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;
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);
}
}
在这没什么特别的:有许多的扩展方法,把扩展的URL加到域名上。这是一个预设ActionLink helpers,个人精神食粮来了DomainRoute class(详见:Dark Magic)
Dark magic
瞥眼之间,您可能已经看到了个人DomainRoute类代码段。这个类其实是提取子域,并增长了象征性支持域部分的传入的URL,
咱们将扩展基类,它已经给了咱们一些属性和方法,可是咱们得重写他们!
public class DomainRoute : Route
{
//
public string Domain { get; set; }
//
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);
// Route 数据
RouteData data = null;
if (domainMatch.Success && pathMatch.Success)
{
data = new RouteData(this, RouteHandler);
// 添加默认选项
if (Defaults != null)
{
foreach (KeyValuePair<string, object> item in Defaults)
{
data.Values[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;
}
}
}
}
// 匹配域名路径
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;
}
}
}
}
}
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<string, object> pair in values)
{
hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString());
}
// Return 域名数据
return new DomainData
{
Protocol = "http",
HostName = hostname,
Fragment = ""
};
}
// 
}
哇,这是一串按照咱们定义的route转换传入请求的URL到tokens的代码,咱们这样作是转换{controller}和按照regex而后再尝试匹配route规则,在咱们的DomainRoute class里还有其余的helper方法,须要更多的功能能够本身研究扩展。
附代码:附件