Páginas

17 de noviembre de 2015

ASPNET 5, ¿dónde está mi customErrors?

ASP.NET 5
Seguro que ya conocéis la respuesta: no está. Desapareció. Kaput. Es simplemente otro de los efectos colaterales derivados de los cambios en ASP.NET 5, y más concretamente, de la sustitución del archivo web.config por otros mecanismos de configuración.

Sin embargo, seguro que también estaréis de acuerdo en que era una característica sumamente interesante porque nos permitía configurar el comportamiento de nuestra aplicación cuando se producía un error inesperado. Jugando un poco con la configuración podíamos optar por mostrar valiosa información de depuración, como datos sobre la excepción lanzada, el punto exacto donde se produjo o la pila de ejecución, o bien páginas de error personalizadas con mensajes aptos para todos los públicos (como la ballenita voladora de Twitter u otras creativas páginas "oops!" que inundan la red).


En aplicaciones ASP.NET 5, la gestión de errores de aplicación se delega ahora a middlewares especializados que, posicionados estratégicamente en el pipeline, vigilan el resultado del proceso de las peticiones y toman el control cuando se ha producido un error. El código básico de gestión de errores viene ya incluido en las plantillas de proyectos MVC, pero creo que es interesante analizar un poco qué ha cambiado y en qué consiste la solución propuesta por este marco de trabajo.

Así que comencemos desde el principio… aunque antes, permitidme el tradicional disclaimer: ASP.NET 5 todavía está en desarrollo, y algunos de los detalles que contemos a continuación aún podrían variar.

1. ¿Qué ocurre (por defecto) en ASP.NET 5 cuando explota nuestra aplicación?

Para comprobarlo, creamos una aplicación MVC 6 vacía e introducimos la siguiente aberración en el controlador HomeController:
public IActionResult About()
{
var j = 0;
var i = 10/j; // Buggy code

ViewData["Message"] = $"Your magic number is {i}.";
return View();
}
Obviamente, al ejecutar y acceder a la ruta /home/about de nuestra aplicación, la acción lanza una excepción de división por cero y no puede continuar, pero, ¿qué recibimos desde el cliente cuando se produce este error?

Excepción no capturadaPues probablemente os pueda sorprender un poco al principio, pero en el lado cliente no recibiremos absolutamente nada. Bueno, sí, un código de error HTTP 500 acompañado de un contenido totalmente vacío, pero nada de páginas de error descriptivas o pistas que indiquen dónde puede estar el problema.

Y la explicación es realmente sencilla, pero tenemos que olvidar la estructura monolítica de System.Web, usado en ASP.NET 4 y anteriores, donde se incluían siempre todos los módulos y funcionalidades, las usáramos o no.

En ASP.NET 5, si queremos usar una funcionalidad, como puede ser la gestión de errores o cualquier otra, primero tendremos que incluirla previamente en nuestro proyecto, y después configurarla para que funcione de acuerdo a nuestras necesidades.

Pipeline sin gestión de erroresEn este caso, como no hemos configurado nada al respecto, la petición entra en el pipeline y comienza a ascender, atravesando los middlewares que tenemos configurados y acabando en el framework MVC, quien ejecuta nuestra acción. Al producirse la excepción, ésta desciende a través del pipeline recorriéndolo en sentido inverso hasta llegar al inicio del pipeline. Como ningún middleware ha tomado el control, el servidor se encuentra con la excepción y lo único que puede hacer con ella es generar el error 500 sin contenido adicional que recibimos en el lado cliente.

Simplemente, es que el servidor tiene poco más que decir ;)

2. Middlewares de depuración y gestión de errores

En ASP.NET 5 podemos emular fácilmente lo que podíamos conseguir en versiones anteriores con la etiqueta <customErrors> del web.config, aunque la forma de hacerlo es bastante diferente. Como hemos adelantado anteriormente, ahora la solución a la gestión de errores la tenemos en forma de middleware.

Pipeline con gestión de erroresLa idea es la representada en el diagrama adjunto. Como se puede ver, lo que hacemos es posicionar un middleware capaz de procesar los errores justo en la entrada del pipeline, dejando pasar todas las peticiones entrantes y controlando todos los resultados salientes. Cuando un error desciende por el pipeline, este middleware será capaz de detectarlo, capturarlo y hacer algo con él, como retornar una descripción detallada del problema, o enviar al usuario una bonita página descriptiva. Básicamente, el qué hacer dependerá de si nos encontramos en un entorno de desarrollo o pruebas.

Para entornos de desarrollo, en ASP.NET 5 disponemos del middleware llamado DeveloperExceptionPageMiddleware, que viene incluido de serie en el paquete "Microsoft.AspNet.Diagnostics". Obviamente, para usarlo tendremos que haber añadido previamente la referencia a dicho paquete en nuestro proyecto:
project.json con referencia al paquete de diagnósticos










Por cierto, un inciso: este paquete, además del middleware de gestión de errores, incluye interesantes herramientas de depuración, por lo que vale la pena tenerlo a mano. Otro día hablaremos de algunas de ellas.

Una vez instalado el paquete, ya podemos añadir el middleware al pipeline. Para ello, siguiendo las convenciones habituales en este tipo de componentes, podemos usar el método UseDeveloperExceptionPage() sobre el IAppBuilder que recibimos en el método Configure() de la clase Startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

// Set up other middlewares
...
}
Fijaos que añadimos el middleware sólo si estamos ejecutando la aplicación en entornos de desarrollo, lo cual asegurará que nadie externo a ellos podrá ver información técnica que podría comprometer la seguridad del sistema.

Si ahora ejecutamos la aplicación y accedemos a /Home/About, encontramos algo bastante más razonable y útil para nuestro trabajo :)
Página de error descriptiva
Como se puede observar, la página de error es bastante más completa que la que teníamos por defecto en versiones anteriores de ASP.NET, pues no sólo muestra la pila de ejecución sino también interesante información de contexto de la petición, como los parámetros de la consulta, las cookies y todos los encabezados enviados al servidor en la petición.

En entornos de producción utilizaremos en cambio el middleware ExceptionHandlerMiddleware, incluido en el mismo paquete "Microsoft.AspNet.Diagnostics". Ese módulo es capaz de capturar los errores, dejar una traza en el log del sistema e introducir en el pipeline una nueva petición, cuyo resultado será el que finalmente se envíe al cliente.

Para ver un ejemplo, añadimos primero el middleware al pipeline de la siguiente forma, de forma que sólo se aplique cuando el entorno no sea el de desarrollo:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
imageTras incluir este código en la inicialización, si accedemos a /home/about en un entorno de producción, el middleware capturará el error y, antes de devolver nada al cliente, lanzará internamente una petición a la ruta que le indicamos, en este caso /home/error.

El resultado de la ejecución de la acción Error() en HomeController es el que será será enviado al cliente. Por ejemplo, si en esta acción simplemente hacemos un return View(), al cliente llegará el contenido de la vista /Views/Home/Error.cshtml, que podría ser similar al que vemos en la captura de pantalla adjunta (el texto "An error ocurred while processing your request").
public class HomeController: Controller
{
... // Other actions

// GET /home/error
public IActionResult Error()
{
return View();
}
}
Ojo, que cuando hablamos de "petición interna" en ningún momento quiere decir que se trate de una redirección enviada al navegador. Se trata de algo totalmente transparente para el lado cliente, es sólo una especie de petición falsa introducida en el pipeline por ExceptionHandlerMiddleware con objeto de que sea procesada por otros middlewares posteriores.

Y antes de acabar, una última observación. En los ejemplos anteriores hemos probado forzando un error en nuestra aplicación lanzando una excepción, pero, ¿y qué ocurre con otro tipo de errores, como un 404 "not found" o errores que no son propiamente dichos de la aplicación?

Pues por defecto el comportamiento es básicamente igual: se retorna el código de error (404, por ejemplo) sin ningún tipo de contenido o página que lo acompañe. De nuevo, la petición subirá por el pipeline y no será procesada por ningún middleware, lo que provocará que el servidor retorne este error.

Sin embargo, este tipo de errores no son capturables por los middlewares DeveloperExceptionPageMiddlewareExceptionHandlerMiddleware, orientados exclusivamente a la captura de excepciones de aplicación. Para gestionar errores ajenos a ella, como un error 404, hay que usar técnicas diferentes, que veremos en otra ocasión.

Publicado en Variable not found.


from Variable not found http://www.variablenotfound.com/2015/11/aspnet-5-donde-esta-mi-customerrors.html
via IFTTT