Asp.Net Core Razor Pages 异常处理中断了应用程序

Asp.Net Core Razor Pages Exception Handling breaks the application

我正在构建 Asp.Net Core 2.2 Razor Pages 应用程序。我正在编写一个全局异常处理以避免在许多地方出现 try catch 块。我关注了文章 - Exception Handling Reference

这是Demo Solution Link

这是我的代码,

Startup.cs

if(env.IsDevelopment())
{
    //app.UseDeveloperExceptionPage(new DeveloperExceptionPageOptions {
    //    SourceCodeLineCount = 2
    //});
    //app.UseDatabaseErrorPage();
    app.UseExceptionHandler("/Error");
    app.UseStatusCodePagesWithReExecute("/Error/{0}");
    app.UseExceptionMiddleware();
} else {...}

ExceptionMiddleware.cs

public ExceptionMiddleware(RequestDelegate next,IMailService emailSender)
{
    _next = next;
    _emailSender = emailSender;
}

public async Task InvokeAsync(HttpContext context)
{
    try
    {
        await _next(context);
    } catch(Exception ex)
    {
        EmailException(context,ex);
    }
}

private async void EmailException(HttpContext context,Exception ex)
{
    var uaString = context.Request.Headers["User-Agent"].ToString();
    var ipAnonymizedString = context.Connection.RemoteIpAddress.AnonymizeIP();
    var userId = "Unknown";
    var profileId = "Unknown";
    if(context.User.Identity.IsAuthenticated)
    {
        userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
        profileId = context.User.GetUserClaim(ClaimsKey.ProfileId);
    }

    var sb = new StringBuilder($"An error has occurred on {context.Request.Host}. \r\n \r\n");
    sb.Append($"Path = {context.Request.Path} \r\n \r\n");
    sb.Append($"Error Message = {ex.Message} \r\n");
    sb.Append($"Error Source = {ex.Source} - {profileId} \r\n");

    if(ex.InnerException != null)
    {
        sb.Append($"Inner Exception = {ex.InnerException.ToString()} \r\n");
    } else
    {
        sb.Append("Inner Exception = null \r\n");
    }

    sb.Append($"Error StackTrace = {ex.StackTrace} \r\n");

    await _emailSender.SendMasterEmailAsync($"Error on {context.Request.Host}.",sb.ToString(),uaString,ipAnonymizedString,userId);

    throw ex;
}

现在,我在我的页面获取请求之一中创建异常,如下所示,

public void OnGet()
{
    throw new Exception();
}

中间件捕获异常并在发送电子邮件后抛出该异常。然后 Visual Studio 表示应用程序处于中断模式并且 visual studio 停止。但是我的 Error.cshtml.cs 没有在应用程序中断和停止之前捕获异常。知道我哪里出错了吗?

在他的 async guidance GitHub 存储库中,David Fowler 指出:

Use of async void in ASP.NET Core applications is ALWAYS bad. Avoid it, never do it. Typically, it's used when developers are trying to implement fire and forget patterns triggered by a controller action. Async void methods will crash the process if an exception is thrown.

对于您所描述的错误,最后一行至关重要 - 您的应用程序 抛出异常,这会导致进程崩溃。

对于 EmailException,您应该从使用 async void 切换到使用 async Task,如下所示:

public ExceptionMiddleware(RequestDelegate next,IMailService emailSender)
{
    ...
}

public async Task InvokeAsync(HttpContext context)
{
    try
    {
        await _next(context);
    }
    catch(Exception ex)
    {
        await EmailException(context, ex); // Await.
    }
}

private async Task EmailException(HttpContext context, Exception ex) // Return a Task.
{
    ...

    throw ex;
}

如果您尝试使用 async void 的原因是您不必等待电子邮件发送后再向用户返回响应,您可能需要查看使用一个Background Task