martes, 15 de junio de 2010
Aunque las convenciones propuestas por el framework ASP.NET MVC nos ayudan a estructurar nuestras aplicaciones y, la mayoría de veces, a ser más productivos, de vez en cuando también nos obligan a introducir código repetitivo para respetar el patrón propuesto.
Por ejemplo, en el caso de controladores con acciones que retornan la vista por defecto, solemos utilizar un código como el siguiente:
No es que sea algo terrible, pero en controladores con muchas acciones de este tipo puede resultar un poco pesado… ¿no podríamos hacer algo para mejorar esto?
Una vía muy rápida para hacerlo es sobrescribir el método
Recordemos que cuando una petición llega a un controlador, se ejecuta el método cuyo nombre coincide con el de la acción invocada; en caso de no existir, el framework llamada a
Para ello, basta con crear una clase base llamada
¿Sencillo, no? Lo único que hacemos en el código es utilizar la colección de
Si no es posible localizar una vista para la acción invocada, se ejecutará el tratamiento por defecto para esta situación, que no es más que el lanzamiento de una excepción de tipo
¡Y eso es todo! A partir de este momento, todas las clases controlador que hereden de
Por ejemplo, el controlador que escribíamos al comienzo de este post podría quedar de la siguiente forma, bastante más compacta:
Cada petición recibida por el controlador que no se encuentre implementada de forma explícita será procesada con el automatismo introducido en
Por ejemplo, sería posible acceder a vistas directamente, sólo con saber su nombre, lo que en algunos escenarios puede resultar peligroso desde el punto de vista de la seguridad del sistema.
Obviamente, la técnica tampoco será válida en aquellas ocasiones en que la vista espere recibir algún tipo de información del controlador (como datos de vista, o la indicación de utilización de una página maestra concreta), o cuando la acción deba estar decorada con un filtro.
En estos casos, estas acciones concretas deberán seguir siendo implementadas explícitamente en el controlador, aunque el resto puedan seguir siendo procesadas de forma automática:
En resumen, en este post hemos estudiado una técnica que nos permite crear controladores capaces de aportar un comportamiento por defecto para todas las peticiones realizadas hacia el mismo, ahorrándonos la escritura de acciones que simplemente retornan la vista por defecto.
Y aunque, como todo, en la práctica presenta limitaciones y peligros, su uso puede ser interesante en sitios web sin grandes requisitos de seguridad, como webs de información pública, cuyos controladores sean principalmente del tipo descrito.
Publicado en: Variable not found.
Por ejemplo, en el caso de controladores con acciones que retornan la vista por defecto, solemos utilizar un código como el siguiente:
public class HomeController: Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Company()
{
return View();
}
public ActionResult Services()
{
return View();
}
public ActionResult Contact()
{
return View();
}
}
No es que sea algo terrible, pero en controladores con muchas acciones de este tipo puede resultar un poco pesado… ¿no podríamos hacer algo para mejorar esto?
Controladores automáticos
Si observamos el código del controlador anterior, vemos que la lógica de ejecución es muy simple: cada acción implementada retorna al usuario la vista que le corresponde según convención. Obviamente se trata de un comportamiento que puede ser generalizado jugando con los mecanismos de extensión del framework.Una vía muy rápida para hacerlo es sobrescribir el método
HandleUnknownAction()
de la clase Controller
, descrito por aquí hace tiempo, que nos permite procesar las peticiones realizadas a acciones no existentes en el controlador.Recordemos que cuando una petición llega a un controlador, se ejecuta el método cuyo nombre coincide con el de la acción invocada; en caso de no existir, el framework llamada a
HandleUnknownAction()
, permitiéndonos tomar el control de la situación. En nuestro caso, podríamos introducir en este método la lógica para retornar al usuario, de forma automática, una vista cuyo nombre coincide con el de la acción, siguiendo la convención de nombrado estándar.Para ello, basta con crear una clase base llamada
AutoController
, e introducir el siguiente código para tener el problema resuelto:public class AutoController : Controller
{
protected override void HandleUnknownAction(string actionName)
{
// Intentamos localizar una vista con el nombre de la acción...
ViewEngineResult viewResult = ViewEngines.Engines.FindView
(this.ControllerContext, actionName, null) ;
if (viewResult.View != null)
{
View(actionName).ExecuteResult(ControllerContext);
return;
}
// Si no hemos encontrado nada, seguimos el
// comportamiento por defecto…
base.HandleUnknownAction(actionName);
}
¿Sencillo, no? Lo único que hacemos en el código es utilizar la colección de
ViewEngines
para buscar una vista cuyo nombre coincida con el de la acción que se está intentando ejecutar, retornándola al usuario cuando sea posible localizarla.Si no es posible localizar una vista para la acción invocada, se ejecutará el tratamiento por defecto para esta situación, que no es más que el lanzamiento de una excepción de tipo
HttpException
con el error 404 (not found).¡Y eso es todo! A partir de este momento, todas las clases controlador que hereden de
AutoController
incluirán este comportamiento, por lo que será posible obviar la implementación de métodos cuya única misión sea retornar la vista por defecto según convención.Por ejemplo, el controlador que escribíamos al comienzo de este post podría quedar de la siguiente forma, bastante más compacta:
public class HomeController: AutoController
{
}
Pero ojito, que no es oro todo lo que reluce…
Eso sí, antes de usar esta técnica tenemos que tener claro lo que significa realmente para no toparnos con sorpresas desagradables.Cada petición recibida por el controlador que no se encuentre implementada de forma explícita será procesada con el automatismo introducido en
HandleUnknowAction()
sin pasar por ningún tipo de filtro, ni enviar información alguna en el ViewData
.Por ejemplo, sería posible acceder a vistas directamente, sólo con saber su nombre, lo que en algunos escenarios puede resultar peligroso desde el punto de vista de la seguridad del sistema.
Obviamente, la técnica tampoco será válida en aquellas ocasiones en que la vista espere recibir algún tipo de información del controlador (como datos de vista, o la indicación de utilización de una página maestra concreta), o cuando la acción deba estar decorada con un filtro.
En estos casos, estas acciones concretas deberán seguir siendo implementadas explícitamente en el controlador, aunque el resto puedan seguir siendo procesadas de forma automática:
public class HomeController: AutoController
{
// GET /Home/Customers
// Sólo para usuarios registrados
[Authorize]
public ActionResult Customers()
{
return View();
}
// GET /Home/References
public ActionResult References()
{
var services = new AppServices();
return View(services.GetReferences());
}
// GET /Home/{CualquierOtraCosa}
// Procesado por defecto, retorna la vista {CualquierOtraCosa}
}
En resumen, en este post hemos estudiado una técnica que nos permite crear controladores capaces de aportar un comportamiento por defecto para todas las peticiones realizadas hacia el mismo, ahorrándonos la escritura de acciones que simplemente retornan la vista por defecto.
Y aunque, como todo, en la práctica presenta limitaciones y peligros, su uso puede ser interesante en sitios web sin grandes requisitos de seguridad, como webs de información pública, cuyos controladores sean principalmente del tipo descrito.
Publicado en: Variable not found.
Aún no hay comentarios, ¡sé el primero!
Enviar un nuevo comentario