ASP.NET MVC 實現二級域名

 

 

自從微軟發佈 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方法,須要更多的功能能夠本身研究擴展。

附代碼:附件

相關文章
相關標籤/搜索