其整体实现思路就是捕获后续执行过程中出现的异常,如果出现异常则包装异常信息以及Http上下文和路由相关信息到ErrorPageModel模型中,然后这个模型作为异常展示界面的数据模型进行展示。
UseExceptionHandlerUseExceptionHandler可能是我们在实际开发过程中使用最多的方式。UseDeveloperExceptionPage固然强大,但是返回的终究还是供开发者使用的界面,通过UseExceptionHandler我们可以通过自己的方式处理异常信息,这里就需要我自己编码
app.UseExceptionHandler(configure => { configure.Run(async context => { var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var ex = exceptionHandlerPathFeature?.Error; if (ex != null) { context.Response.ContentType = "text/plain;charset=utf-8"; await context.Response.WriteAsync(ex.ToString()); } }); }); //或 app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandler = async context => { var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var ex = exceptionHandlerPathFeature?.Error; if (ex != null) { context.Response.ContentType = "text/plain;charset=utf-8"; await context.Response.WriteAsync(ex.ToString()); } } }); //或 app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandlingPath = new PathString("/exception") });通过上面的实现方式我们大概可以猜出扩展方法的几种类型找到源码位置ExceptionHandlerExtensions扩展类
public static class ExceptionHandlerExtensions { public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app) { return app.UseMiddleware<ExceptionHandlerMiddleware>(); } public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, string errorHandlingPath) { return app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandlingPath = new PathString(errorHandlingPath) }); } public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, Action<IApplicationBuilder> configure) { //创建新的执行管道 var subAppBuilder = app.New(); configure(subAppBuilder); var exceptionHandlerPipeline = subAppBuilder.Build(); return app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandler = exceptionHandlerPipeline }); } public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, ExceptionHandlerOptions options) { return app.UseMiddleware<ExceptionHandlerMiddleware>(Options.Create(options)); } }通过UseExceptionHandler扩展方法我们可以知道这么多扩展方法其实本质都是在构建ExceptionHandlerOptions我们来看一下大致实现
public class ExceptionHandlerOptions { /// <summary> /// 指定处理异常的终结点路径 /// </summary> public PathString ExceptionHandlingPath { get; set; } /// <summary> /// 指定处理异常的终结点委托 /// </summary> public RequestDelegate ExceptionHandler { get; set; } }这个类非常简单,要么指定处理异常信息的具体终结点路径,要么自定义终结点委托处理异常信息。通过上面的使用示例可以很清楚的看到这一点,接下来我们查看一下ExceptionHandlerMiddleware中间件的大致实现
public class ExceptionHandlerMiddleware { private readonly RequestDelegate _next; private readonly ExceptionHandlerOptions _options; private readonly ILogger _logger; private readonly Func<object, Task> _clearCacheHeadersDelegate; private readonly DiagnosticListener _diagnosticListener; public ExceptionHandlerMiddleware( RequestDelegate next, ILoggerFactory loggerFactory, IOptions<ExceptionHandlerOptions> options, DiagnosticListener diagnosticListener) { _next = next; _options = options.Value; _logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>(); _clearCacheHeadersDelegate = ClearCacheHeaders; _diagnosticListener = diagnosticListener; //ExceptionHandler和ExceptionHandlingPath不同同时不存在 if (_options.ExceptionHandler == null) { if (_options.ExceptionHandlingPath == null) { throw new InvalidOperationException(Resources.ExceptionHandlerOptions_NotConfiguredCorrectly); } else { _options.ExceptionHandler = _next; } } } public Task Invoke(HttpContext context) { ExceptionDispatchInfo edi; try { var task = _next(context); //task未完成情况 if (!task.IsCompletedSuccessfully) { return Awaited(this, context, task); } return Task.CompletedTask; } catch (Exception exception) { edi = ExceptionDispatchInfo.Capture(exception); } return HandleException(context, edi); //处理未完成task static async Task Awaited(ExceptionHandlerMiddleware middleware, HttpContext context, Task task) { ExceptionDispatchInfo edi = null; try { //等待完成 await task; } catch (Exception exception) { //收集异常信息 edi = ExceptionDispatchInfo.Capture(exception); } if (edi != null) { await middleware.HandleException(context, edi); } } } }