exception处理中间件和页面

我是中间件概念的新手,我目前正在努力处理我的MVC Core项目中的exception。

我想要发生的是捕获,记录exception,然后将用户发送到带有消息的友好错误页面。 起初,我试图在中间件中管理所有这些,但意识到我可能没有正确地做到这一点。

因此,如果我想要这个流程发生,我应该使用我的exception日志记录中间件和app.UseExceptionHandler(“/ Error”),以便中间件重新抛出exception到页面? 如果是这样,我如何在错误页面中获取exception详细信息? 我想首先拦截的exception处理机制应该是Configure中的最后一个吗?这是正确的吗?

我发现的所有示例都严格处理HTTP状态代码错误,如404; 我正在寻找处理实际的exception(及其子类)。 这样,在我的View页面中,我可以在适用的情况下抛出自己的exception(例如,如果为必填字段提供了一个null。)

Startup.cs片段:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, LoanDbContext context) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseStatusCodePages(); if (env.IsDevelopment() || env.IsEnvironment("qa")) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Error"); } app.UseMiddleware(loggerFactory); // ... rest of Configure is irrelevant to this issue 

MyExceptionHandler.cs

 using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System; using System.Data.SqlClient; using System.Threading.Tasks; namespace MyCompany.MyProject.Helpers { ///  /// A custom Exception Handler Middleware which can be re-used in other projects. ///  public sealed class MyExceptionHandler { private readonly RequestDelegate _next; private readonly ILogger _logger; public MyExceptionHandler(RequestDelegate next, ILoggerFactory loggerFactory) { _next = next; _logger = loggerFactory.CreateLogger(); } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (Exception ex) { HandleException(ex); } } // I realize this function looks pointless, but it will have more meat to it eventually. public void HandleException(Exception ex) { if (ex is ArgumentException argEx) { _logger.LogError(0, argEx, argEx.Message); } else if (ex is InvalidOperationException ioEx) { _logger.LogError(0, ioEx, "An Invalid Operation Exception occurred. This is usually caused by a database call that expects " + "one result, but receives none or more than one."); } else if (ex is SqlException sqlEx) { _logger.LogError(0, sqlEx, $"A SQL database exception occurred. Error Number {sqlEx.Number}"); } else if (ex is NullReferenceException nullEx) { _logger.LogError(0, nullEx, $"A Null Reference Exception occurred. Source: {nullEx.Source}."); } else if (ex is DbUpdateConcurrencyException dbEx) { _logger.LogError(0, dbEx, "A database error occurred while trying to update your item. This is usually due to someone else modifying the item since you loaded it."); } else { _logger.LogError(0, ex, "An unhandled exception has occurred.") } } } } 

我应该使用我的exception日志记录中间件和app.UseExceptionHandler("/Error")以便中间件重新抛出exception到页面?

是。

仅使用您的示例的片段。

 public async Task Invoke(HttpContext context) { try { await _next(context); } catch (Exception ex) { HandleException(ex); // re -throw the original exception // after logging the information throw; } } 

以上将在记录之后重新抛出原始错误,以便管道中的其他处理程序将捕获它并执行开箱即用的处理。

ASP.NET核心中的源error handling

如何在错误页面中获取exception详细信息?

使用IExceptionHandlerPathFeature获取exception和Path

 public IActionResult Error() { // Get the details of the exception that occurred var exceptionFeature = HttpContext.Features.Get(); if (exceptionFeature != null) { // Get which route the exception occurred at string routeWhereExceptionOccurred = exceptionFeature.Path; // Get the exception that occurred Exception exceptionThatOccurred = exceptionFeature.Error; // TODO: Do something with the exception // Log it with Serilog? // Send an e-mail, text, fax, or carrier pidgeon? Maybe all of the above? // Whatever you do, be careful to catch any exceptions, otherwise you'll end up with a blank page and throwing a 500 } return View(); } 

使用IExceptionHandlerPathFeature在ASP.NET Core中添加全局error handling和日志记录的源代码

在上面的示例中,他们提到在视图中进行日志记录,但您已经在自定义处理程序中完成了该操作。

在渲染错误视图时要特别注意这个小注释。

无论你做什么,都要小心抓住任何例外情况,否则你最终会得到一个空白页并投掷500

通过插入管道,您可以避免重新发明框架已经提供的function,并且还可以管理跨领域的问题。