深入探究ASP.NET Core异常处理中间件 (2)

通过上面代码我们可以了解到我们可以通过自定义IDeveloperPageExceptionFilter的方式拦截到异常信息做处理

public class MyDeveloperPageExceptionFilter : IDeveloperPageExceptionFilter { private readonly ILogger<MyDeveloperPageExceptionFilter> _logger; public MyDeveloperPageExceptionFilter(ILogger<MyDeveloperPageExceptionFilter> logger) { _logger = logger; } public async Task HandleExceptionAsync(ErrorContext errorContext, Func<ErrorContext, Task> next) { _logger.LogInformation($"状态码:{errorContext.HttpContext.Response.StatusCode},异常信息:{errorContext.Exception.Message}"); await next(errorContext); } }

自定义的MyDeveloperPageExceptionFilter是需要注入的

services.AddSingleton<IDeveloperPageExceptionFilter,MyDeveloperPageExceptionFilter>();

同时还可以通过诊断日志的方式处理异常信息,我使用了Microsoft.Extensions.DiagnosticAdapter扩展包,所以可以定义强类型类

public class DiagnosticCollector { private readonly ILogger<DiagnosticCollector> _logger; public DiagnosticCollector(ILogger<DiagnosticCollector> logger) { _logger = logger; } [DiagnosticName("Microsoft.AspNetCore.Diagnostics.UnhandledException")] public void OnRequestStart(HttpContext httpContext, Exception exception) { _logger.LogInformation($"诊断日志收集到异常,状态码:{httpContext.Response.StatusCode},异常信息:{exception.Message}"); } }

通过这里可以看出,异常处理扩展性还是非常强的,这仅仅是.Net Core设计方式的冰山一角。刚才我们提到_exceptionHandler才是处理的核心,通过构造函数可知这个委托是通过DisplayException方法初始化的,接下来我们看这里的相关实现

private Task DisplayException(ErrorContext errorContext) { var httpContext = errorContext.HttpContext; var headers = httpContext.Request.GetTypedHeaders(); var acceptHeader = headers.Accept; //如果acceptHeader不存在或者类型不是text/plain,将以字符串的形式输出异常,比如通过代码或者Postman的方式调用并未设置头信息 if (acceptHeader == null || !acceptHeader.Any(h => h.IsSubsetOf(_textHtmlMediaType))) { httpContext.Response.ContentType = "text/plain"; var sb = new StringBuilder(); sb.AppendLine(errorContext.Exception.ToString()); sb.AppendLine(); sb.AppendLine("HEADERS"); sb.AppendLine("======="); foreach (var pair in httpContext.Request.Headers) { sb.AppendLine($"{pair.Key}: {pair.Value}"); } return httpContext.Response.WriteAsync(sb.ToString()); } //判断是否为编译时异常,比如视图文件可以动态编译 if (errorContext.Exception is ICompilationException compilationException) { return DisplayCompilationException(httpContext, compilationException); } //处理运行时异常 return DisplayRuntimeException(httpContext, errorContext.Exception); }

关于DisplayCompilationException我们这里就不做过多解释了,在Asp.Net Core中cshtml文件可以动态编译,有兴趣的同学可以自行了解。我们重点看下DisplayRuntimeException处理

private Task DisplayRuntimeException(HttpContext context, Exception ex) { //获取终结点信息 var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint; EndpointModel endpointModel = null; if (endpoint != null) { endpointModel = new EndpointModel(); endpointModel.DisplayName = endpoint.DisplayName; if (endpoint is RouteEndpoint routeEndpoint) { endpointModel.RoutePattern = routeEndpoint.RoutePattern.RawText; endpointModel.Order = routeEndpoint.Order; var httpMethods = endpoint.Metadata.GetMetadata<IHttpMethodMetadata>()?.HttpMethods; if (httpMethods != null) { endpointModel.HttpMethods = string.Join(", ", httpMethods); } } } var request = context.Request; //往视图还是个输出的模型,对于我们上面截图展示的信息对应的数据 var model = new ErrorPageModel { Options = _options, //异常详情 ErrorDetails = _exceptionDetailsProvider.GetDetails(ex), //查询参数相关 Query = request.Query, //Cookie信息 Cookies = request.Cookies, //头信息 Headers = request.Headers, //路由信息 RouteValues = request.RouteValues, //终结点信息 Endpoint = endpointModel }; var errorPage = new ErrorPage(model); //执行输出视图页面,也就是我们看到的开发者页面 return errorPage.ExecuteAsync(context); }

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

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