正是因为响应状态码是对错误或者异常语义最重要的表达,所以在很多情况下我们需要针对不同的响应状态码来定制显示的错误信息。针对响应状态码对错误页面的定制可以借助一个类型为StatusCodePagesMiddleware的中间件来实现,我们可以调用ApplicationBuilder相应的扩展方法来注册这个中间件。
DeveloperExceptionPageMiddleware和ExceptionHandlerMiddleware中间件都是在后续请求处理过程中抛出异常的情况下才会被调用,而StatusCodePagesMiddleware被调用的前提是后续请求助理过程中产生一个错误响应状态码(范围在400~599之间)。如果仅仅希望显示一个统一的错误页面,我们可以按照如下的形式调用扩展方法UseStatusCodePages注册这个中间件,传入该方法的两个参数分别表示响应采用的媒体类型和主体内容。
public class Program { public static void Main() { new WebHostBuilder() .UseKestrel() .Configure(app=>app .UseStatusCodePages("text/plain", "Error occurred ({0})") .Run(context=> Task.Run(()=>context.Response.StatusCode = 500))) .Build() .Run(); } }
如上面的代码片段所示,应用在处理请求的时候总是会将响应状态码设置为500,所以最终的响应内容将由注册的StatusCodePagesMiddleware中间件来提供。我们调用UseStatusCodePages方法的时候将响应的媒体类型设置为“text/plain”,并将一段简单的错误消息作为了响应的主体内容。值得一提的时候,作为响应内容的字符串可以包含一个占位符({0}),StatusCodePagesMiddleware中间件最终会采用当前响应状态码来替换它。如果我们利用浏览器来访问这个应用,将会得到如下图所示的错误页面。
如果我们希望针对不同的错误状态码显示不同的错误页面,那么我们就需要将具体的请求处理逻辑实现在一个的状态码错误处理器中,并最终提供给StatusCodePagesMiddleware中间件。这个所谓的状态码错误处理器体现为一个类型为Func<StatusCodeContext, Task>的委托对象,作为输入的StatusCodeContext对象是对当前HttpContext的封装,同时承载着其他一些与错误处理相关的选项设置,我们将在本系列后续部分对这个类型进行详细介绍。
对于如下这个应用来说,它在处理任意一个请求是总是会随机地选择一个400~599之间的整数作为响应的状态码,所以客户端返回的响应内容总是通过注册的StatusCodePagesMiddleware中间件来提供。我们在调用另一个UseStatusCodePages方法重载的时候,为注册的中间件指定了一个Func<StatusCodeContext, Task>对象作为状态码错误处理器。
public class Program { private static Random _random = new Random(); public static void Main() { Func<StatusCodeContext, Task> handler = async context => { var response = context.HttpContext.Response; if (response.StatusCode < 500) { await response.WriteAsync($"Client error ({response.StatusCode})"); } else { await response.WriteAsync($"Server error ({response.StatusCode})"); } }; new WebHostBuilder() .UseKestrel() .Configure(app => app .UseStatusCodePages(handler) .Run(context => Task.Run(() => context.Response.StatusCode = _random.Next(400,599)))) .Build() .Run(); } }
我们指定的状态码错误处理器在处理请求的时候,根据响应状态码将错误分成客户端错误和服务端错误两种类型,并选择针对性的错误消息作为响应内容。当我们利用浏览器访问这个应用的时候,显示的错误消息将由响应状态码来决定。
在ASP.NET Core的世界里,针对请求的处理总是体现为一个RequestDelegate对象。如果请求的处理需要借助一个或者多个中间件来完成,我们可以将它们注册到ApplicationBuilder对象上并利用它将中间件管道转换成一个RequestDelegate对象。用于注册StatusCodePagesMiddleware中间件的UseStatusCodePage方法还具有另一个重载,它允许我们采用这种方式来创建一个RequestDelegate对象来完成最终的请求处理工作,所以上面演示的这个应用完全可以改写成如下的形式。