本文老周就不自量力地介绍一下如何使用 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 <= 0)
- {
- 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"] = 80250,
- ["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 < 0 || n < 0)
- {
- throw new Exception("数值不能小于 0");
- }
- return Json(new { num1 = m, num2 = n, result = m + n });
- }
- }
此处把 attrbute 用到方法上
运行应用程序, 然后请出 Postman 大叔来帮我们测试 Web API 为参数 m 和 n 赋值, 然后以 GET 方式发送请求
获得正确的结果, 现在咱们提交小于 0 的参数就会返回刚刚自定义的错误
好了, 今天的内容就说到这里, 下次有空继续扯
来源: https://www.cnblogs.com/tcjiaan/p/8468901.html