viernes, 19 de febrero de 2010

Nombres de algunos controladores distintos en ASP.NET MVC

Hola! Un post para comentaros como he implementado una cosilla que necesitaba en ASP.NET MVC (v1). En concreto necesitaba mapear las URLs de tipo /api/{controller}/{action} al controlador especificado, pero con la salvedad de que el nombre del controlador empezaba por War. Es decir la URL /api/Foo/Index debía llamar a la acción del controlador WarFoo (en lugar del controlador Foo).

En resumen lo que quería era:

/Foo/Index –> Llamar a acción Index del controlador Foo

/api/Foo/Index –> llamar a acción Index del controlador WarFoo

/api/WarFoo/Index –> Llamar a acción Index del controlador WarFoo

La primera de las reglas se cumple fácilmente con la definición estándard de la tabla de rutas de MVC:

routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);





Para dar soporte a las otras dos reglas me he creado un RouteHandler propio…



¿Que son los RouteHandlers?



Los RouteHandlers son clases que implementan la interfaz IRouteHandler y que son las encargadas de procesar las peticiones que vienen via una ruta (casi todas en el caso de MVC). Generalmente un RouteHandler lo que hace es crear un IHttpHandler que procese la petición enrutada. ASP.NET MVC viene con un RouteHandler por defecto (MvcRouteHandler) que termina creando un IHttpHandler por defecto (MvcHandler) que es el que crea el controlador asociado y le cede el control. En mi caso el comportamiento de MvcHandler ya me va bien: no quiero cambiar la política de creación de controladores ni nada, solo quiero añadir el prefijo “War” al controlador.



Por suerte, ASP.NET MVC es muy extensible y nos lo pone realmente fácil para añadir un RouteHandler propio: basta con crearlo y vincularlo con una ruta que tengamos en la tabla de rutas.



Veamos primero como vincularíamos nuestro RouteHandler (que he llamado WarRouteHandler) con una ruta, usando el método Add de la tabla de rutas:




routes.Add(new Route("api/{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home", action = "Index", id = "" }),
new WarRouteHandler())
);





El primer parámetro es la URL de la ruta, el segundo los valores de la ruta (a diferencia del método MapRoute que acepta un objeto anónimo para esto, aquí nosotros debemos crear específicamente el RouteValuesDictionary) y finalmente el RouteHandler a utilizar!



Ya casi estamos: Sólo nos falta crear el RouteHandler, para ello creamos una clase que implemente IRouteHandler, y en el método GetHttpHandler (que es el único) vamos a añadir “War” al nombre del controlador. Para saber el nombre del controlador nos basta con acceder a los valores de la ruta (RouteData.Values) donde están todos (controlador, acción, id y otros adicionales que hubiesen). El código es simple:




public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string controller = requestContext.RouteData.Values["controller"].ToString();
if (!controller.ToLowerInvariant().StartsWith("war"))
{
requestContext.RouteData.Values["controller"] = string.Format("War{0}", controller);
}

return new MvcHandler(requestContext);
}





Ahora ya tenemos las URLs de tipo /api/{controlador} enrutadas al controlador War{controlador}.



Un saludo!



PD: Crosspost desde mi blog en geeks.ms (para variar, pásate por allí mejor!)

No hay comentarios: