如何为 C # MVC4 WebAPI 应用程序记录全局所有异常?

背景资料

我正在为一个客户端开发一个 API 服务层,我已经被要求捕获和记录全球所有的错误。

因此,虽然像未知端点(或动作)这样的事情很容易通过使用 ELMAH 或者通过向 Global.asax添加类似的东西来处理:

protected void Application_Error()
{
Exception unhandledException = Server.GetLastError();
//do more stuff
}

. . 未处理的错误,不相关的路由不会被记录。例如:

public class ReportController : ApiController
{
public int test()
{
var foo = Convert.ToInt32("a");//Will throw error but isn't logged!!
return foo;
}
}

我还尝试通过注册这个过滤器来全局设置 [HandleError]属性:

filters.Add(new HandleErrorAttribute());

但这也不会记录所有错误。

问题/问题

如何拦截类似上面调用 /test所产生的错误,以便记录它们?似乎这个答案应该是显而易见的,但到目前为止我已经试过了所有我能想到的办法。

理想情况下,我希望在错误日志中添加一些内容,比如请求用户的 IP 地址、日期、时间等等。我还希望能够在遇到错误时自动向支持人员发送电子邮件。所有这些我都可以做,只要我可以拦截这些错误时,他们发生了!

解决!

多亏了达林 · 迪米特洛夫,我接受了他的回答,我想通了这个问题

以下是起作用的方法:

1)在名称空间中添加一个自定义过滤器:

public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is BusinessException)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Exception"
});


}


//Log Critical errors
Debug.WriteLine(context.Exception);


throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An error occurred, please try again or contact the administrator."),
ReasonPhrase = "Critical Exception"
});
}
}

2)现在在 WebApiConfig类中全局注册过滤器:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
config.Filters.Add(new ExceptionHandlingAttribute());
}
}

OR 可以跳过注册,只用 [ExceptionHandling]属性装饰一个控制器。

85832 次浏览

将整个事件包装在 try/catch 中并记录未处理的异常,然后传递它。除非有更好的内置方法。

这里有一个参考 捕获所有(已处理或未处理)异常

(编辑: 哦 API)

你有没有想过做一些像处理错误动作过滤器这样的事情

[HandleError]
public class BaseController : Controller {...}

您还可以创建一个自定义版本的 [HandleError],用它可以写入错误信息和所有其他详细信息以进行日志记录

如果您的 web API 驻留在 ASP.NET 应用程序中,那么对于代码中所有未处理的异常(包括您显示的测试操作中的异常) ,将调用 Application_Error事件。因此,您所要做的就是在 Application _ Error 事件中处理这个异常。在示例代码中,您显示只处理 HttpException类型的异常,而 Convert.ToInt32("a")代码显然不是这种情况。因此,一定要记录并处理这里的所有异常:

protected void Application_Error()
{
Exception unhandledException = Server.GetLastError();
HttpException httpException = unhandledException as HttpException;
if (httpException == null)
{
Exception innerException = unhandledException.InnerException;
httpException = innerException as HttpException;
}


if (httpException != null)
{
int httpCode = httpException.GetHttpCode();
switch (httpCode)
{
case (int)HttpStatusCode.Unauthorized:
Response.Redirect("/Http/Error401");
break;


// TODO: don't forget that here you have many other status codes to test
// and handle in addition to 401.
}
else
{
// It was not an HttpException. This will be executed for your test action.
// Here you should log and handle this case. Use the unhandledException instance here
}
}
}

Web API 中的异常处理可以在不同的级别进行:

  • 可以注册为全局异常筛选器的自定义异常筛选器属性

    [AttributeUsage(AttributeTargets.All)]
    public class ExceptionHandlingAttribute : ExceptionFilterAttribute
    {
    public override void OnException(HttpActionExecutedContext context)
    {
    if (context.Exception is BusinessException)
    {
    throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
    {
    Content = new StringContent(context.Exception.Message),
    ReasonPhrase = "Exception"
    });
    }
    
    
    //Log Critical errors
    Debug.WriteLine(context.Exception);
    
    
    throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
    {
    Content = new StringContent("An error occurred, please try again or contact the administrator."),
    ReasonPhrase = "Critical Exception"
    });
    }
    }
    
  • custom action invoker

    public class MyApiControllerActionInvoker : ApiControllerActionInvoker
    {
    public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
    {
    var result = base.InvokeActionAsync(actionContext, cancellationToken);
    
    
    if (result.Exception != null && result.Exception.GetBaseException() != null)
    {
    var baseException = result.Exception.GetBaseException();
    
    
    if (baseException is BusinessException)
    {
    return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
    {
    Content = new StringContent(baseException.Message),
    ReasonPhrase = "Error"
    
    
    });
    }
    else
    {
    //Log critical error
    Debug.WriteLine(baseException);
    
    
    return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
    {
    Content = new StringContent(baseException.Message),
    ReasonPhrase = "Critical Error"
    });
    }
    }
    
    
    return result;
    }
    }
    

为什么重新抛出等? 这个工程,它会使服务返回状态500等

public class LogExceptionFilter : ExceptionFilterAttribute
{
private static readonly ILog log = LogManager.GetLogger(typeof (LogExceptionFilter));


public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
log.Error("Unhandeled Exception", actionExecutedContext.Exception);
base.OnException(actionExecutedContext);
}
}

作为之前答案的补充。

昨天,ASP.NET Web API 2.1正式成为 释放
它提供了另一个全局处理异常的机会。
详情载于 样本

简而言之,添加全局异常记录器和/或全局异常处理程序(只有一个)。
将它们添加到配置中:

public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();


// There can be multiple exception loggers.
// (By default, no exception loggers are registered.)
config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());


// There must be exactly one exception handler.
// (There is a default one that may be replaced.)
config.Services.Replace(typeof(IExceptionHandler), new GenericTextExceptionHandler());
}

他们意识到:

public class ElmahExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
...
}
}


public class GenericTextExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new InternalServerErrorTextPlainResult(
"An unhandled exception occurred; check the log for more information.",
Encoding.UTF8,
context.Request);
}
}