Autor en Google+
Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

10 años online 🎂

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 6 de mayo de 2014
ASP.NET MVCPues continuamos profundizando en el sistema de rutado por atributos, o attribute routing, recientemente incluido en ASP.NET MVC y Web API. Si aún no conoces este mecanismo, te recomiendo que eches un vistazo a los dos artículos anteriores de la serie:
Como vimos en el último post, en las rutas es posible incluir restricciones a los parámetros de entrada, de forma que si las condiciones especificadas no se cumplen, el sistema de routing descartará la regla y continuará buscando en la tabla de rutas una entrada que encaje con la petición entrante.

Así, la acción Confirm() mostrada a continuación no será invocada ante peticiones como “/user/confirm/1234” o “/user/confirm/abcdef”:
[Route("user/confirm/{pin:alpha:length(4}"] // 4 alphabet chars (a-z)
public ActionResult Confirm(string pin) 
{ 
    ... 
}
Y como también veíamos en el post anterior, MVC y Web API traen de serie un buen número de restricciones (alpha, bool, decimal, int, length, etc.) que podemos emplear directamente sobre nuestras rutas, pero, lo que es mejor, se trata nuevamente de un mecanismo extensible: podemos crear nuestras propias restricciones para attribute routing sin demasiado esfuerzo.

Para no desviarnos del objetivo de este post, nos centraremos en un ejemplo muy simple: queremos implementar una restricción de ruta que deje pasar peticiones sólo si el valor del parámetro al que se aplique es un número par. Es decir, comenzando por el final, pretendemos llegar a usar la restricción “even” como se muestra seguidamente:
[Route("add/{n:even}")]
public ActionResult AddEvenNumber(int n)
{
    return Content("AddEvenNumber executed");
}
Para ello, lo primero que tenemos que hacer es crear una clase que implemente el interfaz IRouteConstraint en la que introduciremos la lógica de validación deseada, por ejemplo de la forma mostrada a continuación. Recordad que el método Match() debe retornar true si la petición cumple la restricción y false en caso contrario:
public class EvenNumberConstraint : IRouteConstraint 
{ 
     public bool Match(HttpContextBase httpContext, Route route,
                       string parameterName, RouteValueDictionary values, 
                       RouteDirection routeDirection) 
     { 
         int result; 
         return (values.ContainsKey(parameterName) 
                && (int.TryParse(values[parameterName] as string, out result)) 
                && result%2 == 0); 
     } 
}
Con esto ya podríamos usar la restricción usando el mapeo por convención (el tradicional) añadiéndola en el parámetro constraints de MapRoute() o MapHttpRoute(). Sin embargo, no estaría aún disponible para el attribute routing porque debemos “mapear” previamente esta nueva clase a la denominación que queremos utilizar en la cadena de texto en la que especificamos la ruta, que más arriba decíamos que era “even”.

El mapeo hay que realizarlo antes de invocar al método MapMvcAttributeRoutes() en MVC para que se tenga en cuenta a la hora de interpretar las rutas especificadas en atributos. Básicamente, lo que se hace es crear un “resolvedor” de constraints en el que asignamos la denominación “even” a la clase EvenNumberConstraint:
// MVC routes: /App_Start/RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
   [...]
   var constraintsResolver = new DefaultInlineConstraintResolver();
   constraintsResolver.ConstraintMap.Add("even", typeof(EvenNumberConstraint)); 
   routes.MapMvcAttributeRoutes(constraintsResolver);

   // Convention based routes:
   routes.MapRoute(
      name: "Default",
      url: "{controller}/{action}/{id}",
      defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
   );
}
En Web API sería prácticamente igual, aunque teniendo en cuenta la diferencia entre los espacios de nombre de ambas plataformas:
// Web API routes: /App_Start/WebApiConfig.cs 
public static void Register(HttpConfiguration config)
{
   [...]

   var constraintsResolver = new DefaultInlineConstraintResolver();
   constraintsResolver.ConstraintMap.Add("even", typeof(EvenNumberConstraint));
   config.MapHttpAttributeRoutes(constraintsResolver);

   // Convention based routes:
   config.Routes.MapHttpRoute(
      name: "DefaultApi",
      routeTemplate: "api/{controller}/{id}",
      defaults: new { id = RouteParameter.Optional }
    );
}
Hecho esto, ya estamos en disposición de utilizar nuestra nueva restricción, de forma totalmente integrada en el sistema de rutado por atributos. En el siguiente código, por ejemplo, una petición como “add/4” sería procesada por el método de acción AddEventNumber(), mientras que “add/3” no cumpliría la restricción y sería ejecutada por AddOddNumber(), la siguiente regla en la tabla de rutas con la que encajaría:
[Route("add/{n:even}")]
public ActionResult AddEvenNumber(int n)
{
    return Content("AddEvenNumber executed");
}

[Route("add/{n:int}")]
public ActionResult AddOddNumber(int n)
{
    return Content("AddOddNumber executed");
}
¡Y esto es todo! Creo que con este post hemos hablado de todo lo necesario para comenzar a utilizar attribute routing de forma efectiva, por lo que la serie sobre este tema finaliza aquí. Al menos, de momento ;-)

Publicado en Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

Aún no hay comentarios, ¡sé el primero!