asp.net-mvc - handling - custom error mvc 5
¿Cómo hacer que ELMAH funcione con el atributo ASP.NET MVC[HandleError]? (8)
Estoy tratando de usar ELMAH para registrar errores en mi aplicación ASP.NET MVC, sin embargo, cuando uso el atributo [HandleError] en mis controladores, ELMAH no registra ningún error cuando ocurren.
Como supongo que es porque ELMAH solo registra errores no manejados y el atributo [HandleError] está manejando el error, por lo que no hay necesidad de registrarlo.
¿Cómo modifico o cómo modifico el atributo para que ELMAH pueda saber que hubo un error y lo registre?
Edición: Permítame asegurarme de que todos entiendan, sé que puedo modificar el atributo, pero esa no es la pregunta que hago. ya por el atributo ... Lo que pregunto es si hay una manera de hacer que ELMAH vea el error y lo registre a pesar de que el atributo lo manejó ... Busqué y no veo ningún método al que llamar para forzarlo a iniciar sesión el error....
¡Esto es exactamente lo que necesitaba para la configuración de mi sitio MVC!
OnException
una pequeña modificación al método OnException
para manejar múltiples instancias de HandleErrorAttribute
, como lo sugirió Atif Aziz:
tenga en cuenta que es posible que tenga que tener cuidado de que si varias instancias de
HandleErrorAttribute
están vigentes, no se produzca un registro duplicado.
Simplemente context.ExceptionHandled
antes de invocar la clase base, solo para saber si alguien más manejó la excepción antes del controlador actual.
Funciona para mí y publico el código en caso de que alguien más lo necesite y para preguntar si alguien sabe si pasé por alto algo.
Espero que sea útil:
public override void OnException(ExceptionContext context)
{
bool exceptionHandledByPreviousHandler = context.ExceptionHandled;
base.OnException(context);
Exception e = context.Exception;
if (exceptionHandledByPreviousHandler
|| !context.ExceptionHandled // if unhandled, will be logged anyhow
|| RaiseErrorSignal(e) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(e);
}
Ahora hay un paquete ELMAH.MVC en NuGet que incluye una solución mejorada de Atif y también un controlador que maneja la interfaz elmah dentro del enrutamiento MVC (ya no es necesario usar ese axd)
El problema con esa solución (y con todos los que están aquí) es que de una u otra forma el controlador de errores de elmah en realidad está manejando el error, ignorando lo que podría querer configurar como una etiqueta customError o mediante ErrorHandler o su propio controlador de errores
La mejor solución IMHO es crear un filtro que actuará al final de todos los otros filtros y registrar los eventos que ya se han manejado. El módulo elmah debe encargarse de registrar los otros errores que no son manejados por la aplicación. Esto también le permitirá usar el monitor de estado y todos los demás módulos que se pueden agregar a asp.net para ver los eventos de error
Escribí esto mirando con el reflector al ErrorHandler dentro de elmah.mvc
public class ElmahMVCErrorFilter : IExceptionFilter
{
private static ErrorFilterConfiguration _config;
public void OnException(ExceptionContext context)
{
if (context.ExceptionHandled) //The unhandled ones will be picked by the elmah module
{
var e = context.Exception;
var context2 = context.HttpContext.ApplicationInstance.Context;
//TODO: Add additional variables to context.HttpContext.Request.ServerVariables for both handled and unhandled exceptions
if ((context2 == null) || (!_RaiseErrorSignal(e, context2) && !_IsFiltered(e, context2)))
{
_LogException(e, context2);
}
}
}
private static bool _IsFiltered(System.Exception e, System.Web.HttpContext context)
{
if (_config == null)
{
_config = (context.GetSection("elmah/errorFilter") as ErrorFilterConfiguration) ?? new ErrorFilterConfiguration();
}
var context2 = new ErrorFilterModule.AssertionHelperContext((System.Exception)e, context);
return _config.Assertion.Test(context2);
}
private static void _LogException(System.Exception e, System.Web.HttpContext context)
{
ErrorLog.GetDefault((System.Web.HttpContext)context).Log(new Elmah.Error((System.Exception)e, (System.Web.HttpContext)context));
}
private static bool _RaiseErrorSignal(System.Exception e, System.Web.HttpContext context)
{
var signal = ErrorSignal.FromContext((System.Web.HttpContext)context);
if (signal == null)
{
return false;
}
signal.Raise((System.Exception)e, (System.Web.HttpContext)context);
return true;
}
}
Ahora, en tu configuración de filtro quieres hacer algo como esto:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//These filters should go at the end of the pipeline, add all error handlers before
filters.Add(new ElmahMVCErrorFilter());
}
Tenga en cuenta que dejé un comentario allí para recordarle a la gente que si quieren agregar un filtro global que realmente maneje la excepción, debe ir ANTES de este último filtro, de lo contrario, se encontrará con el caso en el que ElmahMVCErrorFilter ignorará la excepción no controlada porque no ha sido manejado y debe ser registrado por el módulo Elmah pero luego el siguiente filtro marca la excepción como manejado y el módulo la ignora, lo que da como resultado que la excepción nunca se convierta en elmah.
Ahora, asegúrate de que los ajustes para elmah en tu webconfig tengan un aspecto como este:
<add key="elmah.mvc.disableHandler" value="false" /> <!-- This handles elmah controller pages, if disabled elmah pages will not work -->
<add key="elmah.mvc.disableHandleErrorFilter" value="true" /> <!-- This uses the default filter for elmah, set to disabled to use our own -->
<add key="elmah.mvc.requiresAuthentication" value="false" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.allowedRoles" value="*" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.route" value="errortracking" /> <!-- Base route for elmah pages -->
El importante aquí es "elmah.mvc.disableHandleErrorFilter", si esto es falso, usará el controlador dentro de elmah.mvc que realmente manejará la excepción usando el HandleErrorHandler predeterminado que ignorará sus configuraciones personalizadas de error
Esta configuración le permite establecer sus propias etiquetas ErrorHandler en clases y vistas, mientras aún registra esos errores a través de ElmahMVCErrorFilter, agregando una configuración personalizada de error a su web.config a través del módulo elmah, incluso escribiendo sus propios Controladores de errores. Lo único que debe hacer es recordar no agregar ningún filtro que realmente maneje el error antes del filtro elmah que hemos escrito. Y me olvidé de mencionar: no hay duplicados en elmah.
Lo siento, pero creo que la respuesta aceptada es una exageración. Todo lo que necesitas hacer es esto:
public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
public void OnException (ExceptionContext context)
{
// Log only handled exceptions, because all other will be caught by ELMAH anyway.
if (context.ExceptionHandled)
ErrorSignal.FromCurrentContext().Raise(context.Exception);
}
}
y luego registrarlo (el orden es importante) en Global.asax.cs:
public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
}
Para mí fue muy importante hacer funcionar el registro de correo electrónico. Después de un tiempo, descubro que esto necesita solo 2 líneas de código más en el ejemplo de Atif.
public class HandleErrorWithElmahAttribute : HandleErrorAttribute
{
static ElmahMVCMailModule error_mail_log = new ElmahMVCMailModule();
public override void OnException(ExceptionContext context)
{
error_mail_log.Init(HttpContext.Current.ApplicationInstance);
[...]
}
[...]
}
Espero que esto ayude a alguien :)
Puede tomar el código anterior e ir un paso más allá introduciendo una fábrica de controladores personalizados que inyecta el atributo HandleErrorWithElmah en cada controlador.
Para obtener más información, consulte mi serie de blogs sobre el inicio de sesión en MVC. El primer artículo cubre cómo configurar y ejecutar Elmah para MVC.
Hay un enlace al código descargable al final del artículo. Espero que ayude.
Soy nuevo en ASP.NET MVC. Me enfrenté con el mismo problema, el siguiente es mi trabajo en mi Erorr.vbhtml (funciona si solo necesita registrar el error usando el registro de Elmah)
@ModelType System.Web.Mvc.HandleErrorInfo
@Code
ViewData("Title") = "Error"
Dim item As HandleErrorInfo = CType(Model, HandleErrorInfo)
//To log error with Elmah
Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(New Elmah.Error(Model.Exception, HttpContext.Current))
End Code
<h2>
Sorry, an error occurred while processing your request.<br />
@item.ActionName<br />
@item.ControllerName<br />
@item.Exception.Message
</h2>
¡Es sencillo!
Una solución completamente alternativa es no usar el HandleErrorAttribute
MVC y, en cambio, confiar en el manejo de errores de ASP.Net, con el que está diseñado para trabajar con Elmah.
HandleErrorAttribute
eliminar el HandleErrorAttribute
global predeterminado de App_Start / FilterConfig (o Global.asax), y luego configurar una página de error en su Web.config:
<customErrors mode="RemoteOnly" defaultRedirect="~/error/" />
Tenga en cuenta que esto puede ser una URL enrutada de MVC, por lo que lo anterior se redireccionaría a la acción ErrorController.Index
cuando se produce un error.
Puede subclase HandleErrorAttribute
y anular su miembro OnException
(no es necesario copiarlo) para que registre la excepción con ELMAH y solo si la implementación base lo maneja. La cantidad mínima de código que necesita es la siguiente:
using System.Web.Mvc;
using Elmah;
public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (!context.ExceptionHandled)
return;
var httpContext = context.HttpContext.ApplicationInstance.Context;
var signal = ErrorSignal.FromContext(httpContext);
signal.Raise(context.Exception, httpContext);
}
}
La implementación base se invoca primero, lo que le da la oportunidad de marcar la excepción como manejada. Sólo entonces se señala la excepción. El código anterior es simple y puede causar problemas si se usa en un entorno donde el HttpContext
puede no estar disponible, como las pruebas. Como resultado, querrá un código que sea más defensivo (al costo de ser un poco más largo):
using System.Web;
using System.Web.Mvc;
using Elmah;
public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (!context.ExceptionHandled // if unhandled, will be logged anyhow
|| TryRaiseErrorSignal(context) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(context);
}
private static bool TryRaiseErrorSignal(ExceptionContext context)
{
var httpContext = GetHttpContextImpl(context.HttpContext);
if (httpContext == null)
return false;
var signal = ErrorSignal.FromContext(httpContext);
if (signal == null)
return false;
signal.Raise(context.Exception, httpContext);
return true;
}
private static bool IsFiltered(ExceptionContext context)
{
var config = context.HttpContext.GetSection("elmah/errorFilter")
as ErrorFilterConfiguration;
if (config == null)
return false;
var testContext = new ErrorFilterModule.AssertionHelperContext(
context.Exception,
GetHttpContextImpl(context.HttpContext));
return config.Assertion.Test(testContext);
}
private static void LogException(ExceptionContext context)
{
var httpContext = GetHttpContextImpl(context.HttpContext);
var error = new Error(context.Exception, httpContext);
ErrorLog.GetDefault(httpContext).Log(error);
}
private static HttpContext GetHttpContextImpl(HttpContextBase context)
{
return context.ApplicationInstance.Context;
}
}
Esta segunda versión intentará usar primero la señalización de error de ELMAH, que involucra la canalización totalmente configurada, como el registro, el envío de correos, el filtrado y todo lo que tenga. Si falla, intenta ver si el error debe filtrarse. Si no, el error simplemente se registra. Esta implementación no maneja las notificaciones de correo. Si se puede señalar la excepción, se enviará un correo si está configurado para hacerlo.
También es posible que tenga que tener cuidado de que si varias instancias de HandleErrorAttribute
están vigentes, no se produzca el registro duplicado, pero los dos ejemplos anteriores deberían comenzar.