您现在的位置是:网站首页> 编程资料编程资料
ASP.NET Core应用错误处理之StatusCodePagesMiddleware中间件针对响应码呈现错误页面_实用技巧_
2023-05-24
436人已围观
简介 ASP.NET Core应用错误处理之StatusCodePagesMiddleware中间件针对响应码呈现错误页面_实用技巧_
前言
StatusCodePagesMiddleware中间件与ExceptionHandlerMiddleware中间件比较类似,它们都是在后续请求处理过程中“出错”的情况下利用一个错误处理器来完成最终的请求处理与响应的任务。它们之间的差异在于对“错误”的界定上,对于ExceptionHandlerMiddleware中间件来说,它所谓的错误就是抛出异常,但是对于StatusCodePagesMiddleware中间件来说,则将介于400~599之间的响应状态码视为错误。如下面的代码片段所示,StatusCodePagesMiddleware中间件也采用“标准”的定义方式,针对它的配置选项通过一个对应的对象以Options模式的形式提供给它。
public class StatusCodePagesMiddleware { public StatusCodePagesMiddleware(RequestDelegate next, IOptions options); public Task Invoke(HttpContext context); } 除了针对错误的界定,StatusCodePagesMiddleware和ExceptionHandlerMiddleware这两个中间件对于错误处理器的表达也不相同。我们知道ExceptionHandlerMiddleware中间件使用的错误处理器实际上就是一个类型为RequestDelegate的委托对象,但是错误处理器之于StatusCodePagesMiddleware中间件来说则是一个类型为Func
public class StatusCodePagesOptions { public Func HandleAsync { get; set; } } 我们知道一个RequestDelegate对象相当于一个类型为Func
public class StatusCodeContext { public HttpContext HttpContext { get; } public RequestDelegate Next { get; } public StatusCodePagesOptions Options { get; } public StatusCodeContext(HttpContext context, StatusCodePagesOptions options, RequestDelegate next); }一、针对响应状态码的错误处理
由于采用了针对响应状态码的错误处理策略,所以实现在StatusCodePagesMiddleware中间件中的所有错误处理操作只会发生在当前响应状态码在400~599之间的情况,如下所示的代码片段体现了这一点。从下面给出的代码片段可以看出,StatusCodePagesMiddleware中间件在决定是否执行错误处理操作时除了会查看当前响应状态码之外,还会查看响应内容以及媒体类型,如果已经包含了响应内容或者设置了媒体类型,该中间件将不会执行任何操作。
public class StatusCodePagesMiddleware { private RequestDelegate _next; private StatusCodePagesOptions _options; public StatusCodePagesMiddleware(RequestDelegate next, IOptions options) { _next = next; _options = options.Value; } public async Task Invoke(HttpContext context) { await _next(context); var response = context.Response; if ((response.StatusCode >= 400 && response.StatusCode <= 599) &&!response.ContentLength.HasValue && string.IsNullOrEmpty(response.ContentType)) { await _options.HandleAsync(new StatusCodeContext(context, _options, _next)); } } } StatusCodePagesMiddleware中间件针对错误的处理非常简单,它只需要从StatusCodePagesOptions对象中提取出作为错误处理器的这个Func
二、阻止异常处理
如果当前响应已经被写入了内容,或者响应的媒体类型已经被预先设置,那么StatusCodePagesMiddleware中间件将不会再执行任何的错误处理操作。这种情况实际上代表由后续中间件构成的管道可能需要自行控制当前的响应,所以StatusCodePagesMiddleware中间件不应该再做任何的干预。从这个意义上来讲,StatusCodePagesMiddleware中间件仅仅是作为一种后备的错误处理机制而已。
更进一步来将,如果后续的某个中间件返回了一个状态码在400~599之间的响应,并且这个响应只有报头集合没有主体(媒体类型自然也不会设置),那么按照我们在上面给出的错误处理逻辑,StatusCodePagesMiddleware中间件还是会按照自己的策略来处理并响应请求。为了解决这种情况下,我们必须赋予后续中间件一个能够阻止StatusCodePagesMiddleware中间件进行错误处理的能力。
阻止StatusCodePagesMiddleware中间件进行错误处理的机制是借助于一个名为StatusCodePagesFeature的特性来实现的。StatusCodePagesFeature对应如下这个IStatusCodePagesFeature接口,它具有唯一的布尔类型的属性成员Enabled。默认使用的StatusCodePagesFeature类型实现了这个接口,默认情况下这个开关是开启的。
public interface IStatusCodePagesFeature { bool Enabled { get; set; } } public class StatusCodePagesFeature : IStatusCodePagesFeature { public bool Enabled { get; set; } = true ; }StatusCodePagesMiddleware中间件在将请求交付给后续管道之前,它会创建一个StatusCodePagesFeature特性对象并将其添加到当前HttpContext之中。当最终决定是否执行错误处理操作的时候,它还会通过这个特性检验是否某个后续的中间件不希望自己“画蛇添足”地进行不必要的错误处理,如下的代码片段很好的体现了这一点。
public class StatusCodePagesMiddleware { … public async Task Invoke(HttpContext context) { StatusCodePagesFeature feature = new StatusCodePagesFeature(); context.Features.Set(feature); await _next(context); var response = context.Response; if ((response.StatusCode >= 400 && response.StatusCode <= 599) && !response.ContentLength.HasValue &&string.IsNullOrEmpty(response.ContentType) && feature.Enabled) { await _options.HandleAsync(new StatusCodeContext(context, _options, _next)); } } } 我们通过一个简单的实例来演示如果利用这个StatusCodePagesFeature特性来屏蔽StatusCodePagesMiddleware中间件。在下面这个应用中,我们将针对请求的处理定义在Invoke方法中,该方法会返回一个状态码为“401 Unauthorized”的响应。我们通过随机数让这个方法会在50%的情况下利用StatusCodePagesFeature特性来阻止StatusCodePagesMiddleware中间件自身对错误的处理。我们通过调用扩展方法UseStatusCodePages注册的StatusCodePagesMiddleware中间件会直接响应一个内容为“Error occurred!”的字符串。
public class Program { public static void Main() { new WebHostBuilder() .UseKestrel() .Configure(app => app .UseStatusCodePages(async context => await context.HttpContext.Response.WriteAsync("Error occurred!")) .Run(Invoke)) .Build() .Run(); } private static Random _random = new Random(); private static Task Invoke(HttpContext context) { context.Response.StatusCode = 401; if (_random.Next() % 2 == 0) { context.Features.Get().Enabled = false; } return Task.CompletedTask; } } 对于针对该应用的请求来说,我们会得到如下两种不同的响应。没有主体内容响应是通过Invoke方法产生的,这种情况下发生在StatusCodePagesMiddleware中间件通过StatusCodePagesFeature特性被屏蔽的时候。具有主体内容的响应则来源于StatusCodePagesMiddleware中间件。
HTTP/1.1 401 Unauthorized Date: Sun, 18 Dec 2016 01:59:37 GMT Server: Kestrel Content-Length: 15 Error occurred! HTTP/1.1 401 Unauthorized Date: Sun, 18 Dec 2016 01:59:38 GMT Content-Length: 0 Server: Kestrel
三、注册StatusCodePagesMiddleware中间件
我们在大部分情况下都会调用ApplicationBuilder相应的扩展方法来注册StatusCodePagesMiddleware中间件。对于StatusCodePagesMiddleware中间件的注册来说,除了我们已经很熟悉的UseStatusCodePages方之外,还具有额外一些扩展方法供我们选择。
UseStatusCodePages
我们可以调用如下三个UseStatusCodePages方法重载来注册StatusCodePagesMiddleware中间件。不论我们调用那个重载,系统最终都会根据提供的StatusCodePagesOptions对象调用构造函数来创建这个中间件对象,而且这个StatusCodePagesOptions必须具有一个作为错误处理器的Func
public static class StatusCodePagesExtensions { public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app) { return app.UseMiddleware(); } public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, StatusCodePagesOptions options) { return app.UseMiddleware(Options.Create(options)); } public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, Func handler) { return app.UseStatusCodePages(new StatusCodePagesOptions { HandleAsync = handler }); } } 由于StatusCodePagesMiddleware中间件最终的目的还是将定制的错误信息响应给客户端,所以我们可以在注册该中间件的时候直接指定响应的内容和媒体类型,这样的注册方式可以通过调用如下这个UseStatusCodePages方法来完成。从如下所示的代码片段我们不难看出,我们通过bodyFormat方法指定的实际上是一个模板,它可以包含一个表示响应状态的占位符(“{0}”)。
public static class StatusCodePagesExtensions { public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, string contentType, string bodyFormat) { return app.UseStatusCodePages(context => { var body = string.Format(CultureInfo.InvariantCulture, bodyFormat, context.HttpContext.Response.StatusCode); context.HttpContext.Response.ContentType = contentType; return context.HttpContext.Response.WriteAsync(body); }); } }UseStatusCodePagesWithRedirects
如果我们调用UseStatusCodePagesWithRedirects方法,可以让注册的StatusCodePagesMiddleware中间件向指定的路径发送一个客户端重定向。从如下所示的实现代码可以看出,这个作为参数locationFormat的重定向地址也是一个模板,它可以包含一个表示响应状态的占位符(“{0}”)。我们可以指定一个完整的地址,也可以指定一个相对于PathBase的相对路径,后者需要包含表示基地址的“~/”前缀。
public static class StatusCodePagesExtensions { public static IApplicationBuilder UseStatusCodePagesWithRedirects(this IApplicationBuilder app, string locationFormat) { if (locationFormat.StartsWith("~")) { locationFormat = locationFormat.Substring(1); return app.UseStatusCodePages(context => { var location = string.Format(CultureInfo.InvariantCulture, locationFormat, context.HttpContext.Response.StatusCode); context.HttpContext.Response.Redirect(context.HttpContext.Request.PathBase + location); return Task.CompletedTask; }); } else { return app.UseStatusCodePages(context => { var location = string.Format(CultureInfo.InvariantCulture, locationFormat, context.HttpContext.Response.StatusCode); context.HttpContext.Response.Redirect(location); return Task.CompletedTask; }); } } }我们通过一个简单的应用来演示针对客户端重定向的错误页面呈现方式。我们在如下这个应用中注册了一个路由模板为“error/{statuscode}”的路由,路由参数“statuscode”自然代表响应的状态码。在作为路由处理器的HandleError方法中,我们会直接响应一个包含响应状态码的字符串。我们调用UseStatusCodePagesWithRedirects方法注册StatusCodePagesMiddleware中间件的时候将重定义路径设置为“error/{0}”。
public class Program { private static Random _random = new Random(); public static void Main() { new WebHostBuilder() .UseKestrel() .ConfigureServices(svcs => svcs.AddRouting()) .Configure(app => app .UseStatusCodePagesWithRedirects("~/error/{0}") .UseRouter(builder=>builder.MapRoute("error/{statuscode}", HandleError)) .Run(context=>Task.Run(()=>context.Response.StatusCode = _random.Next(400,599)))) .Build() .Run(); } private async static Task HandleError(HttpContext context) { var statusCode = context.GetRouteData().Values["statuscode"]; await context.Response.WriteAsync($"Error occurred ({statusCode})"); } }提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
相关内容
- ASP.NET Core应用错误处理之ExceptionHandlerMiddleware中间件呈现“定制化错误页面”_实用技巧_
- .NET Core Dapper操作mysql数据库的实现方法_实用技巧_
- 详解log4net的使用_实用技巧_
- 详解.net core日记记录_实用技巧_
- Asp.Net Core Web应用程序—探索_实用技巧_
- Visual Studio 2015下载和安装图文教程_实用技巧_
- 详解asp.net core重新加载应用配置_实用技巧_
- 详解.NET Core使用Quartz执行调度任务进阶_实用技巧_
- .NET CORE中使用AutoMapper进行对象映射的方法_实用技巧_
- 详解使用DotNet CLI创建自定义的WPF项目模板_实用技巧_
点击排行
本栏推荐
