Cómo hacer que las páginas de error personalizadas funcionen en ASP.NET MVC 4

Quiero que se muestre una página de error personalizada para 500, 404 y 403. Esto es lo que hice:

  1. Habilitado los errores personalizados en el web.config de la siguiente manera:

        
  2. Registró HandleErrorAttribute como un filtro de acción global en la clase FilterConfig la siguiente manera:

     public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new CustomHandleErrorAttribute()); filters.Add(new AuthorizeAttribute()); } 
  3. Creó una página de error personalizada para cada uno de los mensajes anteriores. El predeterminado para 500 ya estaba disponible fuera de la caja.

  4. Declarado en cada vista de página de error personalizada que el modelo para la página es System.Web.Mvc.HandleErrorInfo

Para 500, muestra la página de error personalizado. Para otros, no es así.

¿Hay algo que este olvidando?

Parece que esto no es todo lo que hay para mostrar errores personalizados cuando leo el código en el método OnException de la clase HandleErrorAttribute y solo maneja 500.

¿Qué debo hacer para manejar otros errores?

Mi configuración actual (en MVC3, pero creo que todavía se aplica) se basa en tener un ErrorController , entonces uso:

      

Y el controlador contiene lo siguiente:

 public class ErrorController : Controller { public ViewResult Index() { return View("Error"); } public ViewResult NotFound() { Response.StatusCode = 404; //you may want to set this to 200 return View("NotFound"); } } 

Y las vistas tal como las implementa. Aunque tiendo a agregar un poco de lógica, para mostrar la traza de la stack y la información de error si la aplicación está en modo de depuración. Entonces Error.cshtml se ve más o menos así:

 @model System.Web.Mvc.HandleErrorInfo @{ Layout = "_Layout.cshtml"; ViewBag.Title = "Error"; } 
Error
An unexpected error has occurred. Please contact the system administrator.
@if (Model != null && HttpContext.Current.IsDebuggingEnabled) {

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

 @Model.Exception.StackTrace 

}

He hecho la solución de pablo y siempre tuve el error (MVC4)

La vista ‘Error’ o su maestro no se encontró o ningún motor de vista admite la ubicación buscada.

Para deshacerte de esto, elimina la línea

  filters.Add(new HandleErrorAttribute()); 

en FilterConfig.cs

Hago algo que requiere menos encoding que las otras soluciones publicadas.

Primero, en mi web.config, tengo lo siguiente:

     

Y el controlador (/Controllers/ErrorPageController.cs) contiene lo siguiente:

 public class ErrorPageController : Controller { public ActionResult Oops(int id) { Response.StatusCode = id; return View(); } } 

Y finalmente, la vista contiene lo siguiente (desglosado por simplicidad, pero puede contener:

 @{ ViewBag.Title = "Oops! Error Encountered"; } 

@Response.Status

Possible causes:

  • Baptist explanation: There must be sin in your life. Everyone else opened it fine.
  • Presbyterian explanation: It's not God's will for you to open this link.
  • Word of Faith explanation: You lack the faith to open this link. Your negative words have prevented you from realizing this link's fulfillment.
  • Charismatic explanation: Thou art loosed! Be commanded to OPEN!
  • Unitarian explanation: All links are equal, so if this link doesn't work for you, feel free to experiment with other links that might bring you joy and fulfillment.
  • Buddhist explanation: .........................
  • Episcopalian explanation: Are you saying you have something against homosexuals?
  • Christian Science explanation: There really is no link.
  • Atheist explanation: The only reason you think this link exists is because you needed to invent it.
  • Church counselor's explanation: And what did you feel when the link would not open?


HTTP @Response.StatusCode - @Response.StatusDescription

Recomendaría usar el archivo Global.asax.cs.

  protected void Application_Error(Object sender, EventArgs e) { var exception = Server.GetLastError(); if (exception is HttpUnhandledException) { Server.Transfer("~/Error.aspx"); } if (exception != null) { Server.Transfer("~/Error.aspx"); } try { // This is to stop a problem where we were seeing "gibberish" in the // chrome and firefox browsers HttpApplication app = sender as HttpApplication; app.Response.Filter = null; } catch { } } 

Parece que hay una serie de pasos aquí mezclados. Presentaré lo que hice desde cero.

  1. Crear el controlador ErrorPage

     public class ErrorPageController : Controller { public ActionResult Index() { return View(); } public ActionResult Oops(int id) { Response.StatusCode = id; return View(); } } 
  2. Agregue vistas para estas dos acciones (haga clic derecho -> Agregar vista). Deben aparecer en una carpeta llamada ErrorPage.

  3. Dentro de App_Start abra FilterConfig.cs y comente el filtro de manejo de errores.

     public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // Remove this filter because we want to handle errors ourselves via the ErrorPage controller //filters.Add(new HandleErrorAttribute()); } 
  4. Dentro de web.config agregue las siguientes entradas , en System.Web

         
  5. Prueba (por supuesto). Lanza una excepción no controlada en tu código y ve que va a la página con id 500, y luego usa una URL en una página que no existe para ver 404.

Sobre la base de la respuesta publicada por maxspan, he creado un proyecto de muestra mínimo en GitHub que muestra todas las partes funcionales.

Básicamente, simplemente agregamos un método Application_Error a global.asax.cs para interceptar la excepción y darnos la oportunidad de redirigir (o más correctamente, transferir la solicitud ) a una página de error personalizada.

  protected void Application_Error(Object sender, EventArgs e) { // See http://stackoverflow.com/questions/13905164/how-to-make-custom-error-pages-work-in-asp-net-mvc-4 // for additional context on use of this technique var exception = Server.GetLastError(); if (exception != null) { // This would be a good place to log any relevant details about the exception. // Since we are going to pass exception information to our error page via querystring, // it will only be practical to issue a short message. Further detail would have to be logged somewhere. // This will invoke our error page, passing the exception message via querystring parameter // Note that we chose to use Server.TransferRequest, which is only supported in IIS 7 and above. // As an alternative, Response.Redirect could be used instead. // Server.Transfer does not work (see https://support.microsoft.com/en-us/kb/320439 ) Server.TransferRequest("~/Error?Message=" + exception.Message); } } 

Controlador de error:

 ///  /// This controller exists to provide the error page ///  public class ErrorController : Controller { ///  /// This action represents the error page ///  /// Error message to be displayed (provided via querystring parameter - a design choice) ///  public ActionResult Index(string Message) { // We choose to use the ViewBag to communicate the error message to the view ViewBag.Message = Message; return View(); } } 

Vista de página de error:

    Error   

My Error

@ViewBag.Message

Nada más está involucrado, aparte de deshabilitar / eliminar filters.Add(new HandleErrorAttribute()) en FilterConfig.cs

 public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //filters.Add(new HandleErrorAttribute()); // <== disable/remove } } 

Si bien es muy simple de implementar, el único inconveniente que veo en este enfoque es usar querystring para entregar información de excepción a la página de error de destino.

Aquí está mi solución. Usar [ExportModelStateToTempData] / [ImportModelStateFromTempData] es incómodo en mi opinión.

~ / Views / Home / Error.cshtml:

 @{ ViewBag.Title = "Error"; Layout = "~/Views/Shared/_Layout.cshtml"; } 

Error


@Html.ValidationMessage("Error")

~ / Controladores / HomeController.sc:

 public class HomeController : BaseController { public ActionResult Index() { return View(); } public ActionResult Error() { return this.View(); } ... } 

~ / Controladores / BaseController.sc:

 public class BaseController : Controller { public BaseController() { } protected override void OnActionExecuted(ActionExecutedContext filterContext) { if (filterContext.Result is ViewResult) { if (filterContext.Controller.TempData.ContainsKey("Error")) { var modelState = filterContext.Controller.TempData["Error"] as ModelState; filterContext.Controller.ViewData.ModelState.Merge(new ModelStateDictionary() { new KeyValuePair("Error", modelState) }); filterContext.Controller.TempData.Remove("Error"); } } if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult)) { if (filterContext.Controller.ViewData.ModelState.ContainsKey("Error")) { filterContext.Controller.TempData["Error"] = filterContext.Controller.ViewData.ModelState["Error"]; } } base.OnActionExecuted(filterContext); } } 

~ / Controladores / MyController.sc:

 public class MyController : BaseController { public ActionResult Index() { return View(); } public ActionResult Details(int id) { if (id != 5) { ModelState.AddModelError("Error", "Specified row does not exist."); return RedirectToAction("Error", "Home"); } else { return View("Specified row exists."); } } } 

Te deseo proyectos exitosos 😉

Puede conseguir que los errores funcionen correctamente sin hackear global.cs, jugar con HandleErrorAttribute, hacer Response.TrySkipIisCustomErrors, conectar Application_Error, o lo que sea:

En system.web (solo lo usual, encendido / apagado)

     

y en system.webServer

  

Ahora las cosas deberían comportarse como se esperaba, y puedes usar tu ErrorController para mostrar lo que necesites.

Tenía todo configurado, pero aún no podía ver las páginas de error adecuadas para el código de estado 500 en nuestro servidor de almacenamiento intermedio, a pesar de que todo funcionaba bien en los servidores de desarrollo local.

Encontré esta publicación del blog de Rick Strahl que me ayudó.

Necesitaba agregar Response.TrySkipIisCustomErrors = true; a mi código de manejo de error personalizado.

Parece que llegué tarde a la fiesta, pero deberías comprobar esto también.

Así que en system.web para almacenar excepciones dentro de la aplicación como HttpNotFound ()

        

y en system.webServer para detectar errores detectados por IIS y que no llegaron al marco de asp.net

         

En la última si te preocupa la respuesta del cliente, cambia el responseMode="Redirect" a responseMode="File" y sirve un archivo html estático, ya que este mostrará una página amigable con un código de respuesta 200.

En web.config, agregue esto debajo de la etiqueta system.webserver como se muestra a continuación,

        

y agregue un controlador como,

 public class ErrorController : Controller { // // GET: /Error/ [GET("/Error/NotFound")] public ActionResult NotFound() { Response.StatusCode = 404; return View(); } [GET("/Error/ErrorPage")] public ActionResult ErrorPage() { Response.StatusCode = 500; return View(); } } 

y agregue sus puntos de vista respetados, esto funcionará definitivamente, supongo para todos.

Esta solución la encontré de: Siglo de Neptuno