Gestión de errores en ASP.NET MVC

2. September 2012 13:49 by Oscar.SS in Desarrollo Web  //  Tags:   //   Comments (0)

Supongo que no es necesario mencionar lo importante que es la correcta gestión de errores (o excepciones) en nuestras aplicaciones web. Calidad del producto, índice de profesionalidad, SEO, usabilidad, son algunas de las razones para tener muy en cuenta esta parte del desarrollo.

En ASP.NET MVC (en adelante MVC) tenemos varias formas de controlar los errores para evitar que el usuario vea esa "pantalla de la muerte" y en su lugar podamos mostrarle un mensaje más amigable. En este artículo daremos un repaso ligero a cada una de las distintas opciones que tenemos a nuestra disposición.

 

Gestión de errores en ASP.NET

Si, has leído bien, he dicho gestión de errores en ASP.NET y me he comido a posta el MVC. Siempre que trabajemos con MVC debemos tener en cuenta que este se integra dentro del marco de ASP.NET, por lo  tanto, muchos de los conceptos y bondades del framework serán también válidos para MVC. 

En la figura de la derecha (Fuente: Comparing Web Forms And ASP.NET MVC) podemos ver claramente como MVC se integra en la pila de ejecución de ASP.NET. Por cierto, si os interesa ver en detalle el ciclo de ejecución de MVC os recomiendo la lectura de An Introduction to ASP.NET MVC Extensibility.

En cuanto al tema que nos ocupa, el control de errores, en MVC también podremos hacer uso, con leves diferencias, de las mismas posibilidades que tradicionalmete nos ha brindado el framework de ASP.NET.

Es evidente que podemos usar directamente en nuestro código el control estructurado de excepciones con Try/Catch/Finally.

Otra posibilidad es manejando el evento Application_Error() del archivo Global.asax. En este enlace podéis ver un ejemplo que utiliza esta técnica para gestionar errores producidos durante el procesamiento de solicitudes HTTP como 404, 301, etc (HTTP Codes)

Muy conocido también es el manejo de errores mediante configuración del archivo Web.config y su sección CustomErrors. En Internet existen multitud de ejemplos sobre este tema, aunque particularmente no tantos sobre MVC. Es posible que la razón sea que tenemos que realizar un poco de trabajo extra al definir los métodos de acción en los controladores.

    <customErrors mode="On" defaultRedirect="~/Views/Shared/Error.cshtml">
      
<error statusCode="404" redirect="/Errors/NotFound"/>
      <
error statusCode="500" redirect="InternalError"/>
    </
customErrors>

Con cualquiera de las definiciones de rutas anteriores obtendremos un resultado inesperado. MVC utiliza el sistema de routing de ASP.NET para gestionar las solicitudes entrantes por medio de los controladores. Con la configuración anterior, si definimos un controlador llamado ErrorsController y un método de acción denominado NotFound estaremos manejando solamente el error HTTP 404, del resto de rutas ni se entera. Es imprescindible que las rutas sigan el formato /NombreControlador/NombreVista y que codifiquemos los controladores y métodos de acción asociados. Este es el trabajo extra del que hablaba.

Existen también potentes frameworks de terceros que nos pueden ayudar en la gestión de errores y en algunas funcionalidades más (monitoreo, seguimiento, notificaciones, logs, etc). Un ejemplo podría ser ELMAH que, si no me equivoco, en primer lugar se desarrolló para su uso en ASP.NET pero también está disponible para MVC.

 

Gestión de errores en ASP.NET MVC

Ahora si, hablemos de lo que nos interesa. El framework MVC de partida nos ofrece mecanismos para el manejo de excepciones basados en la clase HandleErrorAttribute. De hecho, para activarlo tan solo tendremos que establecer a On o RemoteOnly el atributo mode del elemento <customErrors> en el archivo de configuración, dado que por defecto en el Global.asax el atributo HandleError viene definido como filtro global a toda la aplicación. En este artículo se explica con sumo detalle la utilización de este mecanismo.

La clase HandleErrorAttribute hereda de FilterAttribute e implementa la interfaz IExceptionFilter cuyo único método OnException() es llamado cuando se produce una excepción. Con la herramienta Reflector podemos estudiar el cuerpo de este método en HandleErrorAttribute.

        public virtual void OnException(ExceptionContext filterContext)
        {
            
if (filterContext == null)
            {
                
throw new ArgumentNullException("filterContext");
            
}

            
if (!filterContext.IsChildAction && 
                (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))
            {
                Exception innerException 
filterContext.Exception;

                if 
((new HttpException(null, innerException).GetHttpCode() == 500) && 
                    
this.ExceptionType.IsInstanceOfType(innerException))
                {
                    
string controllerName (string)filterContext.RouteData.Values["controller"];
                    string 
actionName (string)filterContext.RouteData.Values["action"];
                    
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
                    
ViewResult result = new ViewResult
                    {
                        ViewName 
= this.View,
                        MasterName 
= this.Master,
                        ViewData 
= new ViewDataDictionary<HandleErrorInfo>(model),
                        TempData 
filterContext.Controller.TempData
                    }
;

                    
filterContext.Result result;
                    
filterContext.ExceptionHandled = true;
                    
filterContext.HttpContext.Response.Clear();
                    
filterContext.HttpContext.Response.StatusCode 500;
                    
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
                
}
            }
        }

Al repasar este código lo que más llama la atención es que solo se están gestionando los errores HTTP 500 o errores internos del servidor. Este podría ser el caso de una división por cero (DivideByZeroException), una conversión erronea de tipos (InvalidCastException), o cuando intentamos devolver en un método de acción una vista que no se ha definido (InvalidOperationException).

Si queremos gestionar errores de redireccionamiento del usuario (como HTTP 307; movido temporalmente) o errores de comunicación en la parte del cliente (como HTTP 404; recurso no encontrado) tendremos que hacer uso combinado del los elementos customErrors/error y de la correspondiente codificación de los métodos de acción como hemos visto anteriormente.

Recent Comments

Comment RSS

Month List