1

我試圖建立一個「租戶」子域路由,附加到MVC區域。在這種情況下,我有一個名爲「Tenant」的區域,它有兩個控制器;公共和管理員。我的自定義路由用於抓取子域,如果它匹配然後路由到適當的控制器行動區。MVC自定義路由子域

這個項目的基礎來自於以下 http://www.matrichard.com/post/asp.net-mvc-5-routing-with-subdomain

我遇到的問題是在自定義子域路由。當我點擊Public/Index路由時,routeData返回null,我看到以下錯誤。雖然如果路線是/admin,它將返回正確的routeData

'/'應用程序中的服務器錯誤。

匹配的路由不包含需要的「控制器」路由值。

它似乎也總是匹配使用RouteDebugger工具,這是我的問題的線索?

實例途徑:

控制器=公共行動=索引,面積=租客

http://tenant1.mydomain.com:8080/

http://tenant1.mydomain.com:8080/logon

控制器=管理員動作=索引,面積=租客

http://tenant1.mydomain.com:8080/admin

http://tenant1.mydomain.com:8080/admin/edit

-

SubdomainRouteP.cs

public class SubdomainRouteP : Route 
{ 
    public string Domain { get; set; } 

    public SubdomainRouteP(string domain, string url, RouteValueDictionary defaults): this(domain, url, defaults, new MvcRouteHandler()) 
    { 
    } 

    public SubdomainRouteP(string domain, string url, object defaults): this(domain, url, new RouteValueDictionary(defaults), new MvcRouteHandler()) 
    { 
    } 

    public SubdomainRouteP(string domain, string url, object defaults, IRouteHandler routeHandler): this(domain, url, new RouteValueDictionary(defaults), routeHandler) 
    { 
    } 

    public SubdomainRouteP(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler): base(url, defaults, routeHandler) 
    { 
     this.Domain = domain; 
    } 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     // 
     // routeData object returns null in some cases 
     // 
     var routeData = base.GetRouteData(httpContext); 

     var subdomain = httpContext.Request.Url.Host.Split('.').First(); 

     string[] blacklist = { "www", "mydomain", "localhost" }; 

     // This will ignore anything that is not a client tenant prefix 
     if (blacklist.Contains(subdomain)) 
     { 
      return null; // Continue to the next route 
     } 

     // Why is this NULL? 
     if (routeData == null) 
     { 

      routeData = new RouteData(this, new MvcRouteHandler()); 

     } 

     routeData.DataTokens["Area"] = "Tenant"; 
     routeData.DataTokens["UseNamespaceFallback"] = bool.FalseString; 
     routeData.Values.Add("subdomain", subdomain); 

     // IMPORTANT: Always return null if there is no match. 
     // This tells .NET routing to check the next route that is registered. 
     return routeData; 
    } 

} 

RouteConfig.cs

 routes.Add("Admin_Subdomain", new SubdomainRouteP(
      "{client}.mydomain.com", //of course this should represent the real intent…like I said throwaway demo project in local IIS 
      "admin/{action}/{id}", 
      new { controller = "Admin", action = "Index", id = UrlParameter.Optional })); 

     routes.Add("Public_Subdomain", new SubdomainRouteP(
      "{client}.mydomain.com", //of course this should represent the real intent…like I said throwaway demo project in local IIS 
      "{controller}/{action}/{id}", 
      new { controller = "Public", action = "Index", id = UrlParameter.Optional })); 

     // This is the MVC default Route 
     routes.MapRoute(
      "Default", 
      "{controller}/{action}/{id}", 
      new { controller = "Home", action = "Index", id = UrlParameter.Optional }); 

以下鏈接給我從RouteDebugger以下結果。在測試1和2期間,路由仍然匹配/ admin。

失敗測試1:http://tenant.mydomain.com/

失敗測試2:http://tenant.mydomain.com/logon

成功3:http://tenant.mydomain.com/admin

匹配地址默認

admin/{action}/{id}controller = Admin, action = Index

{controller}/{action}/{id}controller = Public, action = Index

+0

我也看過這篇文章,但它有硬編碼路線,當我真的需要默認值。 http://stackoverflow.com/questions/278668/is-it-possible-to-make-an-asp-net-mvc-route-based-on-a-subdomain/15287579#15287579 – phanf

回答

1

的職位,您鏈接到有一個錯誤:當一個約束或URL不匹配,base.GetRouteData方法將返回null。在這種情況下,將子域名添加到路由字典顯然會引發異常。在該行之前應該有一個空警戒條款。

public override RouteData GetRouteData(HttpContextBase httpContext) 
{ 
    var routeData = base.GetRouteData(httpContext); 
    if (routeData != null) 
    { 
     routeData.Values.Add("client", httpContext.Request.Url.Host.Split('.').First()); 
    } 
    return routeData; 
} 

您的路線應該如此。您需要確保在基類返回null(表示URL或約束不匹配,並且我們需要跳過處理此路由)的情況下返回null。另外,我不確定它是否會將數據直接添加到DataTokens,但MVC框架有一個IRouteWithArea可以實現配置路由適用的區域。

public class SubdomainRouteP : Route, IRouteWithArea 
{ 
    public string Area { get; private set; } 

    public SubdomainRouteP(string area, string url, RouteValueDictionary defaults): this(area, url, defaults, new MvcRouteHandler()) 
    { 
    } 

    public SubdomainRouteP(string area, string url, object defaults): this(area, url, new RouteValueDictionary(defaults), new MvcRouteHandler()) 
    { 
    } 

    public SubdomainRouteP(string area, string url, object defaults, IRouteHandler routeHandler): this(area, url, new RouteValueDictionary(defaults), routeHandler) 
    { 
    } 

    public SubdomainRouteP(string area, string url, RouteValueDictionary defaults, IRouteHandler routeHandler): base(url, defaults, routeHandler) 
    { 
     this.Area = area; 
    } 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     var routeData = base.GetRouteData(httpContext); 

     // This will ignore anything where the URL or a constraint doesn't match 
     // in the call to base.GetRouteData(). 
     if (routeData != null) 
     { 
      var subdomain = httpContext.Request.Url.Host.Split('.').First(); 

      string[] blacklist = { "www", "mydomain", "localhost" }; 

      // This will ignore anything that is not a client tenant prefix 
      if (blacklist.Contains(subdomain)) 
      { 
       return null; // Continue to the next route 
      } 

      routeData.DataTokens["UseNamespaceFallback"] = bool.FalseString; 
      routeData.Values.Add("subdomain", subdomain); 
     } 

     // IMPORTANT: Always return null if there is no match. 
     // This tells .NET routing to check the next route that is registered. 
     return routeData; 
    } 

} 

我想不出你要用domain參數來做什麼。該網址很有可能會返回某個域名。所以,看起來你應該在第一條路線上有一個限制,否則你將永遠不會有一個會通過默認路線的情況。或者,您可以在網址中使用明確的細分,以便區分它(與您的管理路線相同)。

routes.Add("Admin_Subdomain", new SubdomainRouteP(
    "Tenant", 
    "admin/{action}/{id}", 
    new { controller = "Admin", action = "Index", id = UrlParameter.Optional })); 

routes.Add("Public_Subdomain", new SubdomainRouteP(
    "Tenant", 
    "public/{action}/{id}", 
    new { controller = "Public", action = "Index", id = UrlParameter.Optional })); 

// This is the MVC default Route 
routes.MapRoute(
    "Default", 
    "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }); 

另一種選擇是添加另一個構造參數,以傳遞一個顯式的有效域列表進行檢查。

+0

- NightOwl888,謝謝你的迴應,我按照你的建議修改了源代碼,看起來效果很好!我還包括了'IRouteWithArea'區域參數,這將在稍後的項目中證明有用。我也被來自matrichard鏈接的額外'domain'參數弄糊塗了。謝謝你的幫助。我已將此標記爲解決方案。 – phanf