ASP.NET MVC HandleError

110

¿Cómo hago con el [HandleError]filtro en asp.net MVC Preview 5?
Configuré customErrors en mi archivo Web.config

<customErrors mode="On" defaultRedirect="Error.aspx">
  <error statusCode="403" redirect="NoAccess.htm"/>
  <error statusCode="404" redirect="FileNotFound.htm"/>
</customErrors>

y pon [HandleError] encima de mi clase de controlador de esta manera:

[HandleError]
public class DSWebsiteController: Controller
{
    [snip]
    public ActionResult CrashTest()
    {
        throw new Exception("Oh Noes!");
    }
}

Luego dejo que mis controladores hereden de esta clase y llamo a CrashTest () en ellos. Visual Studio se detiene ante el error y después de presionar f5 para continuar, me redirigen a Error.aspx? Aspxerrorpath = / sxi.mvc / CrashTest (donde sxi es el nombre del controlador usado. Por supuesto, no se puede encontrar la ruta y obtengo "Error del servidor en la aplicación '/'". 404.

Este sitio fue portado de la vista previa 3 a 5. Todo funciona (no fue mucho trabajo para portar) excepto el manejo de errores. Cuando creo un proyecto completamente nuevo, el manejo de errores parece funcionar.

Ideas?

--Nota--
Dado que esta pregunta tiene más de 3K vistas ahora, pensé que sería beneficioso poner lo que estoy usando actualmente (ASP.NET MVC 1.0). En el proyecto mvc contrib hay un atributo brillante llamado "RescueAttribute" Probablemente deberías comprobarlo también;)

Boris Callens
fuente
Enlace a la RescueAttributefuente: mvccontrib.codeplex.com/SourceControl/changeset/view/…
Peter

Respuestas:

158
[HandleError]

Cuando solo proporciona el atributo HandleError a su clase (o su método de acción para el caso), cuando se produce una excepción no controlada, MVC buscará primero una Vista correspondiente denominada "Error" en la carpeta Vista del controlador. Si no puede encontrarlo allí, procederá a buscar en la carpeta Vista compartida (que debería tener un archivo Error.aspx de forma predeterminada)

[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "LameErrorHandling")]

También puede acumular atributos adicionales con información específica sobre el tipo de excepción que está buscando. En ese momento, puede dirigir el Error a una vista específica que no sea la vista predeterminada "Error".

Para obtener más información, consulte la publicación del blog de Scott Guthrie al respecto.

Elijah Manor
fuente
1
Gracias por la información ampliada. No sé qué hice mal, pero creé un nuevo proyecto, porté todas las vistas, controladores y modelos existentes y ahora funciona. Sin embargo, no sabía nada de las vistas selectivas.
Boris Callens
Si se desea registrar estas excepciones, ¿sería este un lugar aceptable para agregar un código subyacente a la vista?
Peter J
6
Icónico, aquí está mi respuesta "más vale tarde que nunca" a su comentario: en su lugar, puede subclasificar el HandleErrorAttribute y anular su método "OnException": Luego, inserte cualquier registro o acciones personalizadas que desee. Luego, puede manejar completamente la excepción (configurando context.ExceptionHandled en verdadero), o volver al método OnException de la clase base para esto. Aquí hay un artículo excelente que puede ayudar con esto: blog.dantup.me.uk/2009/04/…
Funka
Tengo gran cantidad de controladores de modo puedo manejar esto dentro global.asaxcomo esta para mostrar un mensaje a los usuarios?
shaijut
@ ¿Qué hay de usar la misma página de error que PartialView y representarla en el cuadro de diálogo modal después de que ocurra la excepción? ¿Podría dar un ejemplo en su respuesta? Lo que quiero lograr se explicó sobre el manejo de errores globales usando PartialView en MVC .
Jack
23

También debe tenerse en cuenta que los errores que no establecen el código de error http en 500

(por ejemplo, UnauthorizedAccessException)

no será manejado por el filtro HandleError.

Corin Blaikie
fuente
1
Es cierto, pero echa un vistazo a RescueAttribute en MVC contrib (enlace en OP)
Boris Callens
14

Solución para el código de error http a 500 este es un atributo llamado [ERROR] ponerlo en una acción

public class Error: System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
    {

            if (filterContext.HttpContext.IsCustomErrorEnabled)
            {
                filterContext.ExceptionHandled = true;

            }
            base.OnException(filterContext);
            //OVERRIDE THE 500 ERROR  
           filterContext.HttpContext.Response.StatusCode = 200;
    }

    private static void RaiseErrorSignal(Exception e)
    {
        var context = HttpContext.Current;
      // using.Elmah.ErrorSignal.FromContext(context).Raise(e, context);
    } 

}

//EJEMPLO:

[Error]
[HandleError]
[PopulateSiteMap(SiteMapName="Mifel1", ViewDataKey="Mifel1")]
public class ApplicationController : Controller
{
}
Raul
fuente
12

Los atributos en MVC son muy útiles en el manejo de errores en el método get y post , también rastrea la llamada ajax .

Cree un controlador base en su aplicación y heredelo en su controlador principal (EmployeeController).

EmployeeController de clase pública: BaseController

Agregue el siguiente código en el controlador base.

/// <summary>
/// Base Controller
/// </summary>
public class BaseController : Controller
{       
    protected override void OnException(ExceptionContext filterContext)
    {
        Exception ex = filterContext.Exception;

        //Save error log in file
        if (ConfigurationManager.AppSettings["SaveErrorLog"].ToString().Trim().ToUpper() == "TRUE")
        {
            SaveErrorLog(ex, filterContext);
        }

        // if the request is AJAX return JSON else view.
        if (IsAjax(filterContext))
        {
            //Because its a exception raised after ajax invocation
            //Lets return Json
            filterContext.Result = new JsonResult()
            {
                Data = Convert.ToString(filterContext.Exception),
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();

            filterContext.Result = new ViewResult()
            {
                //Error page to load
                ViewName = "Error",
                ViewData = new ViewDataDictionary()
            };

            base.OnException(filterContext);
        }
    }

    /// <summary>
    /// Determines whether the specified filter context is ajax.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    private bool IsAjax(ExceptionContext filterContext)
    {
        return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
    }

    /// <summary>
    /// Saves the error log.
    /// </summary>
    /// <param name="ex">The ex.</param>
    /// <param name="filterContext">The filter context.</param>
    void SaveErrorLog(Exception ex, ExceptionContext filterContext)
    {
        string logMessage = ex.ToString();

        string logDirectory = Server.MapPath(Url.Content("~/ErrorLog/"));

        DateTime currentDateTime = DateTime.Now;
        string currentDateTimeString = currentDateTime.ToString();
        CheckCreateLogDirectory(logDirectory);
        string logLine = BuildLogLine(currentDateTime, logMessage, filterContext);
        logDirectory = (logDirectory + "\\Log_" + LogFileName(DateTime.Now) + ".txt");

        StreamWriter streamWriter = null;
        try
        {
            streamWriter = new StreamWriter(logDirectory, true);
            streamWriter.WriteLine(logLine);
        }
        catch
        {
        }
        finally
        {
            if (streamWriter != null)
            {
                streamWriter.Close();
            }
        }
    }

    /// <summary>
    /// Checks the create log directory.
    /// </summary>
    /// <param name="logPath">The log path.</param>
    bool CheckCreateLogDirectory(string logPath)
    {
        bool loggingDirectoryExists = false;
        DirectoryInfo directoryInfo = new DirectoryInfo(logPath);
        if (directoryInfo.Exists)
        {
            loggingDirectoryExists = true;
        }
        else
        {
            try
            {
                Directory.CreateDirectory(logPath);
                loggingDirectoryExists = true;
            }
            catch
            {
            }
        }

        return loggingDirectoryExists;
    }

    /// <summary>
    /// Builds the log line.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    /// <param name="logMessage">The log message.</param>
    /// <param name="filterContext">The filter context.</param>       
    string BuildLogLine(DateTime currentDateTime, string logMessage, ExceptionContext filterContext)
    {
        string controllerName = filterContext.RouteData.Values["Controller"].ToString();
        string actionName = filterContext.RouteData.Values["Action"].ToString();

        RouteValueDictionary paramList = ((System.Web.Routing.Route)(filterContext.RouteData.Route)).Defaults;
        if (paramList != null)
        {
            paramList.Remove("Controller");
            paramList.Remove("Action");
        }

        StringBuilder loglineStringBuilder = new StringBuilder();

        loglineStringBuilder.Append("Log Time : ");
        loglineStringBuilder.Append(LogFileEntryDateTime(currentDateTime));
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("Username : ");
        loglineStringBuilder.Append(Session["LogedInUserName"]);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ControllerName : ");
        loglineStringBuilder.Append(controllerName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ActionName : ");
        loglineStringBuilder.Append(actionName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("----------------------------------------------------------------------------------------------------------");
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append(logMessage);
        loglineStringBuilder.Append(System.Environment.NewLine);
        loglineStringBuilder.Append("==========================================================================================================");

        return loglineStringBuilder.ToString();
    }

    /// <summary>
    /// Logs the file entry date time.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileEntryDateTime(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd-MMM-yyyy HH:mm:ss");
    }

    /// <summary>
    /// Logs the name of the file.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileName(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd_MMM_yyyy");
    }

}

==============================================

Encuentra el directorio: Root / App_Start / FilterConfig.cs

Agregue el siguiente código:

/// <summary>
/// Filter Config
/// </summary>
public class FilterConfig
{
    /// <summary>
    /// Registers the global filters.
    /// </summary>
    /// <param name="filters">The filters.</param>
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

Seguimiento de error AJAX:

Llame a la función CheckAJAXError en la carga de la página de diseño.

function CheckAJAXError() {
    $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {

        var ex;
        if (String(thrownError).toUpperCase() == "LOGIN") {
            var url = '@Url.Action("Login", "Login")';
            window.location = url;
        }
        else if (String(jqXHR.responseText).toUpperCase().indexOf("THE DELETE STATEMENT CONFLICTED WITH THE REFERENCE CONSTRAINT") >= 0) {

            toastr.error('ReferanceExistMessage');
        }
        else if (String(thrownError).toUpperCase() == "INTERNAL SERVER ERROR") {
            ex = ajaxSettings.url;
            //var url = '@Url.Action("ErrorLog", "Home")?exurl=' + ex;
            var url = '@Url.Action("ErrorLog", "Home")';
            window.location = url;
        }
    });
};
Sandip - Desarrollador Full Stack
fuente
Está filtrando detalles de excepción en las solicitudes AJAX, no siempre quiere eso. Su código de registro no es seguro para subprocesos. Asume que hay una vista de Error y la devuelve, sin alterar el código de respuesta. Luego, revisa ciertas cadenas de error en JavaScript (¿qué pasa con la localización?). Básicamente, está repitiendo lo que ya dice una respuesta existente: "Anular OnExceptionpara manejar excepciones" , pero muestra una implementación bastante mala.
CodeCaster
¿Qué es el parámetro @ School.Resource.Messages.ReferanceExist?
Jack
@CodeCaster ¿Conoce alguna forma mejor de usar este tipo de método de manejo de errores con AJAX en ASP.NET MVC? ¿Alguna ayuda por favor?
Jack
Devuelve 400 o 500, como se pretende con HTTP. No busque cadenas específicas en el cuerpo de la respuesta.
CodeCaster
@CodeCaster ¿Podría echar un vistazo al manejo de errores globales usando PartialView en MVC con respecto a este problema?
Jack
4

Falta Error.aspx :) En la vista previa 5, se encuentra en su carpeta Vistas / Compartidos. Simplemente cópielo de un nuevo proyecto Preview 5.

Ricky
fuente
Gracias por la respuesta, pero ya copié la página Error.aspx. De hecho, podría haber sido algo que normalmente olvidaría, pero no esta vez. : P
Boris Callens
-1
    [HandleError]
    public class ErrorController : Controller
    {        
        [AcceptVerbs(HttpVerbs.Get)]
        public ViewResult NotAuthorized()
        {
            //401
            Response.StatusCode = (int)HttpStatusCode.Unauthorized;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult Forbidden()
    {
        //403
        Response.StatusCode = (int)HttpStatusCode.Forbidden;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult NotFound()
    {
        //404
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ViewResult ServerError()
    {
        //500
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

}

Jack
fuente