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

请求处理过程中抛出的异常和原始请求路径的恢复是通过相应的特性完成的。具体来说,传递这两者的特性分别叫做ExceptionHandlerFeature和ExceptionHandlerPathFeature,对应的接口分别为IExceptionHandlerFeature和IExceptionHandlerPathFeature,如下面的代码片段所示,后者继承前者。默认使用的ExceptionHandlerFeature实现了这两个接口。

public interface IExceptionHandlerFeature { Exception Error { get; } } public interface IExceptionHandlerPathFeature : IExceptionHandlerFeature { string Path { get; } } public class ExceptionHandlerFeature : IExceptionHandlerPathFeature, { public Exception Error { get; set; } public string Path { get; set; } }

当ExceptionHandlerMiddleware中间件将代码当前请求的HttpContext传递给请求处理器之前,它会按照如下所示的方式根据抛出的异常的原始的请求路径创建一个ExceptionHandlerFeature对象,该对象最终被添加到HttpContext之上。当整个请求处理流程完全结束之后,ExceptionHandlerMiddleware中间件会借助这个特性得到原始的请求路径,并将其重新应用到当前请求上下文上。

public class ExceptionHandlerMiddleware { ... public async Task Invoke(HttpContext context) { try { await _next(context); } catch(Exception ex) { context.Response.StatusCode = 500; var feature = new ExceptionHandlerFeature() { Error = ex, Path = context.Request.Path, }; context.Features.Set<IExceptionHandlerFeature>(feature); context.Features.Set<IExceptionHandlerPathFeature>(feature); if (_options.ExceptionHandlingPath.HasValue) { context.Request.Path = _options.ExceptionHandlingPath; } RequestDelegate handler = _options.ExceptionHandler ?? _next; try { await handler(context); } finally { context.Request.Path = originalPath; } } } }

在具体进行异常处理的时候,我们可以从当前HttpContext中提取这个ExceptionHandlerFeature对象,进而获取抛出的异常和原始的请求路径。如下面的代码所示,我们利用HandleError方法来呈现一个定制的错误页面。在这个方法中,我们正式借助于这个ExceptionHandlerFeature特性得到抛出的异常,并将它的类型、消息以及堆栈追踪显示出来。

public class Program { public static void Main() { new WebHostBuilder() .UseKestrel() .ConfigureServices(svcs=>svcs.AddRouting()) .Configure(app => app .UseExceptionHandler("/error") .UseRouter(builder=>builder.MapRoute("error", HandleError)) .Run(context=> Task.FromException(new InvalidOperationException("Manually thrown exception")))) .Build() .Run(); } private async static Task HandleError(HttpContext context) { context.Response.ContentType = "text/html"; Exception ex = context.Features.Get<IExceptionHandlerPathFeature>().Error; await context.Response.WriteAsync("<html><head><title>Error</title></head><body>"); await context.Response.WriteAsync($"<h3>{ex.Message}</h3>"); await context.Response.WriteAsync($"<p>Type: {ex.GetType().FullName}"); await context.Response.WriteAsync($"<p>StackTrace: {ex.StackTrace}"); await context.Response.WriteAsync("</body></html>"); }

在上面这个应用中,我们注册了一个模板为“error”的路由指向这个HandleError方法。对于通过调用扩展方法UseExceptionHandler注册的ExceptionHandlerMiddleware来说,我们将该路径设置为异常处理路径。那么对于任意从浏览器发出的请求,都会得到如下图所示的错误页面。

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

三、清除缓存

对于一个用于获取资源的GET请求来说,如果请求目标是一个相对稳定的资源,我们可以采用客户端缓存的方式避免相同资源的频繁获取和传输。对于作为资源提供者的Web应用来说,当它在处理请求的时候,除了将目标资源作为响应的主体内容之外,它还需要设置用于控制缓存的相关响应报头。由于缓存在大部分情况下只适用于成功的响应,如果服务端在处理请求过程中出现异常,之前设置的缓存报头是不应该出现在响应报文中。对于ExceptionHandlerMiddleware中间件来说,清楚缓存报头也是它负责的一项重要工作。

我们同样可以通过一个简单的实例来演示ExceptionHandlerMiddleware中间件针对缓存响应报头的清除。在如下这个应用中,我们将针对请求的处理实现在Invoke方法中,它有50%的可能会抛出异常。不论是返回正常的响应内容还是抛出异常,这个方法都会先设置一个“Cache-Control”的响应报头,并将缓存时间设置为1个小时(“Cache-Control: max-age=3600”)。

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

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