martes, 11 de marzo de 2014

Excepciones en MVC – HandleErrorAttribute Filter

Esta semana en areaTIC nos disponemos a evaluar las posibilidades que ofrece ASP.NET MVC para la gestión de excepciones. En nuestro caso siempre habíamos optado en entornos WebForms por soluciones globales tipo Application_Error más que ir página a página. En MVC tenemos alguna que otra posibilidad más.

Veamos que opciones tenemos:
  • A nivel local en cada acción de cada controlador con bloque try catch. Implica controlar el error cada vez que creamos una acción en un controlador.

  • A nivel local también podríamos sobre escribir el método OnException del controlador y de este modo evitaríamos tener que escribir un bloque Try/Catch por cada acción.

  • A nivel global si aplicamos mediante FilterConfig un filtro a todos los controladores (por defecto la plantilla de Visual Studio lo aplica) podemos especificar entonces vía web.config una página a la que redirigir la petición cuando se produce un error en un controlador y una vez en la página mostrar el error. Por defecto no se puede gestionar el logging ni tratar diferente los errores según el escenario (por ejemplo llamadas Ajax). Además todo error que se produzca fuera de MVC como podría ser un 404 no pasará por este filtro. Para salvar las 2 primeras limitaciones tendríamos que extender HandleErrorAttribute y para redirigir el 404 a la página de error se podría hacer vía web.config también.

  • Otra opción es usar Application_Error de Global.asx. Tiene sus inconvenientes, el principal es que el evento salta fuera de contexto MVC y por tanto es más complicado personalizar la excepción en función de la acción que la origina.

En nuestro caso nos decantamos por Filters, veamos como extender HandleErrorAttribute. Los pasos para crear un Filter personalizado son muy parecidos a los que haríamos para crear un Helper propio. Creamos una carpeta Filters en el proyecto (si no existe) y dentro añadimos la clase que queremos extender. En este caso heredamos de HandleErrorAttribute e implementamos los métodos base y sobreescribimos el método OnException(). Nos creamos también un método privado para determinar si la petición es Ajax o no ya que en función de esto devolveremos vía MVC la página de error o bien responderemos a la petición inicial Ajax con el código de error correspondiente y el contenido del mensaje para poder gestionar el error en cliente como necesitemos.
namespace AreaTIC.Web.Filters
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AreaTICHandleErrorAttribute : HandleErrorAttribute  
    {
        public override void OnException(ExceptionContext filterContext)
        {
            if (IsAjax(filterContext))
            {
                filterContext.Result = new JsonResult()
                {
                    Data = filterContext.Exception.Message,
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };

                filterContext.ExceptionHandled = true;
                filterContext.HttpContext.Response.StatusCode = 500;
            }else
                base.OnException(filterContext);
        }

        private bool IsAjax(ExceptionContext filterContext)
        {
            return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
        }
    }
}
Por último en app_start en el archivo FilterConfig.cs veréis que hay una línea que se encarga de automáticamente añadir el filtro HandleErrorAttribute a cada acción de todos los controladores donde se ha de especificar el nuevo filtro extendido que acabamos de crear.
namespace AreaTIC.Web
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new AreaTICHandleErrorAttribute());
        }
    }
}
Ahora todo correcto si el error se produce durante una llamada Ajax se lanza el evento error: de la llamada Ajax y allí podemos mostrar el contenido del error en un popup o realizar las acciones que creamos oportunas. En cambio si el error se genera desde una petición HTTP normal sin Ajax MVC responde con la página de error que tenemos configurada en Web.Config en la sección CustomErrors.

Hasta aquí el artículo de hoy, como siempre recordar que podéis seguir areaTIC en las redes sociales y son bienvenidos todo tipo de aportaciones y/o comentarios. Que paséis una buena semana!

No hay comentarios:

Publicar un comentario