【ASP.NET Core】处理异常(下篇)
上一篇中,老周给大伙伴们扯了有关 ASP.NET Core 中异常处理的简单方法。按照老周的优良作风,我们应该顺着这个思路继续挖掘。
本文老周就不自量力地介绍一下如何使用 MVC Filter 来处理异常。MVC 模型(当然适用于 Razor Page 、Web API 模型)可以用一系列的 Filter 来对请求与回应消息进行过滤处理。其中,在 Microsoft.AspNetCore.Mvc.Filters 命名空间下,你会发现有两个接口,它们跟异常处理有关:
IExceptionFilter:实现 OnException 方法,可以自定义回传给客户端的异常信息。
IAsyncExceptionFilter:跟上面的一样的,只不过这厮支持异步等待而已。
在实现处理异常的 Filter 时,传给 OnException / OnExceptionAsync 方法的有一个 ExceptionContext 类型参数,我们可以通过它来设置自定义的返回结果。
访问 Exception 属性,你可以得到相关的异常实例,当然这个属性是可写的,所以你可以获取异常实例后,将它改为其他异常实例,再重新赋给这个属性,比如,你用你自己编写的异常类来重新封装。通过 Result 属性设置返回结果,这个与 MVC Action 方法的返回方法一样,不同的是,在 Action 方法中,你可以调用 Controller 基类的方法来返回对应的 Result ,而对于 Result 属性,你必须显式地去创建实现了 IActionResult 接口的类型实例。
另外,值得注意的是,ExceptionContext 类还有一个 ExceptionHandled 属性,该属性值可读可写,主要是用于标识当前发生的异常是否已经过处理。这主要是应对 Filter 的执行顺序的,一种情况是你可能使用了多个 Filter 来处理异常,在处理过程中你就可以将这个属性值设为 true 以表示这个错误已处理过了,后面的就不必处理了;另一种情况是,以 Attribute 方式使用的 Filter 的优先级会比全局使用的 Filter 高,也许在 Attribute 上我没有对异常进行处理,那么到了全局 Filter 执行的时候,我就可以检查一下这个属性,如果没有处理就进行一下处理。关于 Attribute 方式使用 Filter 老周随后会说的,这里先提一下。
好了,咱们先说说如何实现自己的异常处理 Filter,其实很简单,看下面代码。
public class MyExceptionFilter : IExceptionFilter, IFilterMetadata
{
public void OnException(ExceptionContext context)
{
if(context.ExceptionHandled == false)
{
string msg = context.Exception.Message;
context.Result = new ContentResult
{
Content = msg,
StatusCode = StatusCodes.Status200OK,
ContentType = "text/html;charset=utf-8"
};
}
context.ExceptionHandled = true; //异常已处理了
}
在 OnException 方法中,我直接获取异常信息,然后用一个 ContentResult 对象来返回,这个是类似于 MVC 中 Controller . Action 方法返回结果,我这里简单地以 HTML 文本形式返回,一旦处理到异常,应用程序会自动把这个 Result 返回给客户端。
你可能发现了,我除了实现 IExceptionFilter 接口外,还实现了一个 IFilterMetadata 接口,这个接口是必须的,不然待会儿我们无法应用这个 Filter 了,为什么呢,等一下你就会明白了。
这里实现的这个是同步调用的,如果你希望有一个可异步等待的版本,那么,你就顺便实现一下 IAsyncExceptionFilter 接口。把上面的代码改为:
public class MyExceptionFilter : IExceptionFilter, IAsyncExceptionFilter, IFilterMetadata
{
public void OnException(ExceptionContext context)
{
if(context.ExceptionHandled == false)
{
string msg = context.Exception.Message;
context.Result = new ContentResult
{
Content = msg,
StatusCode = StatusCodes.Status200OK,
ContentType = "text/html;charset=utf-8"
};
}
context.ExceptionHandled = true; //异常已处理了
} public Task OnExceptionAsync(ExceptionContext context)
{
OnException(context);
return Task.CompletedTask;
}
}
好了,接下来咱们得考虑怎么用它了。在 Startup.ConfigureServices 方法中,添加 MVC 功能后可以把咱们自己写的 Filter 添加进去。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(opt =>
{
opt.Filters.Add<MyExceptionFilter>();
});
}
上面代码添加 Filter 后,是用于全局的,说白了,当应用程序内不管哪个 Controller 里面发生的异常,都会经过咱们添加的 Filter 处理。
现在我们测试一下这个异常处理的 Filter 起到什么作用。为了不影响测试,请把 Configure 方法中这段代码删除。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
变成这样
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
然后,随便弄段代码来测试。
[HttpPost("/code")]
public IActionResult SubmitSome(int val)
{
if(val <= )
{
throw new ArgumentException("号码不能小于或等于 0。");
}
return Content($"恭喜你,中奖了。\n中奖号码为:{val}", "text/html;charset=utf-8");
}
这个逻辑很简单,就是在前台页面输入一个数值,然后 POST 上来,如果数值不是大于 0 的值就抛异常。
然后我故意输入一个 -10。
POST 后在服务器上引发异常。
继续执行,让 Filter 对异常进行处理。
最后,异常信息就返回给浏览器了。
这样说明咱们写的 Filter 起作用了。
刚刚说过,在 ConfigureServices 方法中添加的 Filter 是用于全局的,如果我们的项目中有个别的 Controller 或者 Controller 中的个别方法,希望使用专门的 Filter 去处理异常,这时候就可以考虑以 Attribute 的方式去处理。
要用 Attribute 方式处理异常,需要实现 ExceptionFilterAttribute 抽象类。该抽象类已实现了咱们上面提到过的几个接口。
这个类还实现了 IOrderedFilter 接口,可以用来安排多个 Attribute 实例在处理异常上的顺序(假设你用了多个实例来处理)。
下面咱们自己实现一个 Attribute ,用来处理异常。
public class MyExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var ex = context.Exception;
// 构建错误信息对象
var dic = new Dictionary<string, object>
{
["err_code"] = ,
["err_msg"] = ex.Message,
["err_sol"] = "建议携带你的数据到医院做检查。"
};
// 设置结果
context.Result = new JsonResult(dic);
context.ExceptionHandled = true;
} public override Task OnExceptionAsync(ExceptionContext context)
{
OnException(context);
return Task.CompletedTask;
}
}
上面代码中,我以 JSON 格式返回错误数据。
这个 Attribute 可以用于类与方法,然后咱们用 Web API 来测试。
[Route("api/[controller]")]
public class DemoController : Controller
{
[HttpGet]
[MyExceptionFilter]
public IActionResult Compute(int m, int n)
{
if (m < || n < )
{
throw new Exception("数值不能小于 0。");
}
return Json(new { num1 = m, num2 = n, result = m + n });
}
}
此处把 attrbute 用到方法上。
运行应用程序,然后请出 Postman 大叔来帮我们测试 Web API。为参数 m 和 n 赋值,然后以 GET 方式发送请求。
获得正确的结果,现在咱们提交小于 0 的参数。就会返回刚刚自定义的错误。
好了,今天的内容就说到这里,下次有空继续扯。
【ASP.NET Core】处理异常(下篇)的更多相关文章
- Asp.net Core全局异常监控和记录日志
前言 系统异常监控可以说是重中之重,系统不可能一直运行良好,开发和运维也不可能24小时盯着系统,系统抛异常后我们应当在第一时间收到异常信息.在Asp.net Core里我使用拦截器 ...
- Asp.Net Core 发布异常 502.5 [The Application process failed to Start]
出现这个问题大部分时间都是因为发布时,少打包了一些文件.. 只打包了.Net Core的运行时库,没有打包Asp.Net Core 运行时.. 需要在打包指导文件中加入以下节点 <Propert ...
- asp.net core全局异常过滤并监控系统BUG将异常信息记录到日志
添加: using Dw.Util.Helper; using Microsoft.AspNetCore.Mvc.Filters; using System; using System.Collect ...
- 我的asp.net core目录
推荐 Asp.NETCore轻松学系列阅读指引目录(asp.net core 2.2) 官方文档翻译 http://www.cnblogs.com/dotNETCoreSG/p/aspnetcore- ...
- ASP.NET Core 中间件自定义全局异常处理
目录 背景 ASP.NET Core过滤器(Filter) ASP.NET Core 中间件(Middleware) 自定义全局异常处理 .Net Core中使用ExceptionFilter .Ne ...
- ASP.NET Core管道详解[6]: ASP.NET Core应用是如何启动的?[下篇]
要承载一个ASP.NET Core应用,只需要将GenericWebHostService服务注册到承载系统中即可.但GenericWebHostService服务具有针对其他一系列服务的依赖,所以在 ...
- 一题多解,ASP.NET Core应用启动初始化的N种方案[下篇]
[接上篇]"天下大势,分久必合,合久必分",ASP.NET应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系 ...
- ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”
在<ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式>中,我们通过几个简单的实例演示了如何呈现一个错误页面,这些错误页面的呈现分别由三个对应的中间件来完成,接下来我们将 ...
- ASP.NET Core的配置(4):多样性的配置来源[下篇]
我们在上篇和中篇对配置模型中默认提供的各种ConfigurationProvider进行了深入详尽的介绍,如果它们依然不能满足项目中的配置需求,我们可以还可以通过自定义ConfigurationPro ...
随机推荐
- 【Asp.net Core】在 Linux 子系统中安装 nginx 并配置反向代理
上一篇鸟文中,老周已经介绍过在 Ubuntu 子系统中安装 dotnet-sdk 的方法,本文老周给大伙伴们说说安装 nginx 服务,并配置反向代理.同样,老周假设你从来没有用过 Linux,所以老 ...
- ngRx 官方示例分析 - 3. reducers
上一篇:ngRx 官方示例分析 - 2. Action 管理 这里我们讨论 reducer. 如果你注意的话,会看到在不同的 Action 定义文件中,导出的 Action 类型名称都是 Action ...
- -------- ROOTKIT 核心技术——系统服务调度表挂钩调试(PART III) --------
---------------------------------------------------------------------------------------- 本篇开始进行真枪实弹的 ...
- arcgis server 中Web墨卡托投影与WGS-84坐标的转换
arcgis server 中Web墨卡托投影坐标与WGS-84坐标的转换 //经纬度转墨卡托 function lonlat2mercator(lonlat){ var mercator={x:0, ...
- Centos7-安装telnet服务
1,检查是否安装 telnet-server和xinetd rpm -qa telnet-server rpm -qa xinetd 2,如果没有安装过就安装 查找yum yum list |grep ...
- JS与ajax遍历list
示例: <script> <c:forEach var="yanan" items="${yananList}"> ...
- Linux 中su和sudo命令的几个注意点
1 su与su - 的区别 1.1命令说明 su对应是是no-login shell的方式进行账号登陆,命令行的变量配置还是切换账号前的变量. su-对应的是login shell的方式进行账号登陆, ...
- js面向对象之继承那点事儿根本就不是事
继承 说道这个继承,了解object-oriented的朋友都知道,大多oo语言都有两种,一种是接口继承(只继承方法签名):一种是实现继承(继承实际的方法) 奈何js中没有签名,因而只有实现继承,而且 ...
- php之插入排序
<?phpfunction insertSort($arr) { //插入排序 $len = count($arr); for($i=1;$i<$len;$i++){ ...
- 华硕笔记本电脑Win10改Win7设置U盘启动
华硕笔记本电脑Win10改Win7设置U盘启动 尝试开机按ESC选择前面没有UEFI项的USB启动: 1,在BIOS设置里advanced菜单,把 Lgeacy USB support选择为enabl ...