2012-05-02 21 views
5

mam trasy skonfigurowane następująco:MVC Routing ograniczeniem Controller Names

 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 
       } 
     ); 

I mam palące pragnienie posiadania Area/friendlyAccountName/Home wziąć mnie do Dashboard() ale to nie działa (404). Myślę, że powodem jest to, że szukamy przyjaznego kontroleraAccountName.

Wygodny ze świadomością, że gdybym miał wybrać nazwę konta po jednym z moich kontrolerów, wszystko się zawiesza, czy istnieje sposób na przejście do następnej trasy w przypadku, gdy ciąg nie znajdzie odpowiedniego kontrolera? Czy istnieje sposób na wykorzystanie refleksji i unikanie utrzymywania ograniczeń za każdym razem, gdy modyfikuję listę kontrolerów?

EDIT

Znasz drogę, która nie wykorzystuje odbicia lub przynajmniej zawiera pochodną typu przeszukiwanie do tego obszaru? Nie podoba mi się pomysł poniesienia tego obciążenia dwukrotnie, gdy drugi parametr trasy nie pasuje do nazwy kontrolera (przekroczenie limitu, a następnie ponowne wyszukiwanie podczas konstruowania kontrolera). Szkoda, że ​​nie było sposobu, aby uchwycić wyjątek w momencie konstruowania kontrolera, a następnie utworzyć kopię zapasową i przejść do następnej trasy.

Odpowiedz

6

Dlaczego w ogóle potrzebujesz pierwszej trasy? Jeśli opcja {accountFriendlyId} jest opcjonalna, powinieneś mieć możliwość pominięcia tej opcji i uzyskania tej samej wartości domyślnej trasy, co pierwsza zarejestrowana trasa.

W ten sposób będzie pasować na podanej nazwie AccountArea, co jest potrzebne, a następnie, jeśli nie zostanie określony {accountFriendlyId}, traktuje pierwszy token za obszarem jako kontrolerem.

Właściwie, wydaje mi się, że powinieneś być w stanie całkowicie usunąć pierwsze dwie trasy i po prostu trzymać się ostatniej, ponieważ pierwsze dwa parametry trasy są opcjonalne, a wartości domyślne są identyczne.

UPDATE

Ponieważ {accountFriendlyId} może być poprawną nazwę operacji kontroler, można zrobić kilka innych rzeczy:

  1. Move {accountFriendlyId} do końca trasy, zamiast na początek. Wynika to z bardziej naturalnego stylu URL najszerszego zasobu do określonych szczegółów w ramach zasobu.
  2. Użyj route constraints. Teoretycznie możesz użyć odbicia, aby wygenerować wyrażenie regularne pasujące do nazw kontrolerów w postaci custom constraint, lub możesz po prostu napisać je ręcznie. Coś takiego:

context.MapRoute (

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

);

+0

Bez pierwszych dwóch trasach, 'Ar ea/Controller/Action "wypełni przyjazne parametry id i wyśle ​​mnie do domyślnego kontrolera, a akcja" Area/friendly/Controller/Action "zawiedzie –

+0

Ach, przepraszam, nie złapałem tego' {accountFriendlyId} ' będzie prawidłową nazwą operacji. Zaktualizuję odpowiedź. –

+0

Jaka jest konwencja dotycząca analizowania tych łańcuchów? Nie miałem pojęcia, że ​​można to zrobić w sposób dosłowny (prawdopodobnie nie będę ich używał w szczególności). –

2

To jest problem z przestrzenią URL. Jak odróżnić accountFriendlyId, campaignFriendlyId i kontroler? Łatwym sposobem jest umieszczenie ich w różnych segmentach adresu URL, ale w twoich trasach kontroler może być drugim, trzecim lub czwartym segmentem.Musisz użyć ograniczenia do dwuznaczności i zamówić je tak:

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" }); 

Pomysł zasugerował, jeśli kontroler nie zostanie znaleziony następnie spróbuj następnego pasującego trasę, to nie działa w ten sposób, gdy trasa mecze to wszystko, musisz zmodyfikować UrlRoutingModule, aby spróbować wykonać tę pracę.

+0

A jeśli chodzi o filtrowanie odbicia, aby uzyskać ciągi znaków kontrolera tylko w tym obszarze? Czy powinienem włożyć pustą kontrolkę IAreaController pomiędzy 'IController' i kontrolery w tym obszarze? –

+0

@ nik.shornikov Nie jestem pewien, o czym mówisz, chcesz zbudować ograniczenia dinomicznie? –

+0

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

6

Ostatecznie, w celu ułatwienia co chciałem (co było uzależnione od posiadania aplikacja dynamicznie odróżnić dowolnych ciągów znaków i nazw Controller) skonfigurować trasy tak:

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" 
      } 
     ); 
    } 

i skonfigurować ograniczenie tak (ograniczenie może być również nazywany 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; 
    } 
} 

kredyty: https://stackoverflow.com/a/1152735/938472