2009-05-21 16 views
80

En el StackOverflow Podcast #54, Jeff menciona que registran sus rutas URL en la base de código 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).Enrutamiento ASP.NET MVC A través de los atributos del método

¿Alguien podría proporcionar alguna muestra para que esto suceda?

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

Respuesta

62

ACTUALIZACIÓN: Esto ha sido publicado en codeplex. El código fuente completo, así como el ensamblado precompilado 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: He añadido algunos nuevos atributos para manejar 1) de pedido ruta, 2) las limitaciones de los parámetros de ruta, y 3) valores por defecto parámetro de ruta. El texto a continuación refleja esta actualización.

De hecho, he hecho algo como esto 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. uso

Ejemplo:

(Nota este ejemplo es algo 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:

/// <summary> 
/// Assigns a URL route to an MVC Controller class method. 
/// </summary> 
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)] 
public class UrlRouteAttribute : Attribute 
{ 
    /// <summary> 
    /// Optional name of the route. If not specified, the route name will 
    /// be set to [controller name].[action name]. 
    /// </summary> 
    public string Name { get; set; } 

    /// <summary> 
    /// 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. 
    /// </summary> 
    public string Path { get; set; } 

    /// <summary> 
    /// 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. 
    /// </summary> 
    public int Order { get; set; } 
} 

Definición de UrlRouteParameterConstraintAttrib UTE:

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

    /// <summary> 
    /// Regular expression constraint to test on the route parameter value 
    /// in the URL. 
    /// </summary> 
    public string Regex { get; set; } 
} 

Definición de UrlRouteParameterDefaultAttribute:

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

    /// <summary> 
    /// Default value to set on the route parameter if not specified in the URL. 
    /// </summary> 
    public object Value { get; set; } 
} 

Cambios en Global.asax.cs:

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

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

     RouteUtility.RegisterUrlRoutesFromAttributes(routes); 
    } 

Definición de RouteUtility.RegisterUrlRoutesFromAttributes:

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

+0

Supongo que hacerlo con atributos impide el uso de rutas predeterminadas y restricciones de ruta ... –

+0

Con este enfoque, nunca he necesitado rutas predeterminadas porque está vinculando cada ruta a un método específico. Tienes razón sobre las restricciones. Busqué la posibilidad de agregar restricciones como propiedad de atributo, pero me encontré con que las restricciones MVC se especifican utilizando objetos anónimos y las propiedades de los atributos solo pueden ser simples. Todavía es posible, creo que hacer restricciones como un atributo (con más codificación), pero no me he molestado todavía porque realmente no he necesitado restricciones en mi trabajo de MVC hasta este punto (tiendo a validar valores de ruta) en el controlador). – DSO

+0

Me refiero a los valores predeterminados para los elementos en la ruta de la ruta como {nombre de usuario} pero también me doy cuenta de que suelen ser primitivos. –

3

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

Al convertir mis rutas a atributos, necesitaba manejar el atributo ActionName.Así que en GetRouteParamsFromAttribute:

ActionNameAttribute anAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false) 
    .Cast<ActionNameAttribute>() 
    .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é la denominación 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.

+0

Sí, como que oscino en el comportamiento de nomenclatura de ruta. Probablemente sea mejor hacer lo que hiciste, solo usa lo que está en el atributo tal como está o hazlo nulo. Buena idea poniendo las restricciones por defecto en los params. Probablemente lo publique en codeplex en algún momento para gestionar mejor los cambios. – DSO

9

1. Descargar RiaLibrary.Web.dll y hacer referencia a él en su proyecto de sitio web ASP.NET MVC

2. Métodos controlador Decoreate con el [url] Atributos:

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. Usted no tendrá que especificar explícitamente en los valores predeterminados de ruta, que es igual a este: Archivo

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

3. Actualización 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 valores predeterminados y 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 pedido? Ejemplo:

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

[Url("forums/new", Order = 1)] 
public ActionResult NewThread() 
{ 
    return View(); 
} 
+1

¡Muy bonito! Me gusta especialmente la nomenclatura '{? Param}' para los parámetros opcionales. –

+0

+1 nomenclatura :) – GONeale

0

He combinado estos dos enfoques en una versión de Frankenstein para cualquier persona que lo desee. (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/

0

que necesitaba para conseguir el encaminamiento ITCloud trabajando en asp.net mvc 2 utilizando un AsyncController - para hacerlo, basta con editar la clase RouteUtility.cs en la fuente y recompilar. Usted tiene que quitarse el "Completado" en el 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 el AsyncController, decorar la XXXXCompleted ActionResult con los familiares UrlRoute y UrlRouteParameterDefault atributos:

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

la esperanza de que ayuda a alguien con el mismo problema

+0

Para su información, la convención es tener los atributos relacionados MVC en el método ActionNameAsync y no el método ActionNameCompleted. –

+0

Gracias - no me di cuenta. – TimDog

44

También puede probar AttributeRouting, que está disponible en github o en 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 el repositorio de github wiki.

Con esta biblioteca, se puede hacer mucho:

  • decorar sus acciones con GET, POST, PUT y DELETE atributos.
  • Asigne múltiples rutas a una sola acción y solicítelas con una propiedad de Pedido.
  • Especifique los valores predeterminados y las restricciones de la ruta mediante atributos.
  • Especifique parámetros 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 juntas utilizando los prefijos de ruta aplicados a un controlador o controlador base.
  • Soporta 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.
  • Generar urls de salida minúsculas automáticamente.
  • Defina sus propias convenciones de ruta personalizadas y aplíquelas en un controlador para generar las rutas de 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 me olvido. 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é buscando algo que pueda manejar eso. Thanks subkamran!

+10

Este proyecto parece más maduro (mejor documentación, más funciones, suite de pruebas completa) que las otras opciones mencionadas –

+3

Estoy de acuerdo, esto parece hacer todo lo que pueda desear, y con buena documentación de ejemplo. –

+3

Muchas gracias. Estoy felizmente usando esta solución y ha resuelto todos mis conflictos de enrutamiento, ambigüedad y confusión. –

Cuestiones relacionadas