在ASP.NET核心中应用错误处理渲染错误页面的三种方法
前言
因为ASP.NET Core应用是一个同时处理多个请求的服务器应用,所以在处理某个请求的过程中抛出的异常不会导致整个应用的终止。出于安全考虑,为了避免敏感信息的泄露,客户端默认不会得到详细的错误信息,这无疑会增加开发环境中错误检查和纠正的难度。对于生产环境,我们也希望最终用户能够根据具体的错误类型得到针对性的、友好的错误消息。ASP.NET核心提供了相应的中间件来帮助我们呈现定制的错误信息,这些信息在NuGet包“Microsoft . aspnetcore . diagnostics”中定义。在重点介绍这些中间件之前,我们演示几个简单的例子,让读者对这些中间件的功能有一个大致的了解。
首先,显示开发人员异常页面
在正常情况下,如果ASP.NET核心在处理请求时出现异常,它通常会返回一个状态代码为“500内部服务器错误”的响应。为了避免一些敏感信息的泄露,详细的错误信息不会随响应一起发送给客户端,所以客户端只会得到一个非常笼统的错误消息。以下面的程序为例,服务器在处理每个请求时都会抛出一个InvalidOperationException类型的异常。
公共类Program { public static void Main(){ new WebHostBuilder()。UseKestrel()。配置(app=app。运行(上下文=任务。FromException(新的InvalidOperationException('手动引发的异常. '))))) .构建()。run();}}当我们使用浏览器访问这个应用程序时,总是会得到如下图所示的这个错误页面。可以看到,这个页面只告诉我们目标应用目前无法正常处理这个请求,除了响应状态码(“HTTP ERROR 500”)之外,没有提供任何有利于纠错的错误信息。
那么有些人可能会认为浏览器虽然没有显示任何详细的错误消息,但可能隐藏在收到的HTTP响应消息中。对于通过浏览器发出的这个请求,响应内容如下。我们可以发现,响应消息根本没有正文部分,有限数量的报头不携带任何与错误相关的信息。
Http/1.1 500内部服务器错误日期: fri,2016年12月09日23:42336018 GMT内容长度: 0服务器:红隼由于应用程序没有中断,浏览器没有显示任何有针对性的错误信息,开发人员在检查和更正错误时如何准确定位作为错误根源的代码行?具体来说,我们有两个解决方案,一个是使用日志,因为在ASP.NET Core的请求处理中出现的任何错误都会被写入日志,所以我们可以通过注册相应的LoggerProvider来获取写入的错误日志(例如,注册一个ConsoleLoggerProvider来将日志直接写入主机应用程序的控制台)。
至于另一种解决方案,就是直接显示一个包含错误对应信息的错误页面。由于此页面在开发环境中显示给开发人员,因此我们将此页面称为“开发人员异常页面”。自动页面呈现是通过使用名为developerexceptionpageMeddleware的中间件来完成的,该中间件可以通过调用ApplicationBuilder的扩展方法UseDeveloperExceptionPage来注册。
公共类Program { public static void Main(){ new WebHostBuilder()。UseKestrel()。配置(app=app。UseDeveloperExceptionPage()。运行(上下文=任务。FromException(新的InvalidOperationException('手动引发的异常. '))))) .构建()。run();}}一旦注册了developerxception page med dleware中间件,ASP.NET Core应用在处理请求时的异常信息会以下图的形式直接出现在浏览器上。我们可以在这个页面上看到几乎所有的错误信息,包括异常类型、消息和堆栈信息。
除了与抛出异常相关的信息外,开发人员异常页面还以下图所示的形式显示与当前请求上下文相关的信息,包括当前请求URL携带的所有查询字符串、所有请求头和Cookie内容。这样详细的信息无疑会极大地帮助开发者尽快找出错误的根源。
通过developerException呈现的错误页面Meddleware中间件仅供开发人员使用,详细的错误信息往往会携带一些敏感信息,所以一定要记住这个中间件只能在开发环境中注册。下面显示的代码片段反映了developerxception page Meddleware中间件的正确注册方法。
新的WebHostBuilder()。UseStartupStartup() …公共类Startup { public void Configure(IApplicationBuilder应用程序,IHostingEnvironment env) { if (env。IsDevelopment()) { app。usedeveloper exception page();}}}其次,显示定制的异常页面
developerxception pagemiddleware中间件在错误页面中直接呈现异常细节和基于当前请求的内容,为开发者的纠错诊断提供了极大的便利。然而,在生产环境中,我们倾向于向最终用户呈现定制的错误页面,这可以通过注册另一个名为ExceptionHandlerMiddleware的中间件来实现。顾名思义,这个中间件旨在提供一个异常处理程序来处理抛出的异常。实际上,这个所谓的异常处理程序是一个RequestDelegate类型的委托对象。ExceptionHandlerMiddleware中间件捕获抛出的异常,并使用它来响应当前请求。
或者以上面创建的总是抛出InvalidOperationException的应用程序为例。我们以下面的形式调用了ApplicationBuilder的扩展方法UseExceptionHandler来注册上面提到的ExceptionHandlerMiddleware中间件。扩展方法有一个类型为ExceptionHandlerOptions的参数,其ExceptionHandler属性返回RequestDelegate对象作为异常处理程序。
公共类Program { public static void Main(){ request delegate handler=async context=wait context。响应。WriteAsync('出现未处理的异常!');新的WebHostBuilder()。UseKestrel()。配置(app=app。UseExceptionHandler(新的ExceptionHandler选项{ ExceptionHandler=handler})。运行(上下文=任务。FromException(新的InvalidOperationException('手动引发的异常. '))))) .构建()。run();}}如上面的代码片段所示,这个作为异常处理程序的RequestDelegate只是发送了一个简单的错误消息(“发生了未处理的异常!”)作为响应的内容。当我们使用浏览器访问应用程序时,这个定制的错误消息将以图4所示的形式直接显示在浏览器上。
最后一个异常处理程序是RequestDelegate类型的委托对象,ApplicationBuilder能够创建这个委托对象。具体来说,我们可以根据异常处理的需要,将对应的中间件注册到一个ApplicationBuilder对象中,最后使用这个ApplicationBuilder根据注册的中间件创建一个RequestDelegate对象作为异常处理程序。如果异常处理需要通过一个或多个中间件来完成,我们可以用下面的形式调用另一个UseExceptionHandler方法重载。这个方法的参数类型是ActionIApplicationBuilder,我们调用它的Run方法注册一个中间件来响应一个简单的错误消息。
公共类Program { public static void Main(){ new WebHostBuilder()。UseKestrel()。配置(app=app。UseExceptionHandler(builder=builder。运行(异步上下文=等待上下文。响应。WriteAsync('出现未处理的异常!'))) .运行(上下文=任务。FromException(新的InvalidOperationException('手动引发的异常. '))))) .构建()。run();}}以上两种异常处理形式体现在提供RequestDelegate的委托对象来处理抛出的异常并完成最终的响应。如果应用程序设置了错误页,并且错误页有固定的路径,那么在处理异常时就不需要提供这个RequestDelegate对象,只需要重定向到错误页指向的路径即可。使用服务器端重定向的此异常处理方法可以通过调用以下形式的另一个UseExceptionHandler方法重载来完成。这个方法的参数表示重定向的目标路径("/error "),我们为这个路径注册了一个路由来响应定制的错误消息。
公共类Program { public static void Main(){ new WebHostBuilder()。UseKestrel()。configureSerVices(SVC=SVC。AddRouting())。配置(app=app。UseExceptionHandler('/error ')。UseRouter(builder=builder。MapRoute('错误',异步上下文=await上下文。响应。WriteAsync('出现未处理的异常!'))) .运行(上下文=任务。FromException(新的InvalidOperationException('手动引发的异常. '))))) .构建()。run();}}第三,自定义响应状态代码的错误页面
因为Web应用采用HTTP通信协议,所以我们应该尽可能低地满足HTTP标准,将协议规范中定义的语义应用到应用中。异常或错误的语义表达主要体现在HTTP协议层的响应消息的状态代码中。具体来说,HTTP通信的错误大致可以分为以下两种类型:
客户端错误:表示服务器无法正常处理请求,因为客户端提供了不正确的请求信息,响应状态码在400到499之间。服务器端错误:表示服务器在处理请求的过程中由于自身的问题而出错,响应状态码在500到509之间。正是因为响应状态码是错误或者异常语义最重要的表达,所以在很多情况下我们需要根据不同的响应状态码来定制显示的错误信息。可以通过StatusCodePagesMiddleware的中间件,根据响应状态代码定制错误页面。我们可以调用ApplicationBuilder相应的扩展方法来注册这个中间件。
developerxception page middleware和ExceptionHandlerMiddleware中间件只有在后续请求处理过程中抛出异常时才会被调用,而StatusCodePagesMiddleware调用的前提是在后续请求助手处理过程中会生成一个错误响应状态代码(范围从400到599)。如果我们只想显示一个统一的错误页面,可以调用扩展方法UseStatusCodePages以下面的形式注册这个中间件,传入这个方法的两个参数分别表示响应的媒体类型和主要内容。
公共类Program { public static void Main(){ new WebHostBuilder()。UseKestrel()。配置(app=app。UseStatusCodePages('纯文本/纯文本','出现错误({0})')。运行(上下文=任务。运行(()=上下文。Response.StatusCode=500))。构建()。run();}}如上面的代码片段所示,应用程序在处理请求时总是将响应状态代码设置为500,因此最终的响应内容将由注册的StatusCodePagesMiddleware中间件提供。当我们调用UseStatusCodePages方法时,我们将响应的媒体类型设置为“文本/纯文本”,并将一条简单的错误消息作为响应的主要内容。值得一提的是,作为响应内容的字符串可以包含一个占位符({0}),StatusCodePagesMiddleware最终会将其替换为当前的响应状态代码。如果我们使用浏览器访问这个应用程序,我们将得到如下图所示的错误页面。
如果我们想要为不同的错误状态代码显示不同的错误页面,那么我们需要在一个新的状态代码错误处理器中实现特定的请求处理逻辑,并最终将其提供给StatusCodePagesMiddleware中间件。这种所谓的状态代码错误处理程序体现为一个委托对象,类型为FuncStatusCodeContext,Task。输入StatusCodeContext对象封装了当前的HttpContext,并携带了与错误处理相关的其他选项。我们将在本系列的后续部分详细介绍这种类型。
对于下面的应用程序,在处理任何请求时,它总是随机选择一个介于400和599之间的整数作为响应状态代码,因此客户端返回的响应内容总是通过注册的StatusCodePagesMiddleware提供。当我们调用另一个UseStatusCodePages方法重载时,我们为注册的中间件指定一个funcstatuscodecontext,任务对象用作状态代码错误处理程序。
公共类Program {私有静态Random _ Random=new Random();public static void Main(){ functusscodecontext,Task handler=async context={ var response=context。响应;if(响应。StatusCode 500) {等待响应。WriteAsync($)客户端错误({响应。status code })');} else {等待响应。WriteAsync($)服务器错误({响应。status code })');} };新的WebHostBuilder()。UseKestrel()。配置(app=app。UseStatusCodePages(处理程序)。运行(上下文=任务。运行(()=上下文。响应。状态代码=_随机。下一个(400,599))。构建()。run();}}在处理请求时,我们指定的状态码错误处理程序根据响应状态码将错误分为客户端错误和服务器错误两种类型,并选择有针对性的错误消息作为响应内容。当我们使用浏览器访问该应用程序时,显示的错误消息将由响应状态代码决定。
在ASP.NET核心的世界中,请求的处理总是体现为一个RequestDelegate对象。如果请求处理需要一个或多个中间件,我们可以在ApplicationBuilder对象上注册它们,并使用它将中间件管道转换为RequestDelegate对象。用于注册StatusCodePagesMiddleware的UseStatusCodePage方法还有另一个重载,它允许我们以这种方式创建一个RequestDelegate对象来完成最终的请求处理工作,因此上面演示的应用程序可以重写如下。
公共类Program {私有静态Random _ Random=new Random();public static void Main(){ request delegate处理程序=异步上下文={ var response=context。回应;if(响应。StatusCode 500) {等待响应。WriteAsync($)客户端错误({响应。status code })');} else {等待响应。WriteAsync($)服务器错误({响应。status code })');} };新的WebHostBuilder()。UseKestrel()。配置(app=app。UseStatusCodePages(builder=builder。Run(handler))。运行(上下文=任务。运行(()=上下文。响应。状态代码=_随机。下一个(400,599))。构建()。run();}}摘要
以上就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。有问题可以留言交流。谢谢你的支持。
版权声明:在ASP.NET核心中应用错误处理渲染错误页面的三种方法是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。

















