Routing di ASP.NET MVC tramite gli attributi del metodo

Nel Podcast # 54 di StackOverflow , Jeff menziona che registrano le loro rotte URL nella base di codice StackOverflow tramite un attributo sopra il metodo che gestisce la rotta. Sembra un buon concetto (con l’avvertenza che Phil Haack ha sollevato per quanto riguarda le priorità del percorso).

Qualcuno potrebbe fornire qualche esempio per farlo accadere?

Inoltre, eventuali “migliori pratiche” per l’utilizzo di questo stile di routing?

AGGIORNAMENTO : è stato pubblicato su codeplex . Il codice sorgente completo e l’assembly precompilato sono disponibili per il download. Non ho ancora avuto il tempo di pubblicare la documentazione sul sito, quindi questo post SO dovrà essere sufficiente per ora.

AGGIORNAMENTO : ho aggiunto alcuni nuovi attributi per gestire 1) l’ordinamento del percorso, 2) i vincoli dei parametri del percorso e 3) i valori predefiniti dei parametri del percorso. Il testo sottostante riflette questo aggiornamento.

In realtà ho fatto qualcosa del genere per i miei progetti MVC (non ho idea di come Jeff lo stia facendo con stackoverflow). Ho definito una serie di attributi personalizzati: UrlRoute, UrlRouteParameterConstraint, UrlRouteParameterDefault. Possono essere collegati ai metodi di azione del controller MVC in modo che i percorsi, i vincoli e i valori predefiniti vengano associati automaticamente a tali percorsi.

Esempio di utilizzo:

(Nota questo esempio è un po ‘inventato ma dimostra la caratteristica)

public class UsersController : Controller { // Simple path. // Note you can have multiple UrlRoute attributes affixed to same method. [UrlRoute(Path = "users")] public ActionResult Index() { return View(); } // Path with parameter plus constraint on parameter. // You can have multiple constraints. [UrlRoute(Path = "users/{userId}")] [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")] public ActionResult UserProfile(int userId) { // ...code omitted return View(); } // Path with Order specified, to ensure it is added before the previous // route. Without this, the "users/admin" URL may match the previous // route before this route is even evaluated. [UrlRoute(Path = "users/admin", Order = -10)] public ActionResult AdminProfile() { // ...code omitted return View(); } // Path with multiple parameters and default value for the last // parameter if its not specified. [UrlRoute(Path = "users/{userId}/posts/{dateRange}")] [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")] [UrlRouteParameterDefault(Name = "dateRange", Value = "all")] public ActionResult UserPostsByTag(int userId, string dateRange) { // ...code omitted return View(); } 

Definizione di UrlRouteAttribute:

 ///  /// Assigns a URL route to an MVC Controller class method. ///  [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class UrlRouteAttribute : Attribute { ///  /// Optional name of the route. If not specified, the route name will /// be set to [controller name].[action name]. ///  public string Name { get; set; } ///  /// Path of the URL route. This is relative to the root of the web site. /// Do not append a "/" prefix. Specify empty string for the root page. ///  public string Path { get; set; } ///  /// Optional order in which to add the route (default is 0). Routes /// with lower order values will be added before those with higher. /// Routes that have the same order value will be added in undefined /// order with respect to each other. ///  public int Order { get; set; } } 

Definizione di UrlRouteParameterConstraintAttribute:

 ///  /// Assigns a constraint to a route parameter in a UrlRouteAttribute. ///  [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class UrlRouteParameterConstraintAttribute : Attribute { ///  /// Name of the route parameter on which to apply the constraint. ///  public string Name { get; set; } ///  /// Regular expression constraint to test on the route parameter value /// in the URL. ///  public string Regex { get; set; } } 

Definizione di UrlRouteParameterDefaultAttribute:

 ///  /// Assigns a default value to a route parameter in a UrlRouteAttribute /// if not specified in the URL. ///  [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class UrlRouteParameterDefaultAttribute : Attribute { ///  /// Name of the route parameter for which to supply the default value. ///  public string Name { get; set; } ///  /// Default value to set on the route parameter if not specified in the URL. ///  public object Value { get; set; } } 

Modifiche a Global.asax.cs:

Sostituisci le chiamate a MapRoute, con una singola chiamata alla funzione RouteUtility.RegisterUrlRoutesFromAttributes:

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

Definizione di RouteUtility.RegisterUrlRoutesFromAttributes:

L’origine completa è in codeplex . Per favore vai al sito se hai dei feedback o segnalazioni di bug.

Puoi anche provare AttributeRouting , che è disponibile da github o via nuget .

Questa è una spina spudorata, poiché sono l’autore del progetto. Ma dang se non sono molto contento di usarlo. Potresti esserlo anche tu. C’è abbondanza di documentazione e codice di esempio nel wiki del repository github.

Con questa libreria, puoi fare molto:

  • Decora le tue azioni con gli attributi GET, POST, PUT e DELETE.
  • Mappare più percorsi a una singola azione, ordinandoli con una proprietà dell’ordine.
  • Specificare i valori predefiniti e i vincoli della rotta usando gli attributi.
  • Specificare parametri facoltativi con un semplice? token prima del nome del parametro.
  • Specificare il nome della rotta per supportare percorsi denominati.
  • Definire le aree MVC su un controller o controller di base.
  • Raggruppa o annida i percorsi insieme utilizzando i prefissi dei percorsi applicati a un controller o controller di base.
  • Supporta gli URL legacy.
  • Imposta la precedenza delle rotte tra i percorsi definiti per un’azione, all’interno di un controller e tra i controller e i controller di base.
  • Generare automaticamente gli url in uscita minuscoli.
  • Definisci le tue convenzioni personalizzate per il percorso e applicale su un controller per generare le rotte per le azioni all’interno del controller senza attributi boilerplate (si pensi allo stile RESTful).
  • Esegui il debug dei percorsi utilizzando HttpHandler in dotazione.

Sono sicuro che ci sono altre cose che sto dimenticando. Controlla. È indolore installare tramite nuget.

NOTA: dal 16/04/12, AttributeRouting supporta anche la nuova infrastruttura API Web. Nel caso tu stia cercando qualcosa in grado di gestirlo. Grazie Subkamran !

1. Scaricare RiaLibrary.Web.dll e fare riferimento nel progetto del sito Web ASP.NET MVC

2. Decorare i metodi del controller con gli attributi [Url]:

 public SiteController : Controller { [Url("")] public ActionResult Home() { return View(); } [Url("about")] public ActionResult AboutUs() { return View(); } [Url("store/{?category}")] public ActionResult Products(string category = null) { return View(); } } 

BTW, ‘?’ l’accesso al parametro “{? categoria}” significa che è facoltativo. Non è necessario specificarlo esplicitamente nelle impostazioni predefinite del percorso, che è uguale a questo:

 routes.MapRoute("Store", "store/{category}", new { controller = "Store", action = "Home", category = UrlParameter.Optional }); 

3. Aggiornare il file Global.asax.cs

 public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoutes(); // This does the trick } protected void Application_Start() { RegisterRoutes(RouteTable.Routes); } } 

Come impostare valori predefiniti e vincoli? Esempio:

 public SiteController : Controller { [Url("admin/articles/edit/{id}", Constraints = @"id=\d+")] public ActionResult ArticlesEdit(int id) { return View(); } [Url("articles/{category}/{date}_{title}", Constraints = "date=(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")] public ActionResult Article(string category, DateTime date, string title) { return View(); } } 

Come impostare l’ordine? Esempio:

 [Url("forums/{?category}", Order = 2)] public ActionResult Threads(string category) { return View(); } [Url("forums/new", Order = 1)] public ActionResult NewThread() { return View(); } 

Questo post è solo per estendere la risposta di DSO.

Durante la conversione dei miei percorsi in attributi, avevo bisogno di gestire l’attributo ActionName. Quindi in GetRouteParamsFromAttribute:

 ActionNameAttribute anAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false) .Cast() .SingleOrDefault(); // Add to list of routes. routeParams.Add(new MapRouteParams() { RouteName = routeAttrib.Name, Path = routeAttrib.Path, ControllerName = controllerName, ActionName = (anAttr != null ? anAttr.Name : methodInfo.Name), Order = routeAttrib.Order, Constraints = GetConstraints(methodInfo), Defaults = GetDefaults(methodInfo), }); 

Inoltre ho trovato che la denominazione del percorso non è adatta. Il nome è creato dynamicmente con controllerName.RouteName. Ma i miei nomi di percorso sono stringhe const nella class controller e io uso quei const per chiamare anche Url.RouteUrl. Ecco perché ho davvero bisogno che il nome della rotta nell’attributo sia il vero nome della rotta.

Un’altra cosa che farò è convertire gli attributi di default e di vincolo in AttributeTargets.Parameter in modo che io possa attaccarli ai parametri.

Ho combinato questi due approcci in una versione di Frankenstein per chiunque lo desideri. (Mi piaceva la notazione param opzionale, ma pensavo anche che dovevano essere attributi separati da default / vincoli piuttosto che tutti mescolati in uno solo).

http://github.com/djMax/AlienForce/tree/master/Utilities/Web/

Avevo bisogno di far funzionare il routing ITCloud in asp.net mvc 2 usando un AsyncController – per farlo basta modificare la class RouteUtility.cs nel sorgente e ricompilare. Devi rimuovere “Completato” dal nome dell’azione alla riga 98

 // Add to list of routes. routeParams.Add(new MapRouteParams() { RouteName = String.IsNullOrEmpty(routeAttrib.Name) ? null : routeAttrib.Name, Path = routeAttrib.Path, ControllerName = controllerName, ActionName = methodInfo.Name.Replace("Completed", ""), Order = routeAttrib.Order, Constraints = GetConstraints(methodInfo), Defaults = GetDefaults(methodInfo), ControllerNamespace = controllerClass.Namespace, }); 

Quindi, in AsyncController, decorare il XXXXCompleted ActionResult con gli attributi UrlRoute e UrlRouteParameterDefault :

 [UrlRoute(Path = "ActionName/{title}")] [UrlRouteParameterDefault(Name = "title", Value = "latest-post")] public ActionResult ActionNameCompleted(string title) { ... } 

Spero che aiuti qualcuno con lo stesso problema.