2013-02-28 18 views
5

ich zur Zeit auf einer asp.net MVC 4 Applikation arbeite, und ich habe die Notwendigkeit für die folgenden Arten von Urls:ASP.NET MVC 4 route Benutzername/Aktion Problem

Urls, die verlegt werden müssen

  1. http://www.mysite.com/foo/user1 < ------- {username}
  2. http://www.mysite.com/foo/edit
  3. http://www.mysite.com/foo/delete/1
  4. http://www.mysite.com/bar/user1 < ------- {username}
  5. http://www.mysite.com/bar/edit
  6. http://www.mysite.com/bar/delete/1

Das Problem, das ich habe, ist, dass zur Zeit {username} als eine Aktion behandelt wird so zu Umgehung des Problems Ich habe die folgenden Routen implementiert, aber dies würde bedeuten, dass ich jedes Mal, wenn ich eine neue Aktion implementieren oder einen Controller verwenden möchte, der {username} benötigt, meine Routen aktualisieren muss:

Nur Foo Routen

routes.MapRoute("FooSomeAction", "foo/someaction", new { controller = "Food", action = "SomeAction" });    
routes.MapRoute("FooDelete", "foo/delete/{id}", new { controller = "Food", action = "Delete" });    


routes.MapRoute(
    "FooProfile", 
    "foo/{username}", 
    new { controller = "Foo", action = "Index", username = "" } 
); 


// Default route 
routes.MapRoute(
    "Default", // Route name 
    "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
); 

2 Fragen

1) Gibt es eine Möglichkeit gezeigt ich die oben Urls ohne hartzucodieren alle Routen erreichen können?

2) Wie kann man am besten mit einer Situation umgehen, in der jemand einen Benutzernamen verwendet, der zufällig der gleiche Name wie ein Controller oder Aktionsname ist?

DotnetShadow

+0

Ihre zweite Frage, soweit ich weiß, ist nicht möglich. Was ist so schlimm daran, die Action auch dort zu haben? – mattytommo

+0

Was ist so schlimm daran, die Action auch dort zu haben? - Entschuldigung, was meintest du damit? – DotnetShadow

+0

Entschuldigung, wenn ich vage war, ich meine, ist es möglich, dass Sie den Namen der Aktion und des Controllers im Link haben können? So etwas wie/Benutzer/Bearbeiten/Benutzer1? – mattytommo

Antwort

3

Sie eine benutzerdefinierte Route Constraint erstellen könnte, die, wenn der Benutzername existiert in den möglichen Aktionen für den Controller überprüfen würde. Wenn es eine Übereinstimmung findet, schlägt es fehl und verwendet Ihre Standardroute (zB Bearbeiten). Vielleicht möchten Sie die Liste aus Performance-Gründen zwischenspeichern, aber das überlasse ich Ihnen.

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

    public static List<string> GetActionNames(string controllerName) 
    { 
     controllerName = controllerName + "Controller"; 
     var controller = GetSubClasses<Controller>().FirstOrDefault(c => c.Name == controllerName); 

     var names = new List<string>(); 
     if (controller != null) 
     { 
      var methods = controller.GetMethods(BindingFlags.Public | BindingFlags.Instance); 
      foreach (var info in methods) 
      { 
       if (info.ReturnType == typeof(ActionResult)) 
       { 
        names.Add(info.Name); 
       } 
      } 

     } 
     return names; 
    } 


    public class UsernameNotAction : IRouteConstraint 
    { 
     public bool Match 
      (
       HttpContextBase httpContext, 
       Route route, 
       string parameterName, 
       RouteValueDictionary values, 
       RouteDirection routeDirection 
      ) 
     { 
      int i = 0; 
      var username = values["username"]; 
      var actionList = GetActionNames(values["controller"].ToString()); 

      return !actionList.Any(a => a.ToUpper() == username.ToString().ToUpper()); 
     } 
    } 


    public static void RegisterRoutes(RouteCollection routes) 
    { 
     routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

     routes.MapRoute(
      "FooProfile", 
      "{controller}/{username}", 
      new { controller = "Home", action = "Index2", username = "" }, 
      new { IsParameterAction = new UsernameNotAction() } 
     ); 
     // Default route 
     routes.MapRoute(
      "Default", // Route name 
      "{controller}/{action}/{id}", 
      new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
     ); 
    } 
+0

Das ist eine sehr interessante Lösung. Sie möchten definitiv nicht jedes Mal, wenn Sie navigieren, einen Datenbankaufruf durchführen, aber er könnte eine Liste von Nutzernamen in der Sitzung speichern. Ich bin mir nicht wirklich sicher, ob ich es mag, aber ich werde für Originalität und eine coole Idee +1 geben. –

+0

Good Stuff Mark, ich dachte eigentlich an eine Art Zwang, aber ich machte mir auch Sorgen um die Leistung. Danke für den alternativen Ansatz – DotnetShadow

+0

Es ist nicht wirklich einen Datenbankaufruf - es testet, wenn die Aktion in der Steuerung mit Reflexion ist (kann tatsächlich schlechter für die Leistung sein, aber wer weiß) –

1

Sie können es nicht tun, wenn Sie nicht jede einzelne Route routen und das ist nicht der beste Weg zu gehen.

Was ist falsch daran, den Action-Namen darin zu haben?

+0

Was ist falsch daran, den Controller-Namen in es? - Was meinst du damit? – DotnetShadow

+0

bedeutete, dass ich den Namen der Aktion von tun http://www.mysite.com/bar/user1 Diese an den Controller abbildet: bar und Aktion benutzer1 Sie wollten Controller treffen: bar Aktion: Index aber das ist nicht, wie MVC Routen arbeiten Sie müssen etwas tun wie http://www.mysite.com/bar/ActionName/user1 –

2

Dies ist nicht wirklich die Antwort, die Sie suchen, sorry.

1) Es gibt keine Möglichkeit, auf diese Weise zu routen. Es gibt nichts, um diese Routen voneinander zu unterscheiden, außer was Sie getan haben. Ich muss mich fragen, warum das überhaupt notwendig ist, ich bin mir sicher, dass Sie einen guten Grund haben, aber es ergibt für mich keinen Sinn. Sie verwenden immer noch die Aktion "Index", also warum nicht einfach/foo/index/username. Alles, was mir einfällt, ist, dass du aus irgendeinem Grund keine Kontrolle über die URL hast.

2) Wenn Sie die Standardroute verwenden, gibt es kein Problem. Mit Ihrem Routing, Problem. Ihre einzige echte Option besteht darin, den Controller- und Aktionsnamen reservierte Wörter zu geben (verhindern, dass Benutzer mit diesen Benutzernamen in der Datenbank erstellt werden).

Sorry, ich konnte dir nicht wirklich helfen.

+0

Guter Punkt über 1) mit Index als die Aktion, es sieht einfach sauberer mit/foo/username Ich denke Im Grunde war meine Idee, wenn Sie jemand Profil öffentlich sehen würde, hätte ich: /Profil/username /Freunde/username /gallery/Benutzername aber wenn ich in meinem eigenen Profil bin würde ich tun, nur /profile/edit/1 /Freunde/Liste /gallery/löschen/1 Wenn Das macht Sinn? – DotnetShadow

+0

Ich bekomme, was Sie versuchen zu tun, aber das Standard-Routing, das für 83,57% aller Fälle funktioniert, ist Controller/Aktion/Ansicht. Wenn Sie dieses Muster brechen wollen, erzeugen Sie Arbeit für sich selbst, also fragen Sie sich einfach, ob es die Mühe wert ist, was Sie gewinnen. IMO. –

+0

Auch Leute sollten nicht Ihre Website navigieren, indem Sie die URL ändern, diese werden über Aktionsverknüpfungen kommen und so und wirklich, schaut jemand auf die URL oder kümmert sich, wie es aussieht? http: // mysite/viewusers/index/bob1111 vs http: // mysite/viewusers/bob1111, wer wird sich wirklich darum kümmern? :) –