Autor en Google+
Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

10 años online 🎂

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 1 de diciembre de 2015
ASP.NET CorePues no parecía que esto de la gestión de errores fuera a dar para tanto, pero este es ya el tercer post dedicado a este tema, que por otro lado debemos controlar totalmente cuando comencemos a crear aplicaciones reales con ASP.NET Core y MVC.

Hasta ahora hemos visto cómo gestionar las excepciones producidas desde nuestras aplicaciones utilizando el middleware ExceptionHandlerMiddleware, y cómo obtener información sobre dichas excepciones desde su código manejador. Esto nos permitía un buen grado de control sobre los errores HTTP 500, pero el amigo Juan, muy atento a lo que íbamos viendo, preguntaba:
"Siguiendo con la captura de excepciones, esta vez en mvc6 como se podría capturar los códigos de estado como por ejemplo 404"
Obviamente el resto de errores HTTP entre el 400 (errores en cliente) y el 599 (errores en servidor) no son capturados por ExceptionHandlerMiddleware porque en realidad no se deben a excepciones generadas desde nuestra aplicación sino a otros problemas que han podido surgir durante el proceso de la petición. Por ello, requieren un tratamiento distinto que de nuevo viene en forma de middleware.

Introducing StatusCodePagesMiddleware

Este middleware, que al igual que los ya vistos en otros artículos anteriores se incluye en el paquete Microsoft.AspNetCore.Diagnostics, permite capturar las respuestas sin contenido con códigos HTTP 4xx y 5xx e implementar código personalizado de tratamiento.

Como es habitual, para añadirlo en el pipeline utilizaremos el extensor UseStatusCodePages() sobre IApplicationBuilder en el método Configure() de la clase de inicialización:
public void Configure(IApplicationBuilder app)
{
    ... // Initialization code
    app.UseStatusCodePages();
    ... // Other midlewares
}
Recordad que la posición de los middlewares es crucial, puesto que las peticiones y los retornos serán ejecutados dependiendo del orden en el que han sido añadidos. Dado que este middleware captura retornos, puede ser interesante insertarlo muy al principio del pipeline, para que sólo actúe en el caso de que otros no hayan gestionado el error antes.

Error 404 capturado por el middlewareLa introducción de esa simple línea ya establece un manejador por defecto para los errores HTTP citados que simplemente mostrará el error y su reason phrase, tal y como se muestra en la captura de pantalla adjunta.

Si quisiéramos personalizar un poco el retorno enviado, podríamos hacerlo utilizando la siguiente sobrecarga, en la que indicamos el content-type a retornar, y una plantilla de texto donde "{0}" será sustituido por el código del error capturado:
app.UseStatusCodePages(
    "text/plain", 
    "Just another HTTP {0} error :)"
);
Error con texto personalizadoEl resultado al capturar un error 404 sería el mostrado en la captura de pantalla adjunta.

Hasta aquí, hemos avanzado un poco en el manejo del error, pero aún no es demasiado práctico. Para mejorarlo, podemos usar otra de las sobrecargas de UseStatusCodePages() a la que podemos suministrar un delegado con código de tratamiento personalizado del error. En el siguiente ejemplo utilizamos esta característica sólo para enviar directamente un texto personalizado con el error que se ha producido, pero en ese punto podríamos introducir cualquier tipo de código:
app.UseStatusCodePages(async ctx =>
{
    await ctx.HttpContext.Response.WriteAsync(
        $"Ops, HTTP {ctx.HttpContext.Response.StatusCode} error!"
    );
});
Error 404 con tratamiento personalizado

Pero si preferimos soluciones precocinadas que nos hagan aún la vida más sencilla, todavía tenemos opciones basadas en el mismo middleware más potentes y prácticas que las anteriores, si lo añadimos al pipeline mediante los extensores UseStatusCodePagesWithRedirects() y UseStatusCodePagesWithReExecute(). Estos dos métodos son lo más parecido que tenemos en ASP.NET Core al atributo redirectMode de la etiqueta <customErrors>.

Como se puede intuir, el extensor UseStatusCodePagesWithRedirects() prepara el middleware para que capture los errores y retorne al cliente una redirección (HTTP 302) hacia la URL que se indique como parámetro. Como ocurría en un ejemplo anterior, de nuevo el "{0}" será sustituido por el código HTTP del error capturado:
// En Startup.cs
app.UseStatusCodePagesWithRedirects("/error/handle/{0}");


// En Controllers/ErrorController.cs
public class ErrorController : Controller
{
   [Route("error/handle/{errorCode}")]
   public IActionResult Handle(int errorCode)
   {
      // Do something and return a view or a content
      return Content("Error: " + errorCode);
    }
 }
El resultado si intentamos acceder a un recurso inexistente será el siguiente:

Captura del error con redirección
Desde el lado cliente, se obtendrá en primer lugar una respuesta HTTP 302 con la dirección /error/handle/404, y seguidamente una respuesta HTTP 200 con el contenido que retorne la acción invocada, que por supuesto puede ser una vista completa llena de colores y mensajes simpáticos, como se lleva ahora ;)

Por otra parte, podemos usar UseStatusCodePagesWithReExecute() para indicar al middleware que debe capturar los errores y generar una petición interna, cuyo resultado será retornado al cliente. Probemos por ejemplo el siguiente código en la inicialización de la aplicación:
app.UseStatusCodePagesWithReExecute("/error/handle/{0}");
Si se produce un error 404, se procesará internamente una petición hacia /error/handle/404, y el resultado será enviado como contenido de la respuesta al cliente, pero el código HTTP retornado seguirá siendo 404.

Y creo que esto es todo… salvo que se nos vayan ocurriendo más casos para ir viendo, claro ;)

Publicado en Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

5 Comentarios:

Juan dijo...

Bien Clara la explicación José Maria, gracias por el aporte!!!

José M. Aguilar dijo...

Muchas gracias a ti, Juan!

Anónimo dijo...

Está gestión se realiza de la misma manera en MVC?
No conozco el concepto de middleware para MVC, sería lo que hasta ahora ha sido un filtro?

José M. Aguilar dijo...

Hola!

Claro, MVC se ejecuta sobre ASP.NET :) De hecho, los ejemplos anteriores usan MVC.

Aunque conceptualmente podrías encontrar alguna similitud con los filtros, no es lo mismo; los filtros son algo propio de MVC y siguen existiendo. Los middlewares arquitecturalmente están por abajo, a nivel de la plataforma ASP.NET.

En este post puedes leer un poco sobre los middlewares y el pipeline de ASP.NET: http://www.variablenotfound.com/2015/02/aspnet-5-donde-esta-mi-globalasax.html.

Saludos!

Anónimo dijo...

muchas gracias por la aclaración! le echo un vistazo sin falta!