ASP.NET Core应用错误处理之ExceptionHandlerMiddleware中间

DeveloperExceptionPageMiddleware中间件利用呈现出来的错误页面实现抛出异常和当前请求的详细信息以辅助开发人员更好地进行纠错诊断工作,而ExceptionHandlerMiddleware中间件则是面向最终用户的,我们可以利用它来显示一个友好的定制化的错误页面。按照惯例,我们还是先来看看ExceptionHandlerMiddleware的类型定义。

public class ExceptionHandlerMiddleware { public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<ExceptionHandlerOptions> options, DiagnosticSource diagnosticSource); public Task Invoke(HttpContext context); } public class ExceptionHandlerOptions { public RequestDelegate ExceptionHandler { get; set; } public PathString ExceptionHandlingPath { get; set; } }

与DeveloperExceptionPageMiddleware类似,我们在创建一个ExceptionHandlerMiddleware对象的时候同样需要提供一个携带配置选项的对象,从上面的代码可以看出这是一个ExceptionHandlerOptions。具体来说,一个ExceptionHandlerOptions对象通过其ExceptionHandler属性提供了一个最终用来处理请求的RequestDelegate对象。如果希望发生异常后自动重定向到某个指定的路径,我们可以利用ExceptionHandlerOptions对象的ExceptionHandlingPath属性来指定这个路径。我们一般会调用ApplicationBuilder的扩展方法UseExceptionHandler来注册ExceptionHandlerMiddleware中间件,这些重载的UseExceptionHandler方法会采用如下的方式完整中间件的注册工作。

public static class ExceptionHandlerExtensions { public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app)=> app.UseMiddleware<ExceptionHandlerMiddleware>(); public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, ExceptionHandlerOptions options) => app.UseMiddleware<ExceptionHandlerMiddleware>(Options.Create(options)); public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, string errorHandlingPath) { return app.UseExceptionHandler(new { ExceptionHandlingPath = new PathString(errorHandlingPath) }); } public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, Action<IApplicationBuilder> configure) { IApplicationBuilder newBuilder = app.New(); configure(newBuilder); return app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandler = newBuilder.Build() }); } }

一、异常处理器

ExceptionHandlerMiddleware中间件处理请求的本质就是在后续请求处理过程中出现异常的情况下采用注册的异常处理器来处理并响应请求,这个异常处理器就是我们再熟悉不过的RequestDelegate对象。该中间件采用的请求处理逻辑大体上可以通过如下所示的这段代码来体现。

public class ExceptionHandlerMiddleware { private RequestDelegate _next; private ExceptionHandlerOptions _options; public ExceptionHandlerMiddleware(RequestDelegate next, IOptions<ExceptionHandlerOptions> options,…) { _next = next; _options = options.Value; … } public async Task Invoke(HttpContext context) { try { await _next(context); } catch { context.Response.StatusCode = 500; context.Response.Clear(); if (_options.ExceptionHandlingPath.HasValue) { context.Request.Path = _options.ExceptionHandlingPath; } RequestDelegate handler = _options.ExceptionHandler ?? _next; await handler(context); } } }

如上面的代码片段所示,如果后续的请求处理过程中出现异常,ExceptionHandlerMiddleware中间件会利用一个作为异常处理器的RequestDelegate对象来完成最终的请求处理工作。如果在创建ExceptionHandlerMiddleware时提供的ExceptionHandlerOptions携带着这么一个RequestDelegate对象,那么它将作为最终使用的异常处理器,否则作为异常处理器的实际上就是后续的中间件。换句话说,如果我们没有通过ExceptionHandlerOptions显式指定一个异常处理器,ExceptionHandlerMiddleware中间件会在后续管道处理请求抛出异常的情况下将请求再次传递给后续管道。

当ExceptionHandlerMiddleware最终利用异常处理器来处理请求之前,它会对请求做一些前置处理工作,比如它会将响应状态码设置为500,比如清空当前所有响应内容等。如果我们利用ExceptionHandlerOptions的ExceptionHandlingPath属性设置了一个重定向路径,它会将该路径设置为当前请求的路径。除了这些,ExceptionHandlerMiddleware中间件实际上做了一些没有反应在上面这段代码片段中的工作。

二、异常的传递与请求路径的恢复

由于ExceptionHandlerMiddleware中间件总会利用一个作为异常处理器的RequestDelegate对象来完成最终的异常处理工作,为了让后者能够得到抛出的异常,该中间件应该采用某种方式将异常传递给它。除此之外,由于ExceptionHandlerMiddleware中间件会改变当前请求的路径,当整个请求处理完成之后,它必须将请求路径恢复成原始的状态,否则前置的中间件就无法获取到正确的请求路径。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdsdpf.html