Enrutamiento ASP.NET MVC a través de los atributos del método

En el podcast StackOverflow # 54 , Jeff menciona que registran sus rutas de URL en la base de código de StackOverflow a través de un atributo sobre el método que maneja la ruta. Parece un buen concepto (con la advertencia que Phil Haack planteó sobre las prioridades de la ruta).

¿Podría alguien proporcionar alguna muestra para que esto suceda?

Además, ¿hay alguna “mejores prácticas” para usar este estilo de enrutamiento?

ACTUALIZACIÓN : Esto ha sido publicado en codeplex . El código fuente completo, así como el ensamblado precomstackdo están disponibles para su descarga. No he tenido tiempo de publicar la documentación en el sitio todavía, por lo que esta publicación SO tendrá que ser suficiente por ahora.

ACTUALIZACIÓN : agregué algunos atributos nuevos para manejar 1) orden de ruta, 2) restricciones de parámetros de ruta, y 3) valores predeterminados de parámetros de ruta. El texto a continuación refleja esta actualización.

De hecho, he hecho algo así para mis proyectos de MVC (no tengo idea de cómo Jeff lo está haciendo con stackoverflow). Definí un conjunto de atributos personalizados: UrlRoute, UrlRouteParameterConstraint, UrlRouteParameterDefault. Se pueden unir a los métodos de acción del controlador MVC para hacer que las rutas, restricciones y valores predeterminados se vinculen a ellos automáticamente.

Ejemplo de uso:

(Tenga en cuenta que este ejemplo es un tanto artificial, pero demuestra la característica)

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

Definición de 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; } } 

Definición de 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; } } 

Definición de 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; } } 

Cambios en Global.asax.cs:

Reemplazar llamadas a MapRoute, con una sola llamada a la función RouteUtility.RegisterUrlRoutesFromAttributes:

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

Definición de RouteUtility.RegisterUrlRoutesFromAttributes:

La fuente completa está arriba en codeplex . Vaya al sitio si tiene comentarios o informes de errores.

También puede probar AttributeRouting , que está disponible desde github o vía nuget .

Este es un enchufe desvergonzado, ya que soy el autor del proyecto. Pero dang si no estoy muy feliz de usarlo. Tú también puedes serlo Hay una gran cantidad de documentación y código de muestra en la wiki del repository github.

Con esta biblioteca, puedes hacer mucho:

  • Decora tus acciones con los atributos GET, POST, PUT y DELETE.
  • Asigne múltiples rutas a una sola acción, ordenándolas con una propiedad Order.
  • Especifique los valores predeterminados de ruta y las restricciones utilizando atributos.
  • Especifique params opcionales con un simple? token antes del nombre del parámetro.
  • Especifique el nombre de la ruta para admitir rutas con nombre.
  • Defina áreas MVC en un controlador o controlador base.
  • Agrupe o jerarquice sus rutas utilizando los prefijos de ruta aplicados a un controlador o controlador base.
  • Admite URL heredadas.
  • Establezca la precedencia de las rutas entre las rutas definidas para una acción, dentro de un controlador y entre controladores y controladores base.
  • Genera urls de salida minúsculas automáticamente.
  • Defina sus propias convenciones de ruta personalizadas y aplíquelas en un controlador para generar las rutas para las acciones dentro del controlador sin los atributos repetitivos (piense en el estilo RESTful).
  • Depure sus rutas utilizando un HttpHandler suministrado.

Estoy seguro de que hay algunas otras cosas que estoy olvidando. Echale un vistazo. Es fácil de instalar a través de nuget.

NOTA: A partir del 16/04/12, AttributeRouting también admite la nueva infraestructura de API web. En caso de que estés buscando algo que pueda manejar eso. Gracias Subkamran !

1. Descargue RiaLibrary.Web.dll y hágalo referencia en su proyecto de sitio web ASP.NET MVC

2. Decoree los métodos de controlador con los atributos [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(); } } 

Por cierto, ‘?’ iniciar sesión en el parámetro ‘{? category}’ significa que es opcional. No será necesario que especifique esto explícitamente en los valores predeterminados de la ruta, que es igual a esto:

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

3. Actualice el archivo 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); } } 

¿Cómo establecer los valores predeterminados y las restricciones? Ejemplo:

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

¿Cómo establecer el orden? Ejemplo:

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

Esta publicación es solo para extender la respuesta de DSO.

Al convertir mis rutas a atributos, necesitaba manejar el atributo ActionName. Entonces en 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), }); 

También encontré que el nombre de la ruta no es adecuado. El nombre está construido dinámicamente con controllerName.RouteName. Pero mis nombres de ruta son cadenas const en la clase de controlador y uso esos const para también llamar a Url.RouteUrl. Es por eso que realmente necesito que el nombre de la ruta en el atributo sea el nombre real de la ruta.

Otra cosa que haré es convertir los atributos predeterminados y de restricción a AttributeTargets.Parameter para poder pegarlos en params.

He combinado estos dos enfoques en una versión de Frankenstein para cualquiera que lo quiera. (Me gustó la notación de param opcional, pero también pensé que deberían ser atributos separados de los valores predeterminados / restricciones en lugar de todos mezclados en uno).

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

Necesitaba hacer que el enrutamiento de ITCloud funcionara en asp.net mvc 2 usando un AsyncController; para hacerlo, simplemente edite la clase RouteUtility.cs en el origen y recompile. Tienes que quitar el “Completado” del nombre de la acción en la línea 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, }); 

Luego, en AsyncController, decore el XXXXCompleted ActionResult con los familiares atributos UrlRoute y UrlRouteParameterDefault :

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

Espero que ayude a alguien con el mismo problema.