11 de septiembre de 2017

Custom Error Pages MVC C#

Antes de empezar dejo un listado de los códigos HTTP más comunes, IMPORTANTE a tener en cuenta que algunos errores serán identificados y tratados por la aplicación MVC ASP.NET mientras que otros errores seran identificados por el IIS.

200 OK
201 CREATED
202 ACCEPTED
204 NO_CONTENT
206 PARTIAL_CONTENT
300 MULTIPLE_CHOICES
301 MOVED_PERMANENTLY
302 FOUND
304 NOT_MODIFIED
400 BAD_REQUEST
401 UNAUTHORIZED
403 FORBIDDEN
404 NOT_FOUND
405 METHOD_NOT_ALLOWED
406 NOT_ACCEPTABLE
409 CONFLICT
410 GONE
412 PRECONDITION_FAILED
413 REQUEST_ENTITY_TOO_LARGE
414 REQUEST_URI_TOO_LARGE
415 UNSUPPORTED_MEDIA_TYPE
417 EXPECTATION_FAILED
500 INTERNAL_SERVER_ERROR
503 SERVER_UNAVAILABLE

Por defecto la aplicación  MVC ASP.NET utilizará la "Yellow Screen Of Death" para mostrar cualquier error que identifique durante su ejecución tanto en desarrollo como en producción, esta vista no debería mostrarse al usuario externo ya que puede no entender cual es el error en la aplicación o bien porque se le muestran demasiados detalles del error.


Para evitar mostrar la YSOD al usuario, se diseñará una vista más amigable según sea el error HTTP, para esto se creará un controlador especifico(ErrorController) con acciones definidas(ActionResult) para cada tipo de error HTTP y estas acciones mostrarán una vista(.cshtml) especifica al usuario, estas vistas deber guardarse en la carpeta Shared para permanecer dísponibles a toda la aplicación.

VERIFICAR/MODIFICAR FilterConfig.cs y Global.asax.cs
Agregar o bien verificar que se cuente con el siguiente filtro global en los archivos que se muestran en la imagen


en caso de querer administrar errores para acciones especificas y no para toda la aplicación se puede utilizar el atributo [HandleError] pero este sólo maneja los errores más conocidos, errores globales desconocidos deben ser manejados por el filtro mostrado arriba.

CREAR ErrorController CON ACCIONES Index y NotFound
En este controlador se pueden agregar las acciones necesarias que manejen los codigos de error HTTP en tu aplicación, para este ejemplo solamente capturo el error 500 y 404

public class ErrorController : Controller  
    {  
  
        public ActionResult Index()  
        {  
            // Response.StatusCode = 500; para HTTP IIS            
           return View();  
        }  
  
        public ActionResult NotFound()  
        {  
            // Response.StatusCode = 404;  para HTTP IIS        
            return View();  
        }  
    } 

CREAR VISTAS
como último paso agregar las vistas Index y NotFound que se mostrarán al usuario, recordar guardarlas en la carpeta Shared, el código de la vista puede ser

@model System.Web.Mvc.HandleErrorInfo

@{  ViewBag.Title = "Error"; }

Ha ocurrido un error.

Controller = @Model.ControllerName Action = @Model.ActionName Message = @Model.Exception.Message StackTrace : @Model.Exception.StackTrace

Claro que estos mensajes deberían mostrarse solamente en desarrollo, nunca cuando la aplicación se encuentre en producción ya que representan un riesgo en seguridad el dar el error tan detallado, pero si no quieres estar cambiando el valor de  de On-Off se puede utilizar el codigo tal como se muestra en esta imagén, de esta manera se puede definir   y será la vista quien defina si la petición es interna --ante lo cual se mostrara el error debidamente detallado con el controlar/vista-- o es una petición de un usuario externo. imagen: DevCurry.com

error-local-and-remote

MODIFICAR Web.config
para indicar a la aplicación que se utilizará una pagina de error diseñada por ti -- o bien la pagina Error.cshtml que por defecto se almacena en la carpeta Shared al crear una aplicación de internet, que es la que utilizaré en este ejemplo-- se debe agregar el tag de XML customErrors con el valor de mode= "On" entre las etiquetas system.web  


como consejo el valor de customErrors debe ser RemoteOnly, asi se mostrará la YSOD cuando se trabaje en desarrollo pero se mostrará la página más amigable con un mensaje personalizado a usuario externos(la que has diseñado por tu cuenta o la Error.cshtml). 

Si solamente quisieramos capturar el error 500 en nuestra aplicación bastará con la etiqueta customErrors mode="On" pero en este ejemplo además se pretende capturar el error HTTP 404 por lo que se muestra el código que debe agregarse en el Web.config dentro de la etiqueta system.web


Controlando errores IIS.
De momento sólo estamos capturando errores HTTP especificos que nuestra aplicación MVC ASP.NET puede reconocer o bien que corresponden a una ruta definida en RouterConfig.cs, pero qué pasa si el usuario especifica una ruta que no corresponda con la configuración de RouterConfig.cs? entonces IIS manejara la excepción y mostrará la standard IIS 404 error page, para cubrir este error y ampliar a errores HTTP controlados por IIS agregar la tag httpErrors dentro de la etiqueta system.webServer --en el ErrorController puede ser necesario agregar Response.StatusCode = 404;


puede ser que no quieras mandar al ErrorController para luego mostrar la vista, entonces el redirect deberia apuntar a la direccion de la página HTML estática

Esta alternativa es más sencilla, no se necesita modificar el Global.asax de la aplicación ni el web.config pero si es necesario que todo error en la aplicación sea envíado al ErrorController junto con un parámetro indicando el tipo de error, entonces en el Global.asax agregar:

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Response.Clear();

    HttpException httpException = exception as HttpException;

    int error = httpException != null ? httpException.GetHttpCode() : 0;

    Server.ClearError();
    Response.Redirect(String.Format("~/Error/?error={0}", error, exception.Message));
}

Creando Controlador que maneja los errores
Este Errorcontrolador en su accion Index recibirá un parámetro del tipo numérico el cual es el tipo de error HTTP ocurrido, luego mediante switch se encargará de definir el error a reportar en la vista segiun sea el código HTTP con el que se encontró la aplicación, luego se manda un mensaje personalizado mediante ViewBag a una vista especifca, para este ejemplo se debe corregir la ruta en la que se encuentra la vista Error.cshtml, si creaste una internet App la ruta en el controller debería ser: return View("~/Views/Shared/Error.cshtml");

public class ErrorController : Controller
{
    // GET: Error
    public ActionResult Index(int error = 0)
    {
        switch (error)
        {
            case 505:
                ViewBag.Title = "Ocurrio un error inesperado";
                ViewBag.Description = "Esto es muy vergonzoso, esperemos que no vuelva a pasar ..";
                break;

            case 404:
                ViewBag.Title = "Página no encontrada";
                ViewBag.Description = "La URL que está intentando ingresar no existe";
                break;

            case 400:
                ViewBag.Title = "Bad Request";
                ViewBag.Description = "Bad request: The request cannot be fulfilled due to bad syntax";
                break;

            case 403:
                ViewBag.Title = "Forbidden";
                ViewBag.Description = "Forbidden";
                break;

            case 408:
                ViewBag.Title = "Server TimeOut";
                ViewBag.Description = "The server timed out waiting for the request";
                break;

            case 500:
                ViewBag.Title = "Internal Server Error";
                ViewBag.Description = "Internal Server Error - server was unable to finish processing the request";
                break;
            
            default:
                ViewBag.Title = "Página no encontrada";
                ViewBag.Description = "Algo salio muy mal :( ..";
                break;
        }

        return View("~/views/error/_ErrorPage.cshtml");
    }
}

Agregando los ViewBag a la vista, esta puede mejorarse pero lo importante de momento es mostrar el mensaje de error enviado desde el controlador

<h2 class="page-header">@ViewBag.Title</h2>
<p>@ViewBag.Description</p>pan>

información obtenida desde:
http://anexsoft.com/p/99/implementando-un-custom-error-page-con-asp-net-mvc