asp 开发app_ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式

由于ASP.NET Core应用是一个同时处理多个请求的服务器应用,所以在处理某个请求过程中抛出的异常并不会导致整个应用的终止。出于安全方面的考量,为了避免敏感信息的外泄,客户端在默认的情况下并不会得到详细的出错信息,这无疑会在开发环境下增加查错纠错的难度。对于生产环境来说,我们也希望最终用户能够根据具体的错误类型得到具有针对性并且友好的错误消息。ASP.NET Core提供了相应的中间件帮助我们将定制化的错误信息呈现出来,这些中间件都定义在“Microsoft.AspNetCore.Diagnostics”这个NuGet包中。在着重介绍这些中间件之前,我们照理演示几个简单的实例让读者朋友们对这些中间件的作用有一个大概的了解。[本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、显示开发者异常页面
二、显示定制异常页面
三、针对响应状态码定制错误页面

一、显示开发者异常页面

一般情况下,如果ASP.NET Core在处理某个请求时出现异常,它一般会返回一个状态码为“500 Internal Server Error”的响应。为了避免一些敏感信息的外泄,详细的错误信息并不会随着响应发送给客户端,所以客户端只会得到一个很一般化的错误消息。以如下这个程序为例,服务端在处理每个请求时都会抛出一个类型为InvalidOperationException的异常。

   1: public class Program   2: {   3:     public static void Main()   4:     {   5:         new WebHostBuilder()   6:             .UseKestrel()   7:             .Configure(app => app.Run(context => Task.FromException(new InvalidOperationException("Manually thrown exception..."))))   8:             .Build()   9:             .Run();  10:     }  11: }

当我们利用浏览器访问这个应用的时候,总是会得到如下图所示的这个错误页面。可以看出这个页面仅仅告诉我们目标应用当前无法正常处理本次请求,除了提供的响应状态码(“HTTP ERROR 500”)之外,它并没有提供任何有益于差错纠错的错误信息。

fa16f1a430751a5010b76fa2f8a66039.png

那么有人可能会觉得虽然浏览器上没有显示出任何详细的错误信息,也许它会隐藏在接收到的HTTP响应报文中。针对通过浏览器放出的这个请求,得到的响应内容如下所示,我们会发现响应报文根本没有主体部分,有限的几个报头也并没有承载任何与错误有关的信息。

   1: HTTP/1.1 500 Internal Server Error
   2: Date: Fri, 09 Dec 2016 23:42:18 GMT
   3: Content-Length: 0
   4: Server: Kestrel

由于应用并没有中断,浏览器上也并没有显示任何具有针对性的错误信息,开发人员在进行查错纠错的时候如何准确定位到作为错误根源的那一行代码呢?具体来说,我们又两种解决方案,一种就是利用日志,因为ASP.NET Core在进行请求处理时出现的任何错误都会被写入日志,所以我们可以通过注册相应的LoggerProvider(比如注册一个ConsoleLoggerProvider将日志直接写入宿主应用的控制台)到来获取写入的错误日志。

至于另一种解决方案,就是直接显示一个包含错误相应信息的错误页面,由于这个页面是在开发环境给开发者看的,所以我们将这个页面称为“开发者异常页面(Developer Exception Page)”。针对页面的自动呈现是利用一个名为DeveloperExceptionPageMiddleware的中间件来完成的,我们可以调用ApplicationBuilder的扩展方法UseDeveloperExceptionPage来注册这个中间件。

   1: public class Program   2: {   3:     public static void Main()   4:     {   5:         new WebHostBuilder()   6:             .UseKestrel()   7:             .Configure(app => app   8:                 .UseDeveloperExceptionPage()   9:                 .Run(context => Task.FromException(new InvalidOperationException("Manually thrown exception..."))))  10:             .Build()  11:             .Run();  12: }  13: }

一旦注册了这个DeveloperExceptionPageMiddleware中间件,ASP.NET Core应用在处理请求出现的异常信息就会以下图的形式直接出现在浏览器上,我们可以在这个页面中看到几乎所有的错误信息,包括异常的类型、消息和堆栈信息等。

c6d689739ad3f38c1935b063e2e2b5cf.png

开发者异常页面除了显示与抛出的异常相关的信息之外,还会以如下图所示的形式显示与当前请求上下文相关的信息,其中包括当前请求URL携带的所有查询字符串、所有请求报头以及Cookie的内容。如此详尽的信息无疑会极大地帮助开发人员尽快地找出错误的根源。

214b0ba97a1b09da9f59804f2183fc75.png

通过DeveloperExceptionPageMiddleware中间件呈现的错误页面仅仅是供开发人员使用的,详细的错误信息往往会携带一些敏感的信息,所以务必记住只有在开发环境才能注册这个中间件,如下所示的代码片段体现了针对DeveloperExceptionPageMiddleware中间件正确的注册方式。

   1: new WebHostBuilder()   2:     .UseStartup()   3:     …   4:     5: public class Startup   6: {   7:     public void Configure(IApplicationBuilder app, IHostingEnvironment env)   8:     {   9:         if (env.IsDevelopment())  10:         {  11:             app.UseDeveloperExceptionPage();  12:         }  13:     }  14: }

二、显示定制异常页面

DeveloperExceptionPageMiddleware中间件通过将异常详细信息和基于当前请求的内容直接呈现在错误页面中,这为开发人员的纠错诊断提供了极大的便利。但是在生产环境下,我们倾向于为最终的用户呈现一个定制的错误页面,而这可以通过注册另一个名为ExceptionHandlerMiddleware的中间件来实现。顾名思义,这个中间件旨在提供一个异常处理器(Exception Handler)来处理抛出的异常。实际上这个所谓的异常处理器就是一个类型为RequestDelegate的委托对象,ExceptionHandlerMiddleware中间件捕捉到抛出的异常后利用它来响应当前的请求。

还是以上面创建的这个总是会抛出一个 InvalidOperationException异常的应用为例。我们按照如下的形式调用ApplicationBuilder的扩展方法UseExceptionHandler注册了上述的这个ExceptionHandlerMiddleware中间件。这个扩展方法具有一个ExceptionHandlerOptions类型的参数,它的ExceptionHandler属性返回的就是这个作为异常处理器的RequestDelegate对象。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         RequestDelegate handler = async context => await context.Response.WriteAsync("Unhandled exception occurred!");
   6: 
   7:         new WebHostBuilder()
   8:             .UseKestrel()
   9:             .Configure(app => app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandler = handler})
  10:             .Run(context => Task.FromException(new InvalidOperationException("Manually thrown exception..."))))
  11:             .Build()
  12:             .Run();
  13:     }
  14: }

如上面的代码片段所示,这个作为异常处理器的RequestDelegate仅仅是将一个简单的错误消息(“Unhandled exception occurred!”)作为响应的内容。当我们利用浏览器访问该应用的时候,这个定制的错误消息将会以如图4所示的形式直接呈现在浏览器上。

e17ddc7981e7c08f56ec5e7e32dd2e51.png

最终作为异常处理器的是一个类型为RequestDelegate的委托对象,而ApplicationBuilder具有创建这个委托对象的能力。具体来说,我们可以根据异常处理的需要将相应的中间件注册到某个ApplicationBuilder对象上,并最终利用这个ApplicationBuilder根据注册的中间件创建出作为异常处理器的RequestDelegate对象。如果异常处理需要通过一个或者多个中间件来完成,我们可以按照如下的形式调用另一个UseExceptionHandler方法重载。这个方法的参数类型为Action,我们调用它的Run方法注册了一个中间件来响应一个简单的错误消息。

   1: public class Program   2: {   3:     public static void Main()   4:     {           5:         new WebHostBuilder()   6:             .UseKestrel()   7:             .Configure(app => app.UseExceptionHandler(builder=>builder.Run(async context => await context.Response.WriteAsync("Unhandled exception occurred!")))   8:             .Run(context => Task.FromException(new InvalidOperationException("Manually thrown exception..."))))   9:         .Build()  10:         .Run();  11:     }  12: }

上面这两种异常处理的形式都体现在提供一个RequestDelegate的委托对象来处理抛出的异常并完成最终的响应。如果应用已经设置了一个错误页面,并且这个错误页面具有一个固定的路径,那么我们在进行异常处理的时候就没有必要提供这个RequestDelegate对象,而只需要重定向到错误页面指向的路径即可。这种采用服务端重定向的异常处理方式可以采用如下的形式调用另一个UseExceptionHandler方法重载来完成,这个方法的参数表示的就是重定向的目标路径(“/error”),我们针对这个路径注册了一个路由来响应定制的错误消息。

   1: public class Program   2: {   3:     public static void Main()   4:     {   5:         new WebHostBuilder()   6:             .UseKestrel()   7:             .ConfigureServices(svcs=>svcs.AddRouting())   8:             .Configure(app => app   9:                 .UseExceptionHandler("/error")  10:                 .UseRouter(builder=>builder.MapRoute("error", async context => await context.Response.WriteAsync("Unhandled exception occurred!")))  11:                 .Run(context => Task.FromException(new InvalidOperationException("Manually thrown exception..."))))  12:         .Build()  13:         .Run();  14:     }  15: }

三、针对响应状态码定制错误页面

由于Web应用采用HTTP通信协议,所以我们应该尽可能低迎合HTTP标准并将定义在协议规范中的语义应用到应用中。对于异常或者错误的语义表达在HTTP协议层面主要体现在响应报文的状态码上,具体来说HTTP通信的错误大体分为如下两种类型:

  • 客户端错误:表示因客户端提供不正确的请求信息而导致服务器不能正常处理请求,响应状态码范围在400~499之间。

  • 服务端错误:表示服务器在处理请求过程中因自身的问题而发生错误,响应状态码在500~509之间。

正是因为响应状态码是对错误或者异常语义最重要的表达,所以在很多情况下我们需要针对不同的响应状态码来定制显示的错误信息。针对响应状态码对错误页面的定制可以借助一个类型为StatusCodePagesMiddleware的中间件来实现,我们可以调用ApplicationBuilder相应的扩展方法来注册这个中间件。

DeveloperExceptionPageMiddleware和ExceptionHandlerMiddleware中间件都是在后续请求处理过程中抛出异常的情况下才会被调用,而StatusCodePagesMiddleware被调用的前提是后续请求助理过程中产生一个错误响应状态码(范围在400~599之间)。如果仅仅希望显示一个统一的错误页面,我们可以按照如下的形式调用扩展方法UseStatusCodePages注册这个中间件,传入该方法的两个参数分别表示响应采用的媒体类型和主体内容。

   1: public class Program   2: {   3:     public static void Main()   4:     {           5:         new WebHostBuilder()   6:             .UseKestrel()   7:             .Configure(app=>app   8:                 .UseStatusCodePages("text/plain", "Error occurred ({0})")   9:                 .Run(context=> Task.Run(()=>context.Response.StatusCode = 500)))  10:         .Build()  11:         .Run();  12:     }  13: }

如上面的代码片段所示,应用在处理请求的时候总是会将响应状态码设置为500,所以最终的响应内容将由注册的StatusCodePagesMiddleware中间件来提供。我们调用UseStatusCodePages方法的时候将响应的媒体类型设置为“text/plain”,并将一段简单的错误消息作为了响应的主体内容。值得一提的时候,作为响应内容的字符串可以包含一个占位符({0}),StatusCodePagesMiddleware中间件最终会采用当前响应状态码来替换它。如果我们利用浏览器来访问这个应用,将会得到如下图所示的错误页面。

9d26d9b24035536ce9500cf5eac5de5c.png

如果我们希望针对不同的错误状态码显示不同的错误页面,那么我们就需要将具体的请求处理逻辑实现在一个的状态码错误处理器中,并最终提供给StatusCodePagesMiddleware中间件。这个所谓的状态码错误处理器体现为一个类型为Func的委托对象,作为输入的StatusCodeContext对象是对当前HttpContext的封装,同时承载着其他一些与错误处理相关的选项设置,我们将在本系列后续部分对这个类型进行详细介绍。

对于如下这个应用来说,它在处理任意一个请求是总是会随机地选择一个400~599之间的整数作为响应的状态码,所以客户端返回的响应内容总是通过注册的StatusCodePagesMiddleware中间件来提供。我们在调用另一个UseStatusCodePages方法重载的时候,为注册的中间件指定了一个Func对象作为状态码错误处理器。

   1: public class Program   2: {   3: private static Random _random = new Random();   4:     5:     public static void Main()   6:     {   7:         Func handler = async context => {   8:             var response = context.HttpContext.Response;   9:             if (response.StatusCode < 500)  10:             {  11:                 await response.WriteAsync($"Client error ({response.StatusCode})");  12:             }  13:             else  14:             {  15:                 await response.WriteAsync($"Server error ({response.StatusCode})");  16:             }  17:         };  18:         new WebHostBuilder()  19:             .UseKestrel()  20:             .Configure(app => app  21:                 .UseStatusCodePages(handler)  22:                 .Run(context => Task.Run(() => context.Response.StatusCode = _random.Next(400,599))))  23:             .Build()  24:             .Run();  25:     }  26: }

我们指定的状态码错误处理器在处理请求的时候,根据响应状态码将错误分成客户端错误和服务端错误两种类型,并选择针对性的错误消息作为响应内容。当我们利用浏览器访问这个应用的时候,显示的错误消息将由响应状态码来决定。

bfe2b261bbd59738614f73a4803136d2.png

在ASP.NET Core的世界里,针对请求的处理总是体现为一个RequestDelegate对象。如果请求的处理需要借助一个或者多个中间件来完成,我们可以将它们注册到ApplicationBuilder对象上并利用它将中间件管道转换成一个RequestDelegate对象。用于注册StatusCodePagesMiddleware中间件的UseStatusCodePage方法还具有另一个重载,它允许我们采用这种方式来创建一个RequestDelegate对象来完成最终的请求处理工作,所以上面演示的这个应用完全可以改写成如下的形式。

   1: public class Program   2: {   3:     private static Random _random = new Random();   4:     public static void Main()   5:     {   6:         RequestDelegate handler = async context =>   7:         {   8:             var response = context.Response;   9:             if (response.StatusCode < 500)  10:             {  11:                 await response.WriteAsync($"Client error ({response.StatusCode})");  12:             }  13:             else  14:             {  15:                 await response.WriteAsync($"Server error ({response.StatusCode})");  16:             }  17:         };  18:         new WebHostBuilder()  19:             .UseKestrel()  20:             .Configure(app => app  21:                 .UseStatusCodePages(builder=>builder.Run(handler))  22:                 .Run(context => Task.Run(() => context.Response.StatusCode = _random.Next(400, 599))))  23:             .Build()  24:             .Run();  25:     }  26: }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/486511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

美国半导体十年计划中的NO.1,模拟硬件究竟有什么价值?

来源&#xff1a; 脑极体中国半导体行业在集体过冬&#xff0c;美国也未必没有紧迫感。前不久&#xff0c;美国半导体行业协会&#xff08;SIA&#xff09;和半导体研究公司&#xff08;SRC&#xff09;就联合发布了一份题为“半导体十年计划”的报告&#xff0c;希望美国政府能…

【编程开发】Python---列表

ERROR&#xff1a;错误 waring&#xff1a;警告&#xff0c;还没到犯错的地步 print(r\n) r"字符串"&#xff0c;字符串里的所有字符都不转义 str "abcdef" 如果想隔一个取出来&#xff0c;str[0::2] 深浅拷贝 列表的内存存放 Python修改的变量只是修改了…

processing python模式_详解python之多进程和进程池(Processing库)

环境:win7python2.7 一直想学习多进程或多线程,但之前只是单纯看一点基础知识还有简单的介绍,无法理解怎么去应用,直到前段时间看了github的一个爬虫项目涉及到多进程,多线程相关内容,一边看一边百度相关知识点,现在把一些相关知识点和一些应用写下来做个记录. 首先说下什么是进…

不确定性的价值

来源&#xff1a; 混沌巡洋舰推荐一篇万字长文 聪大脑的预测性编码讲起&#xff0c;区分三种不确定性&#xff0c;最终对我们如何应对这个充满不确定的时代&#xff0c;给出启示。愿读完后&#xff0c;你能够更全面客观的理解生活中的随机性。霍金斯是一位计算机科学家(后来成为…

网站开启https后很慢_网站优化中哪些设置会影响蜘蛛的抓取?对网站SEO产生什么影响...

网站在优化中&#xff0c;要有一定的收录基础才能有更大的几率提升排名&#xff0c;并获得搜索引擎的喜爱。但在网站优化中难免会有一些细节没注意到而影响蜘蛛的抓取&#xff0c;下面就带大家一起了解一下。一、登录设置有些网站会设置注册账号的要求才能允许用户浏览网站&…

正则表达式提取器_C++11新特性7 - 正则表达式

C11 新增了正则表达式的标准库支持&#xff0c;本文简介 C 正则表达式的使用在 C 中使用正则表达式&#xff0c;和其它语言差别不大int main() {regex e("abc*");bool m regex_search("abccc", e);// 输出 yescout << (m ? "yes" : &quo…

智慧城市建设必须认真对待的几个问题(一):IPV6作为地址的问题

全文共计4384字&#xff0c;预计阅读时间9分钟来源 | 数据观&#xff08;转载请注明来源&#xff09;作者 | 陆宝华 李晋 张永红 李学良编辑 | 蒲蒲引言智慧城市已经热炒几年了&#xff0c;目前还在升温&#xff0c;并且一些做国际联网应用的大企业提出了“城市大脑”的概念。当…

linux open 头文件_linux下通过共享内存和mmap实现进程间通讯

前言最近在学习GNU/Linux内核&#xff0c;看到mmap的时候书上说&#xff1a;mmap/munmap接口函数是用户最常用的两个系统调用接口&#xff0c;无论是在用户程序中分配内存、读写大文件、链接动态库文件&#xff0c;还是多进程间共享内存&#xff0c;都可以看到mmap/munmap的身影…

TinyML:下一轮人工智能革命

来源&#xff1a;AI前线作者&#xff1a;Matthew Stewart译者&#xff1a;盖磊策划&#xff1a;陈思人工智能的一个趋势是正快速从“云端”走向“边缘”。TinyML 是在海量的物联网设备端微控制器上实现的人工智能&#xff0c;有望在未来几年内&#xff0c;成为人工智能在工业领…

springboot导入项目依赖报错_最详细的 Spring Boot 多模块开发与排坑指南

来源于公众未读代码 &#xff0c;作者达西呀创建项目创建一个 SpringBoot 项目非常的简单&#xff0c;简单到这里根本不用再提。你可以在使用 IDEA 新建项目时直接选择 Spring Initlalize 创建一个 Spring Boot 项目&#xff0c;也可以使用 Spring 官方提供的 Spring Boot 项目…

图书管理系统html_结自主可控数字化硕果,同方鼎欣推进基于OFD技术的数字档案管理系统研发...

01浅谈建立自主可控版式格式的重要性说起版式文档&#xff0c;相信大家首先想到的就是Adobe公司推出的PDF&#xff0c;这种格式的电子文档&#xff0c;其效果不因软硬件环境变化而变化&#xff0c;在版面、字体、字号方面可以与纸质文件保持一致&#xff0c;这就是版式文档的重…

联结你与万物的8种元素

本文经授权转载自《原理》微信公众号你不仅由元素构成&#xff0c;你其实就是元素。不同元素的原子在你的身体里&#xff0c;它们随你而动&#xff0c;它们与你一同经历成功的狂喜&#xff0c;一同承受失败的悲痛&#xff0c;也一同走过平淡的日常。当你吃下食物时&#xff0c;…

Spring中的用到的设计模式大全

spring中常用的设计模式达到九种&#xff0c;我们举例说明&#xff1a; 第一种&#xff1a;简单工厂又叫做静态工厂方法&#xff08;StaticFactory Method&#xff09;模式&#xff0c;但不属于23种GOF设计模式之一。 简单工厂模式的实质是由一个工厂类根据传入的参数&#xff…

crontab 日志_聊聊老板让我删除日志文件那些事儿

一、背景老板&#xff1a;小白&#xff0c;刚才咱们的机器告警磁盘空间不足了&#xff0c;你去定位一下原因。我&#xff1a;(......空间不足碍我屁事儿)好的&#xff0c;马上定位我&#xff1a;老板&#xff0c;太多N天前的日志文件占用了太多空间。 老板&#xff1a;你感觉你…

【专家观点】刘经南院士:北斗+5G为何能引领新基建?

来源&#xff1a;智能研究院日前&#xff0c;“第四届全球未来出行大会&#xff08;GFM2020&#xff09;”在德清召开。本次大会旨在探讨未来的城市、未来的出行、未来的汽车如何为居民提供更加经济、便捷、安全、科技友好的新出行方式。在论坛的演讲中&#xff0c;中国工程院院…

ae中心点重置工具_(精品)AE从小白到大神之路(七)-AE动画—动效常见的设计方法...

动画——动效常见的设计方法一&#xff0e;基础动画&#xff1a;1.通过物体本身的旋转/缩放/位移/不透明度等基本属性来做的一些动效属于最基础的动画效果。二&#xff0e;路径动画&#xff1a;&#xff08;1&#xff09;修剪路径动画&#xff08;前面系列案例——下载提示完成…

css 透明度_如何使用CSS实现精美视频片头制作

借助CSS所提供的动画效果&#xff0c;旋转效果除了能够制作动画及网页页面元素&#xff0c;如按钮之外&#xff0c;还可以使用CSS实现精美的动态片头的制作。本文主要介绍CSS与HTML实现精美的动画片头制作实例。如何使用CSS实现精美片头制作CSS动态片头设计实例本例设计使用烟雾…

重磅盘点:过去8年中深度学习最重要的想法

原文&#xff1a;Deep Learning’s Most Important Ideas[1]作者&#xff1a;Denny Britz&#xff08;ML 研究员&#xff0c;Google Brain 前成员&#xff09;译者&#xff1a;REN深度学习是一个瞬息万变的领域&#xff0c;层出不穷的论文和新思路可能会令人不知所措。即使是经…

ActiveMQ 发送和接收消息

一、添加 jar 包 <dependency><groupId>org.apache.activemq</groupId><artifactId>activemq-all</artifactId><version>5.11.2</version> </dependency> 二、消息传递的两种形式 1、点对点&#xff1a;发送的消息只能被一个消…

机器人 蓝buff 钩_机器人要在S赛登场了?Ming韩服练起来了,这是RNG黑科技?

随着S9全球总决赛日程的逼近&#xff0c;各大战队也纷纷结束了休假&#xff0c;投入到了紧张的训练之中。对于这次S9世界赛的版本&#xff0c;应该是上中野的版本&#xff0c;因为不少战士单带型上单得到了巨大加强&#xff0c;而且中路会有一些法师英雄回归&#xff0c;总体来…