2012-05-02 42 views
5

我已經建立了類似的路線:MVC路由約束控制器上的名稱

 context.MapRoute(
     name: "Area", 
     url: "Area/{controller}/{action}", 
     defaults: new 
     { 
      controller = "Home", 
      action = "Dashboard" 
     } 
     ); 

     context.MapRoute(
     name: "AccountArea", 
     url: "Area/{accountFriendlyId}/{controller}/{action}", 
     defaults: new 
     { 
      controller = "Home", 
      action = "Dashboard", 
      accountFriendlyId = RouteParameter.Optional 
     } 
     ); 

     context.MapRoute(
     name: "AccountCampaignArea", 
     url: "Area/{accountFriendlyId}/{campaignFriendlyId}/{controller}/{action}", 
     defaults: new 
       { 
        controller = "Home", 
        action = "Dashboard", 
        accountFriendlyId = RouteParameter.Optional, 
        campaignFriendlyId = RouteParameter.Optional 
       } 
     ); 

而且我有一個強烈的願望,有Area/friendlyAccountName/Home帶我去Dashboard()但這不起作用(404)。我認爲原因是我們去尋找一個friendlyAccountName控制器。

舒服的知識,如果我應該選擇在我的控制器之一後命名一個帳戶一切都崩潰了,是否有一種方法,以避免遇到字符串找不到相應的控制器的下一個路線?有沒有辦法使用反射,並避免每次修改我的控制器列表時保持約束?

編輯

你知道不使用反射的方式,或者至少包含派生類型搜索到這個領域?當第二個路由參數匹配控制器名稱時(傳遞約束然後在構建控制器時再次搜索),我不喜歡這種開銷兩次的想法。我希望有一種方法可以在構建控制器的時候捕捉異常,然後備份並落到下一個路徑。

回答

6

爲什麼你需要第一條路呢?如果{accountFriendlyId}是可選的,那麼您應該可以忽略它,並獲得與第一條註冊路線相同的路由默認值。

這樣,它會匹配AccountArea命名的路由第一,這是你想要的,然後如果未指定{accountFriendlyId}是將區域作爲控制器處理後的第一個標記上。

事實上,我覺得你應該能夠完全移除前兩條路線,並堅持最後一條路線,因爲前兩個路線參數是可選的,並且默認值是相同的。

UPDATE

由於{accountFriendlyId}可能是一個有效的控制器操作名稱,你可以做一些其他的事情:

  1. 移動{accountFriendlyId}到路線的終點,而不是在開始。這遵循更加自然的URL資源的最廣泛的資源類型。
  2. 使用route constraints。您理論上可以使用反射來生成正則表達式以匹配custom constraint中的控制器名稱,或者您可以手動將其寫出。是這樣的:

context.MapRoute(

name: "Area", 
    url: "Area/{controller}/{action}", 
    defaults: new 
    { 
     controller = "Home", 
     action = "Dashboard", 
     new { controller = @"(Account|Profile|Maintenance)" } 
    } 

);

+0

沒有第一兩條路線,'氬EA /控制器/ Action'將填補友好ID參數,並送我到默認的控制器和行動和'區域/友好/控制器/ Action'會失敗 –

+0

啊,對不起,我沒有聽清楚'{accountFriendlyId}'將是一個有效的操作名稱。我會更新答案。 –

+0

這些字符串被解析的約定是什麼?我不知道你可以用文字內聯(我可能不會特別用它)。 –

2

這是一個URL空間問題。您如何區分accountFriendlyId,campaignFriendlyId和控制器?簡單的方法是將它們放在URL的不同部分,但對於您的路由,控制器可以是第二,第三或第四部分。你必須使用約束來消除歧義,並命令他們這樣的:

context.MapRoute(null, "Area/{controller}/{action}", 
    new { controller = "Home", action = "Dashboard" }, 
    new { controller = "Foo|Bar" }); 

context.MapRoute(null, "Area/{accountFriendlyId}/{controller}/{action}", 
    new { controller = "Home", action = "Dashboard" }, 
    new { controller = "Foo|Bar" }); 

context.MapRoute(null, "Area/{accountFriendlyId}/{campaignFriendlyId}/{controller}/{action}", 
    new { controller = "Home", action = "Dashboard" }); 

你的建議,如果沒有找到控制器,然後嘗試下一個匹配的路由的想法,這是行不通的那樣,一旦某條路匹配就是這樣,您將不得不修改UrlRoutingModule以嘗試完成此項工作。

+0

只要過濾反射以獲取控制器名稱字符串只在這個區域?我應該在'IController'和這個區域的控制器之間貼一個空的'IAreaController'嗎? –

+0

@ nik.shornikov我不確定你在說什麼,你想要建立限制dinamically? –

+0

yes - 'Assembly.GetCallingAssembly()。GetTypes()。Where(type => type.IsSubclassOf(typeof(IController)))'' –

6

最終,爲了方便我想要的東西(這是依賴於具有應用的任意字符串和控制器名稱之間進行動態區分),我設置了這樣的路線:

public override void RegisterArea(AreaRegistrationContext context) 
    { 
     context.MapRoute(
     name: "AccountCampaignArea", 
     url: "Area/{accountFriendlyId}/{campaignFriendlyId}/{controller}/{action}", 
     defaults: new 
      { 
       controller = "Home", 
       action = "Dashboard", 
       accountFriendlyId = RouteParameter.Optional, 
       campaignFriendlyId = RouteParameter.Optional, 
       id = UrlParameter.Optional 
      }, 
     constraints: new { accountFriendlyId = new ControllerNameConstraint(), campaignFriendlyId = new ControllerNameConstraint() } 
     ); 

     context.MapRoute(
      name: "AccountArea", 
      url: "Area/{accountFriendlyId}/{controller}/{action}", 
      defaults: new 
       { 
        controller = "Home", 
        action = "Dashboard", 
        accountFriendlyId = RouteParameter.Optional, 
        id = UrlParameter.Optional 
       }, 
      constraints: new { accountFriendlyId = new ControllerNameConstraint() } 
      ); 

     context.MapRoute(
     name: "Area", 
     url: "Area/{controller}/{action}", 
     defaults: new 
      { 
       controller = "Home", 
       action = "Dashboard" 
      } 
     ); 
    } 

併成立了這樣的約束(約束也可以被稱爲NotControllerNameContraint):

public class ControllerNameConstraint : IRouteConstraint 
{ 
    private static List<Type> GetSubClasses<T>() 
    { 
     return Assembly.GetCallingAssembly().GetTypes().Where(
      type => type.IsSubclassOf(typeof(T))).ToList(); 
    } 

    public List<string> GetControllerNames() 
    { 
     List<string> controllerNames = new List<string>(); 
     GetSubClasses<Controller>().ForEach(
      type => controllerNames.Add(type.Name)); 
     return controllerNames; 
    } 
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
    { 
     if (values.ContainsKey(parameterName)) 
     { 
      string stringValue = values[parameterName] as string; 
      return !GetControllerNames().Contains(stringValue + "Controller"); 
     } 

     return true; 
    } 
} 

現金https://stackoverflow.com/a/1152735/938472

相關問題