2013-02-28 8 views
5

Attualmente sto lavorando su un 4 applicazione asp.net MVC e ho la necessità dei seguenti tipi di indirizzi:ASP.NET MVC 4 route username/azione problema

URL che hanno bisogno di essere indirizzati

  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

Il problema che sto avendo è che attualmente {username} viene trattato come un'azione per così aggirare il problema ho implementato i seguenti percorsi, ma questo significherebbe che ogni volta che voglio implementare una nuova azione, o avere un controller che ha bisogno di {username}, dovrei aggiornare i miei percorsi:

Solo percorsi Foo mostrati

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 Domande

1) C'è un modo posso raggiungere gli URL di cui sopra senza hardcoding tutti i percorsi?

2) Qual è il modo migliore per gestire una situazione in cui qualcuno utilizza un nome utente che ha lo stesso nome di un controller o nome di azione?

DotnetShadow

+0

La seconda domanda, per quanto ne so, non è possibile. Cosa c'è di male nell'avere l'azione anche lì? – mattytommo

+0

Cosa c'è di male nell'avere l'azione anche lì? - Scusa, cosa intendevi con questo? – DotnetShadow

+0

Mi scuso se sono stato vago, voglio dire che è possibile avere il nome dell'azione e del controller entrambi presenti nel collegamento? Qualcosa come/Utente/Modifica/Utente1? – mattytommo

risposta

3

Si potrebbe creare un vincolo percorso personalizzato che controlla se il nome utente esiste nelle possibili azioni per il controller. Se trova una corrispondenza di azione, fallisce e utilizzerà la tua rotta predefinita (Modifica, ad esempio). Si consiglia di memorizzare nella cache l'elenco per motivi di prestazioni, ma lo lascio a voi.

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

Questa è una soluzione molto interessante. Sicuramente non vuoi fare una chiamata al database ogni volta che navighi, ma potrebbe memorizzare un elenco di nomi utente nella sessione. Non sono sicuro che mi piaccia, ma farò +1 per l'originalità e una bella idea. –

+0

Good Stuff Mark, stavo davvero pensando a una sorta di vincolo, ma ero preoccupato anche per le prestazioni. Grazie per l'approccio alternativo – DotnetShadow

+0

In realtà non sta facendo una chiamata al database - sta testando se l'azione è nel controller usando il reflection (potrebbe effettivamente essere peggiore per le prestazioni ma chissà) –

1

Non si può fare così a meno che non si instradi ogni singola rotta e che non sia il modo migliore per andare.

Cosa c'è di così sbagliato nell'avere il nome Action in esso?

+0

Cosa c'è di così sbagliato nell'avere il nome del controller in vero? - Cosa intendi con questo? – DotnetShadow

+0

Intendevo il nome di Azione Facendo http://www.mysite.com/bar/user1 Ciò mappare al controller: bar e azione user1 Si voleva colpire Controller: bar Azione: Indice ma non è così che funzionano le rotte MVC Hai bisogno di fare qualcosa come http://www.mysite.com/bar/ActionName/user1 –

2

Questa non è proprio la risposta che stai cercando, mi dispiace.

1) Non c'è modo di fare rotta in questo modo. Non c'è niente per differenziare queste rotte l'una dall'altra, a parte quello che hai fatto. Devo chiedermi perché sia ​​necessario, sono sicuro che hai una buona ragione, ma non ha senso per me. Stai ancora utilizzando l'azione Index, quindi perché non solo/foo/index/username. Tutto quello che posso inventare è che non hai il controllo dell'URL per qualche motivo.

2) Se si utilizza il percorso predefinito, non ci sono problemi. Con il tuo percorso, problema. La tua unica vera opzione è quella di rendere le parole riservate per i nomi delle azioni e del controllore (impedire agli utenti di essere creati con quei nomi utente nel database).

Scusa, non ho potuto davvero aiutarti.

+0

Buon punto a proposito 1) avendo indice come l'azione, sembra proprio più pulito avendo/foo/username Immagino che Fondamentalmente la mia idea era se stavi visualizzando il profilo di qualcuno pubblicamente: /profilo/username /amici/username /galleria/username ma se io sono nel mio profilo vorrei solo fare /profile/modificare/1 /amici/Lista /galleria/cancellare/1 Se ciò ha senso? – DotnetShadow

+0

Ricevo quello che stai cercando di fare, ma il routing predefinito che funziona per l'83.57% di tutti i casi è controller/action/view. Se vuoi rompere questo schema, stai generando lavoro per te stesso, quindi chiediti se vale tutto lo sforzo rispetto a ciò che guadagni. IMO. –

+0

Inoltre, le persone non dovrebbero navigare nel tuo sito modificando l'url, quelli che arriveranno tramite i link di azione e così e davvero, qualcuno guarderà l'url o si preoccuperà di come appare? http: // mysite/viewusers/index/bob1111 vs http: // mysite/viewusers/bob1111, a chi interesserà veramente? :) –