global.asax 中的Application_Error未捕获 WebAPI 中的错误

Application_Error in global.asax not catching errors in WebAPI

提问人:Andrew Gray 提问时间:2/27/2015 最后编辑:masonAndrew Gray 更新时间:10/29/2020 访问量:68557

问:

对于我正在从事的一个项目,我们正在实现的一件事是我们在一些团队的旧 ASP.NET 和 MVC 项目中拥有代码的东西——一个异常捕获器,它向开发团队发送一封电子邮件,其中包含异常经验和最相关的细节。Application_Error

这是它的样子:

Global.asax:

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    string path = "N/A";
    if (sender is HttpApplication)
        path = ((HttpApplication) sender).Request.Url.PathAndQuery;

    string args = string.Format("<b>Path:</b> {0}", path);

    // Custom code that generates an HTML-formatted exception dump
    string message = Email.GenerateExceptionMessage(ex, args);

    // Custom code that sends an email to the dev team.
    Email.SendUnexpectedErrorMessage("Some App", message);
}

不过,有一个“小”问题 - 当我故意让部分代码抛出异常以测试此机制时......

public static void GetMuffinsByTopping(string topping)
{
    throw new Exception("Test Exception!", new Exception("Test Inner Exception!!!"));

    // Actual repository code is unreachable while this test code is there
}

前端 JavaScript 立即拦截 HTTP 500 请求,但未达到上面提到的global.asax.cs代码(我在方法的第一行执行时设置了一个断点。

问题:我可以通过什么方式让“旧”处理程序调度错误电子邮件,以便我们团队的开发人员可以更轻松地调试我们的应用程序?Application_Error

C# asp.net .NET ASP.net-web-api 错误报告

评论

1赞 mason 2/27/2015
可以将错误处理逻辑抽象为一个单独的方法,该方法调用 Web API 方法正文,将 Web API 方法正文包装在 try/catch 中,然后手动将错误逻辑从 Web API 错误传递给抽象的方法。我敢肯定,这不是最干净的方式,但它应该易于实施和“工作”。若要获得更简洁的方法,可以查看 Web API ASP.NET 异常处理Application_Error
0赞 Andrew Gray 2/27/2015
我同意这是一个很好的“让它工作”的建议。我问这个问题的原因是,根据我的技术主管的指导,在每个 API 方法中都有一个实际上是我试图摆脱的。不过,如果所有其他方法都失败了,我们的团队可以将其用作后备。这将是一个非常令人讨厌的模式。try/catch
1赞 mason 2/27/2015
我编辑了我的最后一条评论。查看我提供的链接,并查看异常筛选器。这似乎是避免在所有 Web API 方法正文中使用 try/catch 的不错方法。
0赞 Andrew Gray 2/28/2015
这最终对我有用。您是否介意回答您的评论,即使用异常过滤器是更好的方法?

答:

83赞 mason 2/28/2015 #1

将错误处理逻辑抽象到其自己的函数中。创建 Web API 异常筛选器Application_Error

//register your filter with Web API pipeline
//this belongs in the Application_Start event in Global Application Handler class (global.asax)
//or some other location that runs on startup
GlobalConfiguration.Configuration.Filters.Add(new LogExceptionFilterAttribute());

//Create filter
public class LogExceptionFilterAttribute : ExceptionFilterAttribute 
{
    public override void OnException(HttpActionExecutedContext context)
    {
        ErrorLogService.LogError(context.Exception);
    }
}

//in global.asax or global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    ErrorLogService.LogError(ex);
} 

//common service to be used for logging errors
public static class ErrorLogService
{
    public static void LogError(Exception ex)
    {
        //Email developers, call fire department, log to database etc.
    }
}

来自 Web API 的错误不会触发 Application_Error 事件。但是我们可以创建一个异常过滤器并注册它来处理错误。另请参阅 ASP.NET Web API 2 中的全局错误处理

评论

1赞 Leandro De Mello Fagundes 1/3/2017
为什么?我不明白。这是最好的做法?
7赞 mason 1/3/2017
@LeandroDeMelloFagundes 当 Web API 出现异常时,不会触发 Application_Error 事件。您可能已经登录Application_Error来处理 ASP.NET 异常。因此,将该逻辑放在可从应用于 Web API 操作方法的 Application_Error 或异常筛选器使用的通用服务中,遵循 DRY 原则。没有重复的逻辑。
1赞 Leandro De Mello Fagundes 1/3/2017
感谢@mason,阅读 asp.net 网站上关于您的答案的链接也有助于了解发生的事情。伟大的aswer
1赞 Paul 5/24/2017
你应该把行放在哪里:GlobalConfiguration.Configuration.Filters.Add(new LogExceptionFilterAttribute());是在global.asax Application_Start()还是Application_Error()中?
0赞 mason 5/24/2017
@Paul 它应该位于 中,因为您正在注册过滤器,以便它知道如何在发生错误时处理错误。您始终在应用程序启动时注册任何筛选器。Application_Start
-2赞 danteMesquita 11/12/2019 #2

就我而言,我不喜欢使用 Web.config。然后我在 Global.asax 文件中创建了上面的代码:

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

        //Not Found (When user digit unexisting url)
        if(ex is HttpException && ((HttpException)ex).GetHttpCode() == 404)
        {
            HttpContextWrapper contextWrapper = new HttpContextWrapper(this.Context);

            RouteData routeData = new RouteData();
            routeData.Values.Add("controller", "Error");
            routeData.Values.Add("action", "NotFound");

            IController controller = new ErrorController();
            RequestContext requestContext = new RequestContext(contextWrapper, routeData);
            controller.Execute(requestContext);
            Response.End();
        }
        else //Unhandled Errors from aplication
        {
            ErrorLogService.LogError(ex);
            HttpContextWrapper contextWrapper = new HttpContextWrapper(this.Context);

            RouteData routeData = new RouteData();
            routeData.Values.Add("controller", "Error");
            routeData.Values.Add("action", "Index");

            IController controller = new ErrorController();
            RequestContext requestContext = new RequestContext(contextWrapper, routeData);
            controller.Execute(requestContext);
            Response.End();
        }
    }

thtat 是我ErrorController.cs

public class ErrorController : Controller
{
    // GET: Error
    public ViewResult Index()
    {
        Response.StatusCode = 500;
        Exception ex = Server.GetLastError();
        return View("~/Views/Shared/SAAS/Error.cshtml", ex);
    }

    public ViewResult NotFound()
    {
        Response.StatusCode = 404;
        return View("~/Views/Shared/SAAS/NotFound.cshtml");
    }
}

这是我从@mason课上复制ErrorLogService.cs

//common service to be used for logging errors
public static class ErrorLogService
{
    public static void LogError(Exception ex)
    {
        //Do what you want here, save log in database, send email to police station
    }
}

评论

1赞 facechomp 6/19/2020
这没有回答 OP 的问题,他的代码也没有引用 Web.config。