De descanso

Lanzarote Tras un año duro (¡muy duro!), llegan de nuevo las vacaciones: tiempo de descansar un poco, disfrutar con la familia, formatearse mentalmente y recargar las pilas con vistas a comenzar un nuevo periodo tras el verano. Y todo ello alternando entre Sanlúcar de Barrameda, El Portil, y una escapada a Lanzarote que seguro que será una delicia. No me puedo quejar, la verdad.

Variable Not Found quedará, una vez más, a la deriva hasta que regrese dentro de unas semanas. Además, para evitar que los spammers se vengan a vivir aquí, desactivaré temporalmente el envío de comentarios anónimos, lamento las molestias que pueda causar.

Feliz verano, amigos. 🙂

Publicado en: Variable not found.

Aspectos a tener en cuenta al crear sitios web públicos

 

image Hace unos meses, un usuario de StackOverflow planteaba una interesante cuestión: ¿qué debería saber un desarrollador para construir un sitio web público? Es decir, ¿cuáles son aquellos aspectos importantes que deben tenerse en cuenta a la hora de crear un site de calidad, desde el punto de vista técnico?

Tras un tiempo de respuestas, ideas y debates, otro usuario ha realizado una recopilación de los aspectos y sugerencias más votadas y los ha publicado en forma de lista categorizada, donde podemos encontrar muy buenas ideas a tener en cuenta en nuestros propios desarrollos, y que me he permitido traducir.

Muchos de los puntos son obvios y seguro que ya los estáis teniendo en cuenta, quizás otros son demasiado exagerados, y seguro que alguno de ellos ni siquiera os los habíais planteado. En cualquier caso el resultado es una relación interesante y muy a tener en cuenta para mejorar nuestros sitios web.

Interfaz y experiencia de usuario

  • Ser consciente de que los navegadores implementan los estándares de forma diferente y asegurarse de que el sitio web funciona razonablemente bien en la mayoría de los principales navegadores. Como mínimo, sería necesario probarlo con un navegador que utilice un motor reciente Gecko (Firefox), Webkit (Safari, Chrome y algunos navegadores móviles), las versiones soportadas de Internet Explorer, y Opera.
  • Tener en cuenta que el sitio web puede ser visitado utilizando medios distintos a los navegadores habituales, como por ejemplo teléfonos móviles, lectores de pantalla, o motores de búsqueda. Usar estándares de accesibilidad como WAI o Section508.
  • Considerar los mecanismos de actualización del sitio web para que estos procesos no afecten a los usuarios una vez que el sistema está en marcha y puedan producirse de forma suave y transparente. Por ejemplo, puede ayudar el mantener entornos de prueba paralelos, el uso de herramientas de control del código fuente, o mecanismos de builds automatizados.
  • No mostrar errores directamente al usuario.
  • No incluir en las páginas direcciones de correo de usuarios en texto plano, para evitar que sean bombardeados por los spammers.
  • Incluir límites razonables de utilización del sitio para evitar malos usos por parte de usuarios o procesos automáticos (como puede ser los virus). Por ejemplo, es razonable que un sistema de correo electrónico gratuito limite el número de mensajes diarios que puede enviar un usuario, aunque el número máximo sea muy alto; otro ejemplo podemos verlo en Google, que muestra un mensaje de error cuando detecta demasiado tráfico hacia sus servidores desde una única dirección IP.

Seguridad

  • Conocer la amplia guía de desarrollo OWASP, que  cubre la seguridad de sitios web de forma muy completa.
  • Conocer el fundamento de los ataques de inyección SQL y cómo prevenirlos.
  • Jamás confiar en los datos introducidos por los usuarios.
  • Evitar el almacenamiento de contraseñas en texto plano utilizando técnicas criptográficas como hashes y salts.
  • No intentes utilizar tu magnífico y elaborado sistema de autenticación; es bastante probable que existan fallos impredecibles de los que sólo te darás cuenta después de haber sido hackeado.
  • Usar SSL/HTTPS en las páginas de identificación de usuarios y, en general, en todas aquellas páginas donde sea introducida información sensible, como datos personales o bancarios.
  • Evitar el secuestro de sesiones (session hijacking).
  • Evitar los ataques XSS (Cross Site Scripting).
  • Evitar los ataques XSRF (Cross Site Request Forgeries).
  • Mantener tus sistemas actualizados con los últimos parches disponibles.
  • Asegurarse de que la información de conexión a la base de datos está almacenada en un lugar lo suficientemente seguro.
  • Mantener informado sobre las últimas técnicas de ataque y vulnerabilidades que afecten a la plataforma sobre la que trabajas.
  • Conocer el manual The Google Browser Security Handbook.

Rendimiento

  • Implementar el cacheado de páginas cuando sea necesario. Comprender y usar apropiadamente los mecanismos de cacheo HTTP.
  • Optimizar las imágenes. Por ejemplo, no utilizar una imagen de 20 Kb. como mosaico de fondo.
  • Conocer cómo comprimir el contenido de las páginas con gzip.
  • Echar un vistazo al sitio Yahoo Exceptional Performance, donde se muestran directrices y buenas prácticas para mejorar el rendimiento de sitios web. Utilizar herramientas como YSlow.
  • Utilizar la técnica de CSS Sprites para las pequeñas imágenes (como las que encontramos en las barras de herramientas), con objeto de minimizar el número de peticiones HTTP.
  • Los sitios web de alto tráfico deberían considerar el despliegue de componentes en distintos dominios para optimizar la descarga en paralelo de los mismos.
  • En general, minimizar el número total de peticiones HTTP necesarias para que el navegador muestre las páginas.

SEO

  • Utilizar direcciones URL amigables para los buscadores. Por ejemplo, utilizar direcciones del tipo «ejemplo.com/paginas/titulo-del-articulo» en lugar de «ejemplo.com/index.php?page=45».
  • No utilizar enlaces que digan «pulse aquí». Estarías creando sitio web poco optimizado para buscadores, a la vez que complicando las cosas para los usuarios que utilizan lectores de pantalla.
  • Crear un mapa del sitio en XML (sitemap).
  • Utilizar <link rel="canonical" ... /> Cuando tengas múltiples URLs que apunten a un mismo contenido.
  • Utilizar las herramientas disponibles en www.google.com/webmasters.
  • Instalar Google Analytics desde el principio.
  • Conocer cómo funcionan los rastreadores de los buscadores y el archivo robots.txt.
  • No maquetar con tablas; Google generalmente valorará positivamente el marcado HTML semántico y la maquetación con CSS.
  • Si tienes contenido no textual en la página, utiliza en el sitemap las extensiones de Google para audio, video, etc. Hay alguna información sobre ello en la respuesta de Tim Farley.

Tecnología

  • Entender el protocolo HTTP; conocer cosas como GET, POST, sesiones, cookies, y saber lo que significa e implica su naturaleza «sin estado» (stateless).
  • Escribir el código (X)HTML y CSS conformes a las especificaciones de la W3C, y asegurarse de que validan. El objetivo es evitar las particularidades de los navegadores, a la vez que se facilita enormemente la navegación utilizando browsers no estándar como lectores de pantalla o dispositivos móviles.
  • Comprender cómo se procesa el código javascript en los navegadores. Mover los scripts al final de las páginas.
  • Comprender cómo funciona el sandbox de javascript, especialmente si pretendes utilizar iframes.
  • Asegurarse de que javascript puede ser deshabilitado sin que la página deje de funcionar. AJAX debe ser una extensión, y no la base sobre la que se construya un sitio. Aunque la mayoría de usuarios lo tengan activado, recordar que existen muchos y muy populares dispositivos en los que no funcionará correctamente.
  • Entender la diferencia entre las reflexiones 301 y 302 (esto también es un aspecto SEO).
  • Aprender tanto como sea posible sobre la plataforma en la que será desplegado el sitio web en producción.
  • Considerar el uso de un reseteador de CSS.
  • Considerar herramientas como jQuery, que oculta muchas de las particularidades de los distintos navegadores utilizando javascript para la manipulación del DOM.

Corrección de errores

  • Entender que pasarás el 20% del tiempo codificando y el 80% restante manteniéndolo, por tanto codifica apropiadamente.
  • Configurar un buen sistema de notificación y gestión de errores.
  • Habilitar sistemas para que los usuarios puedan contactar contigo y trasladarte críticas y sugerencias.
  • Documentar cómo funciona la aplicación para facilitar el futuro soporte y mantenimiento del sistema.
  • Poner a funcionar el sistema primero en Firefox y después en Internet Explorer.
  • Hacer copias de seguridad frecuentes.

Publicado en: Variable not found.

Cómo crear un ControllerFactory personalizado para ASP.NET MVC

 

Piezas de Lego Hace unas semanas, en el post Cambiar la ubicación de las vistas en ASP.NET MVC estuvimos viendo cómo era posible aprovechar la flexibilidad del framework para saltarse las convenciones respecto a la ubicación de los archivos de vistas (.aspx, .ascx y .master).

En esta ocasión vamos a seguir profundizando en los puntos de extensión del framework, centrándonos en la facilidad con la que podemos modificar el ControllerFactory, que es el componente encargado de localizar y crear las instancias de los controladores que deben procesar las peticiones.

Y vamos a ilustrarlo con un ejemplo, en el que desmontaremos otra de las convenciones del framework MVC: el nombre de los controladores. Como sabemos, cuando se recibe una petición que según su ruta debe ser procesada por un controlador llamado XYZ, el motor por defecto intentará instanciar una clase denominada XYZController.  Lo que vamos a hacer es retocar este comportamiento para que nuestros controladores puedan denominarse “ControladorDeXYZ”, un nombre mucho más acertado en nuestro idioma.

En este post veremos cómo podemos modificar la forma en que este componente localiza las clases de controladores y adaptarla a nuestras preferencias.

Pero antes, recuerda:

Saltarse las convenciones = malo

Hazlo sólo cuando realmente esté justificado… o por diversión, como es el caso 😉

Lo que vamos a hacer en el post no es una implementación eficiente, y por supuesto no es válida para un entorno de producción; se trata simplemente de un experimento que demuestra la facilidad para sustituir ciertos componentes del framework, y la flexibilidad que nos aporta. Los conceptos que vamos a tratar pueden ser utilizados con otros fines, como facilitar la inyección de dependencias en controladores, que probablemente veamos en un futuro post.

Y ahora, al tema…

1. El proceso básico de la petición

Antes de empezar la faena, es conveniente estudiar un poco el funcionamiento del framework; así podremos entender mejor qué estamos tocando.

Cuando se recibe una petición, su tratamiento pasa a través de una serie de componentes que se encargan de procesarla a distintos niveles siguiendo el ciclo de vida definido para el framework. Así, una vez que el sistema de enrutamiento ha terminado su tarea detectando la información presente en la URL y determinando, por tanto, su destino, utilizará un manejador MvcHandler para continuar procesando la petición desde el método ProcessRequest().

La porción de código de ASP.NET MVC framework donde se define este método es la siguiente (en el archivo MvcHandler.cs, recordad que el código fuente está disponible en Codeplex):

   1: protected internal virtual void ProcessRequest(HttpContextBase httpContext) 
   2: {
   3:     [...]
   4:     
   5:     // Get the controller type
   6:     string controllerName = 
   7:         RequestContext.RouteData.GetRequiredString("controller");
   8:     
   9:     // Instantiate the controller and call Execute
  10:     IControllerFactory factory = 
  11:         ControllerBuilder.GetControllerFactory();
  12:             
  13:     IController controller = 
  14:         factory.CreateController(RequestContext, controllerName);
  15:             
  16:     if (controller == null) 
  17:     {
  18:         throw new InvalidOperationException(...); // Simplificado
  19:     }
  20:     try 
  21:     {
  22:         controller.Execute(RequestContext);
  23:     }
  24:     finally 
  25:     {
  26:         factory.ReleaseController(controller);
  27:     }
  28: }

Resumidamente, podríamos decir que el sistema procesa las peticiones siguiendo estos pasos:

  • en primer lugar, obtiene el nombre del controlador desde el contexto de la petición (normalmente desde la ruta). En el código anterior, se guarda en la variable controllerName.
  • a continuación obtiene una instancia de la factoría de controladores desde la clase ControllerBuilder.
  • una vez obtenida la factoría según el paso anterior, se le pide mediante una llamada a su método CreateController() que cree el controlador que procesará la petición, enviándole como parámetro el nombre obtenido anteriormente.
  • y por último, se invoca al método Execute del controlador para que realice las acciones oportunas, liberando al final los recursos utilizados mediante una llamada a ReleaseController.

Como en otras muchas partes del framework, se puede observar en el código anterior la utilización de interfaces en lugar de clases para los elementos que intervienen en el proceso, lo que da pistas sobre la flexibilidad de ASP.NET MVC: casi cualquier elemento puede ser sustituido por otro siempre que se cumpla el contrato definido.

2. La factoría de controladores, a corazón abierto

Visto el procedimiento anteriormente descrito, está claro que la lógica de localización e instanciación del controlador es responsabilidad de las factorías de controladores, y más exactamente del método CreateController() que éstas deben implementar.

ASP.NET MVC utiliza como factoría la clase DefaultControllerFactory  de forma predeterminada, sin embargo, nada en el código anterior obliga a que esto sea así. Si logramos crear una nueva factoría que implemente el interfaz IControllerFactory e indicamos al framework que la utilice, ya casi tendremos el trabajo hecho.

En el código de la clase DefaultControllerFactory (la factoría predeterminada), se puede observar que el proceso de creación está compuesto por dos pasos; en el primero de ellos se obtiene el tipo de controlador mediante una llamada a GetControllerType(), mientras que en el segundo se obtiene una instancia de dicho tipo llamando a GetControllerInstance(), que es la que retorna el método:

   1: public virtual IController 
   2:     CreateController(RequestContext requestContext, string controllerName) 
   3: {
   4:     [...] // Simplificado
   5:     Type controllerType = GetControllerType(controllerName);
   6:     IController controller = GetControllerInstance(controllerType);
   7:     return controller;
   8: }

Dado que lo único que queremos sustituir es el mecanismo de localización, lo más sencillo es crear nuestra nueva factoría, a la que llamaremos AcmeControllerFactory, heredando de DefaultControllerFactory y sobrescribir el método GetControllerType, que afortunadamente es virtual.

El código completo para esta nueva clase es el siguiente:

   1: public class AcmeControllerFactory: DefaultControllerFactory
   2: {
   3:   protected override Type GetControllerType(string controllerName)
   4:   {
   5:       Type r = base.GetControllerType(controllerName);
   6:       if (r == null)
   7:       {
   8:           Assembly current = Assembly.GetExecutingAssembly();
   9:           r = (from t in current.GetTypes()
  10:                where
  11:                (
  12:                        t.Name.Equals("ControladorDe" + controllerName,
  13:                                       StringComparison.InvariantCultureIgnoreCase)
  14:                   && t.IsPublic
  15:                   && !t.IsAbstract 
  16:                   && typeof(IController).IsAssignableFrom(t)
  17:                )
  18:                select t).FirstOrDefault();
  19:       }
  20:       return r;
  21:   }
  22: }

Como podemos observar, lo primero que hacemos es intentar localizar el controlador con el nombre que nos llega en el parámetro, pero utilizando el método propuesto por la factoría por defecto, es decir, llamando al método GetControllerType() original. Es decir, vamos a ser al menos algo respetuosos con las convenciones de nombrado existentes 😉 … primero intentaremos localizar el controlador ciñéndonos a ellas, y sólo si no tenemos éxito aplicaremos nuestro propio criterio de nombrado.

Así, ante una petición rutada hacia el controlador Cliente, primero buscaremos la clase ClienteController (según la convención), y si no existe es cuando intentaremos localizar un tipo llamado ControladorDeCliente. Obviamente, podríamos hacerlo al revés, es decir, buscar primero según nuestro criterio de nombrado y si no tenemos éxito buscar utilizando el estándar, o bien ni siquiera intentar esta última opción.

Por tanto, si el método por defecto no es capaz de encontrar un controlador correspondiente a dicha denominación es cuando ejecutamos nuestra lógica adicional, que consiste en buscar en el ensamblado actual, mediante una consulta LINQ, aquellas clases cuyo nombre corresponda con el patrón que pretendemos emplear (“ControladorDe”+controllerName), y que sean públicas, instanciables, e implementen el interfaz IController.

No se han implementado optimizaciones, como la inclusión de un caché, que permitan mejorar el rendimiento en esta consulta. Cada petición deberá recorrer, en el peor de los casos, todo el ensamblado en busca de un controlador apropiado.

Tampoco se ha implementado ningún tipo de resolución de conflictos; si existe más de un controlador con el mismo nombre en distintos namespaces, se retornará el primero que se encuentre.

Eso os lo dejo de deberes 😉

3. Sustituir la factoría de controladores

Ya tenemos creada la clase AcmeControllerFactory, nuestra propia factoría de controladores, y está lista para entrar en acción. Pero, ¿cómo le decimos al framework que queremos utilizar una factoría de controladores distinta a la predeterminada?

Volvamos a estudiar el código de procesado de las peticiones que vimos anteriormente. Como se puede observar, el IControllerFactory a utilizar es obtenido desde la clase ControllerBuilder:

   1: protected internal virtual void ProcessRequest(HttpContextBase httpContext) 
   2: {
   3:   [...]
   4:   // Instantiate the controller and call Execute
   5:   IControllerFactory factory = 
   6:             ControllerBuilder.GetControllerFactory();
   7:             
   8:   IController controller = 
   9:             factory.CreateController(RequestContext, controllerName);
  10:     [...]
  11: }

Y aquí de nuevo entra en juego la flexibilidad del diseño de ASP.NET MVC: ControllerBuilder viene preparada para que podamos sustituir muy fácilmente la factoría a instanciar, por lo que podemos indicarle el tipo concreto que queremos que utilice.

En el siguiente código estamos indicando al ControllerBuilder que nuestra factoría por defecto, es decir, la clase encargada de buscar e instanciar los controladores, será AcmeControllerFactory. Como  se define en la inicialización de la aplicación (Application_Start del global.asax), todas las peticiones utilizarán esta factoría para crear los controladores:

   1: protected void Application_Start()
   2: {
   3:     RegisterRoutes(RouteTable.Routes);
   4:     ControllerBuilder
   5:         .Current
   6:         .SetControllerFactory(typeof(AcmeControllerFactory));
   7: }

4. Probar que realmente funciona

Podemos comprobar el funcionamiento de todo este invento de forma muy sencilla. Una vez seguidos los pasos anteriores, basta con crear un nuevo controlador que ya atienda a nuestra propia convención de nombrado, como el siguiente:

   1: [HandleError]
   2: public class ControladorDeCliente : Controller
   3: {
   4:   public ActionResult Listar()
   5:   {
   6:       return View();
   7:   }
   8:  
   9:   public ActionResult Editar()
  10:   {
  11:       return View();
  12:   }
  13: }

Creamos, asimismo, una vista “Listar.aspx” y otra “Editar.aspx” en la carpeta “ViewsCliente” para que el motor pueda localizarlas y podamos ver algún resultado en pantalla. A partir de este momento, ejecutando el proyecto, podremos comprobar cómo si introducimos la URL  http://localhost:tupuerto/Cliente/Listar el flujo de ejecución pasará por el método Listar() visto anteriormente. También podemos invocar a la acción Editar(), y el resultado será el esperado.

Y para los perezosos, ahí va el código fuente del proyecto (requiere Visual Studio 2008 o Web Developer Express con SP1 y ASP.NET MVC 1.0):

Crossposteando desde: Variable not found.

Recopilación de capítulos gratuitos de libros sobre ASP.NET MVC

He recibido algunos mensajes de lectores de Variable not found que quieren iniciarse en ASP.NET MVC, pero no saben por dónde empezar, y me sugieren que escriba algunos posts que expliquen desde cero este nuevo framework. Posiblemente lo haga algún día, pero mientras tanto, les he recordado que existen recursos gratuitos que pueden ser de mucha utilidad para dar los primeros pasos (e incluso profundizar un poco) en esta tecnología.

Por ejemplo, recientemente han aparecido una gran cantidad de libros sobre ASP.NET MVC, y de la mayoría de ellos se pueden descargar capítulos gratuitos que, además de ayudarnos a decidir cuál de ellos puede ser de nuestro interés, tienen valor por la información que nos pueden aportar. Como siempre, el problema es la dispersión de esta información, por lo que he decidido hacer esta recopilación para tenerlos todos a mano. Conforme vaya descubriendo nuevos ejemplares iré ampliando la lista (en el post original, por no duplicar esfuerzos); y por supuesto, si conocéis alguno que no esté aquí, me lo comentáis y lo añadimos.

Eso sí, todos en inglés, aunque al ser muy técnicos creo que son fáciles de comprender.

Pro ASP.NET MVC Framework Pro ASP.NET MVC Framework 
Autor: Steve Sanderson
Post de presentación: Now Published Pro ASP.NET MVC Framework (Apress) « Steve Sanderson’s blog

Capítulo gratuito:  Chapter 2: Your First ASP.NET MVC Application (23 páginas)En este capítulo, el autor muestra muy detalladamente, prácticamente paso a paso, cómo crear nuestra primera aplicación simple con ASP.NET MVC. En primer lugar, crea un proyecto muy básico mediante el cual explica los fundamentos básicos de la programación siguiendo este modelo, y a continuación entra en la creación de una mini-aplicación con entrada de datos, validaciones y lógica simple.

Professional ASP.NET MVC 1.0 Professional ASP.NET MVC 1.0
Autores: Scott Hanselman, Rob Conery, Phil Haack y Scott Guthrie

Capítulo gratuito: Chapter 1: Nerdinner (192 páginas) 
Este extenso capítulo describe la creación de un sitio web medianamente complejo, www.nerddinner.com, utilizando gran cantidad de tecnologías relacionadas con el framework MVC como filtros, Ajax, LinqToSql, pruebas unitarias, etc. Muy recomendable, imprescindible.

image ASP.NET MVC 1.0 Quickly
Autor: Maarten Balliauw
Post de presentación: Announcing my book- ASP.NET MVC 1.0 Quickly

Capítulo gratuito: Chapter 2: Your first ASP.NET MVC application (20 páginas) 
Breve recorrido por el proceso de creación de una aplicación MVC, el sistema de rutas, controladores, vistas y realización de pruebas unitarias. Bastante básico, quizás demasiado “quickly”, pero válido en cualquier caso.

image ASP.NET MVC in Action
Autores: Jeffrey Palermo, Ben Scheirman, Jimmy Bogard
Post de presentación: Announcing ASP.NET MVC in Action (from Manning) – Jeffrey Palermo (.com) – CodeBetter.Com

Recurso gratuito: Getting Started with the ASP.NET MVC Framework (Green Paper – PDF) (18 páginas) (El enlace a este documento te lo envían por correo electrónico tras facilitar tu dirección). Se trata de otra introducción al framework, el sistema de rutas, controladores y vistas partiendo desde cero.

Capítulo gratuito: Chapter 9: AJAX in ASP.NET MVC (21 páginas)
Interesante capítulo de introducción al uso de la tecnología Ajax sobre ASP.NET MVC utilizando jQuery y los Ajax helpers para intercambiar datos con el servidor.

image ASP.NET MVC Framework Unleashed
Autor: Stephen Walther

Capítulos gratuitos: el autor ha publicado varios capítulos en su blog, y los retirará en cuanto el libro esté disponible en Amazon, así que daos prisa que los contenidos son excelentes.
Chapter 1 – An Introduction to ASP.NET MVC
Chapter 2 – Building a Simple ASP.NET MVC Application
Chapter 3 – Understanding Controllers
Chapter 4 – Understanding Views
Chapter 5 – Understanding Models
Chapter 6 – Understanding HTML Helpers
Chapter 9 – Understanding Routing

image Beginning ASP.NET MVC 1.0
Autores: Simone Chiaretta, Keyvan Nayyeri

Capítulo gratuito: Chapter 9: Testing ASP.NET MVC Applications (38 páginas)
Interesantísimo capítulo que describe distintas técnicas para la realización de pruebas unitarias de aplicaciones creadas con este framework, incluyendo la creación de mocks, inyección de dependencias en controladores, testeo de rutas y de refilón, algo de TDD.

Crossposteando desde: Variable not found.

Control de errores en acciones ASP.NET MVC

 

Sorpresa El control de errores en aplicaciones web es fundamental si queremos ofrecer un interfaz robusto y amigable para los usuarios en cualquier situación. No hay nada más frustrante para un usuario que una pantalla de error con contenidos indescifrables y que no le aportan alternativas de salida.

El framework ASP.NET MVC nos ofrece mecanismos de control de errores muy potentes basada en la utilización del atributo HandleError, el cual definirá la vista que será mostrada al usuario cuando se produzca alguna excepción no controlada en el código de los controladores, siempre que en el web.config se haya activado el uso de errores personalizados mediante la propiedad CustomErrors.

En este post vamos a profundizar en el uso del atributo HandleError, comentando cómo se implementa en el controlador, su ámbito de actuación, los parámetros que ofrece y la forma de crear las vistas para mostrar los errores de forma amigable.

El controlador

HandleError puede ser declarado tanto a nivel de clase (controlador) como a nivel de acción. En el primer caso, se establecerá el comportamiento general para todas las acciones del controlador, mientras que en el segundo será aplicable sólo a la acción a la que se asocie el atributo:

 

// Control de errores a nivel de clase de controlador, 
// que será aplicado a todas las acciones del mismo.
[HandleError()]
public class HomeController : Controller
{
...
}

// Control de errores a nivel de acción concreta...
[HandleError(ExceptionType=typeof(ArgumentException), View="ArgumentError")]
public ActionResult Calculate(int a, int b)
{
ViewData[
"results"] = calculateSomething();
return View();
}

En realidad, el comportamiento definido en el atributo HandleError a nivel de clase también se aplicará a los errores generados por las vistas u otros resultados (ActionResult) retornados por los controladores. Es decir, sobre el segundo de los ejemplos anteriores, la vista “ArgumentError” (que existirá en un archivo llamado ArgumentError.aspx) será invocada cuando la excepción ArgumentException sea lanzada bien por el propio controlador, o bien por la vista “Calculate” que retorna por defecto.

Un último detalle sobre esto: el atributo HandleError puede ser especificado tantas veces como necesitemos sobre la misma acción o controlador, indicando comportamientos para distintos tipos de excepción. El atributo que se tendrá en cuenta cuando se produzca un error será el primero que se encuentre cuyo tipo de excepción (parámetro ExceptionType) sea compatible con la excepción lanzada.

El manejador de errores que se empleará en una acción será el primero que corresponda al tipo de excepción producida, teniendo en cuenta tanto los atributos que adornan la acción como los que acompañan a su controlador, y siempre según un orden preestablecido.

Veamos con más detenimiento los parámetros que admite la declaración del atributo.

Parámetros de HandleError

        [HandleError(
ExceptionType
=typeof(DivisionByZero),
View
="ErrorPersonalizado",
Master
="MaestraErrores",
Order
= 0)
]
public ActionResult Index()
...

  • ExceptionType permite indicar el tipo de excepción que se pretende controlar. Por defecto, el sistema entenderá que la regla se refiere al tipo base Exception, por lo que se aplicará a todos los errores que se generen, pues todas las excepciones heredan de esta clase.
  • View, el nombre de la vista que será mostrada al usuario. Por defecto se tomará el valor “Error”, por eso en la plantilla de proyectos ASP.NET MVC ya existe una vista con este nombre.
  • Master, la página maestra con la que será renderizada la vista, independientemente de lo que tenga declarado ésta.
  • Order, un valor numérico (por defecto –1, el más prioritario) que indica la prioridad de aplicación de esta regla cuando el sistema encuentre varios atributos HandleError aplicables al mismo elemento y que puedan presentar conflictos. Los valores más pequeños, Por ejemplo, si no se indicara este parámetro en el siguiente caso, el resultado dependería del orden de declaración, lo cual no es demasiado recomendable:
    [HandleError(Order=10, View="ErrorGenerico")]
    public class HomeController : Controller
    {
    ...
    [HandleError (ExceptionType
    =typeof(DivisionByZero),
    View
    ="OperacionIncorrecta", Order=1)]
    public ActionResult Calculate()
    ...

    Como puede intuirse, esto hará que en caso de producirse una división por cero, se muestre la vista “OperacionIncorrecta” y no la “ErrorGenerico”.

Cada vez que utilicemos HandleError es conveniente tener muy en cuenta la prioridad (definida en la propiedad Order), el alcance (las excepciones a tratar, definidas en la propiedad ExceptionType), así como los valores por defecto en cada caso. Esto evitará comportamientos misteriosos del sistema una vez se produzcan errores en tiempo de ejecución.

Acceso desde la vista a la información del error

Ya hemos comentado anteriormente que la vista que será mostrada a los usuarios cuando se produzca un error será la indicada en el parámetro View del atributo HandleError, o la vista «Error», si este parámetro no es informado. Sea cual sea, el archivo nombredevista.aspx deberá estar localizable por el motor de vistas en el momento de su lanzamiento (por cierto, si no te gustan las ubicaciones por defecto, puedes ver cómo modificar la forma en la que se buscan las vistas en este post).

La vista de un error es una página .aspx normal, como una vista más de la web, pero con la particularidad de que puede recibir información sobre el error que ha provocado su presentación. De hecho, se trata de una vista tipada que hereda de ViewPage<System.Web.Mvc.HandleErrorInfo>, de forma que  la propiedad Model será del tipo HandleErrorInfo, clase que nos ofrece completa información sobre el origen del problema:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits
="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>

<asp:Content ID="errorTitle" ContentPlaceHolderID="TitleContent" runat="server">
Error en el sistema
</asp:Content>
<asp:Content ID="errorContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Ups, se ha producido un ligero inconveniente...
</h2>
<p>
La acción
<%= Model.ActionName %>
del controlador
<%= Model.ControllerName %>
ha lanzado la excepción
<%= Model.Exception.GetType().Name %> con
el mensaje "
<%= Model.Exception.Message %>".
</p>
</asp:Content>

A pesar de no ser un buen ejemplo como pantalla del error amigable para el usuario ;-), el código anterior ilustra cómo es posible acceder desde la vista a los datos del contexto del error proporcionados por el entorno, y cómo la clase HandleErrorInfo nos ofrece una información sobre la acción en la que se ha lanzado la excepción, el controlador en el que se encuentra la misma, y lo más interesante, nos ofrece en la propiedad Exception la excepción producida, por lo que tendremos acceso a su tipo, descripción e incluso al estado de la pila de llamadas en el momento de producirse el error.

Crossposteado desde: Variable not found