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)Y como también veíamos en el post anterior, MVC y Web API traen de serie un buen número de restricciones (
public ActionResult Confirm(string pin)
{
...
}
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}")]Para ello, lo primero que tenemos que hacer es crear una clase que implemente el interfaz
public ActionResult AddEvenNumber(int n)
{
return Content("AddEvenNumber executed");
}
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 : IRouteConstraintCon esto ya podríamos usar la restricción usando el mapeo por convención (el tradicional) añadiéndola en el parámetro
{
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);
}
}
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.csEn Web API sería prácticamente igual, aunque teniendo en cuenta la diferencia entre los espacios de nombre de ambas plataformas:
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 }
);
}
// Web API routes: /App_Start/WebApiConfig.csHecho 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
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 }
);
}
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}")]¡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 ;-)
public ActionResult AddEvenNumber(int n)
{
return Content("AddEvenNumber executed");
}
[Route("add/{n:int}")]
public ActionResult AddOddNumber(int n)
{
return Content("AddOddNumber executed");
}
Publicado en Variable not found.