MVC 的过滤器(Filters)也翻译为“筛选器”。但是老周更喜欢翻译为“过滤器”,意思上更好理解。

既然都叫过滤器了,就是在MVC的操作方法调用前后进行特殊处理的类型。比如:

a、此调用是否已授权?

b、在模型绑定之前要不要修改数据源?(可能含有儿童不宜的数据)

c、在调用MVC方法前要不要改一改输入参数?在MVC方法调用之后要不要处理一下结果(加点味精,进一步调味)

d、发生异常后怎么处理?

过滤器可解决上面一堆提问。

在 ASP.NET Core 的 MVC 框架中,所有过滤器都实现共同接口 IFilterMetadata。该接口空空如也,未定义任何成员。说白了,它的用处是作为一种“记号”。你怎么证明你就是过滤器,嗯,看看你实现了 IFilterMetadata 接口没?实现了就认定是过滤器。所以,该接口纯粹是个角色标签。

咱们写代码一般不会实现 IFilterMetadata 接口,毕竟里面什么卵方法都没有,怎么规范类型?因此,过滤器专属命名空间 Microsoft.AspNetCore.Mvc.Filters 下为我们公开了以下接口,方便开发者实现:

1、IAuthorizationFilter:授权过滤器,它的优先级最高,总是最先运行。看看你有没有权限调用 MVC 方法,若没权限,就 See you La La。

2、IResourceFilter:资源过滤器。它在授权过滤成功后、模型绑定前运行。可以检查一下用于绑定的数据,要不要改一下。

3、IActionFilter:操作方法过滤器,就是针对 MVC Action 的。在操作方法运行前后运行,可以用来修改输入参数值。

4、IResultFilter:结果过滤器。当 MVC 操作方法运行成功后就会运行,可以用来修改运行结果。比如加点 HTTP 消息头什么的。

5、IExceptionFilter:当 MVC 操作方法运行过程中发生异常才会运行,无异常就不会运行。

…… 其实还有的,但这里咱们先不提,免得大伙搞得头晕。

过滤器不止一个,同一类型的过滤还可能有多个,因此,它们就像中间件那样,一个个链接起来,形成下水沟,哦不,是调用管道,或叫调用栈。于是,这就出现谁先运行的问题,虽然上面的介绍有说明,不过那太抽象了。任何编程知识只要能用代码来验证和观察,就不用图表和理论。

下面,咱们实现上述几个接口,然后往控制台上打印一些文本,来看看这些过滤器是怎么运行的。

public class CustAuthFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
Console.WriteLine("授权过滤器运行");
}
} public class CustResourceFilter : IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
Console.WriteLine("资源过滤器 - " + $"{nameof(OnResourceExecuted)}方法运行");
} public void OnResourceExecuting(ResourceExecutingContext context)
{
Console.WriteLine("资源过滤器 - " + $"{nameof(OnResourceExecuting)}方法运行");
}
} public class CustActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("操作过滤器 - " + $"{nameof(OnActionExecuted)}方法运行");
} public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("操作过滤器 - " + $"{nameof(OnActionExecuting)}方法运行");
}
} public class CustResultFilter : IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
Console.WriteLine("结果过滤器 - " + $"{nameof(OnResultExecuted)}方法运行");
} public void OnResultExecuting(ResultExecutingContext context)
{
Console.WriteLine("结果过滤器 - " + $"{nameof(OnResultExecuting)}方法运行");
}
}

这里我没有实现异常过滤器,只实现了授权、资源、操作方法、结果这几个有代表性的。

授权过滤器只要实现 OnAuthorization 方法即可。在实现代码中,可以通过 HttpContext 对象查找授权有关的对象,如果确认是没有访问权限的,可以设置一个自己定 Result 让 MVC 操作方法的调用终止。

资源过滤器要实现两个方法:OnResourceExecuting 方法在模型绑定前调用,这时你有机会修改数据源;OnResourceExecuted 方法是在资源过滤之后的其他过滤器运行结束才被调用,即:

ResourceExecuting
........ 剩余过滤器.......
ResourceExecuted

Action 过滤器也要实现两个方法:OnActionExecuting 在操作调用前运行;OnActionExecuted 是在操作方法调用后运行。

结果过滤器需要实现两个方法:OnResultExecuting 方法在操作结果执行前调用,这里可以修改 MVC 方法返回的值;OnResultExecuted 方法是在操作结果执行之后调用,一般这里可以改改HTTP向应头、Cookie 什么的。

其实咱们刚实现的过滤器都是同步版本,这些过滤器都有配套的异步版本,接口都是以 IAsync 开头。这里咱们先不用管同步异步,避免搞得复杂了。也不要去理会过滤器是全局的还是局部的,下面咱们统一把它们注册为全局的。配置方法是通过 MVC 选项类的 Filters 集合,把要用的过滤器添加进去即可。

 var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(options =>
{
// 配置全局过滤器
options.Filters.Add<CustAuthFilter>();
options.Filters.Add<CustResourceFilter>();
options.Filters.Add<CustActionFilter>();
options.Filters.Add<CustResultFilter>();
});
var app = builder.Build();

添加一个“狗头”控制器,用于测试。

public class GouTouController : Controller
{
public IActionResult Index()
{
Console.WriteLine("Index操作运行");
return View();
}
}

为了防止 ASP.NET Core 应用程序输出的日志干扰咱们查看控制台内容,咱们禁用所有日志输出。打开 appsettings.json 文件,把所有日志类别的记录级别改为 None。

{
"Logging": {
"LogLevel": {
"*": "None"
}
},
"AllowedHosts": "*"
}

星号 * 的意思就是代表所有类别的日志,LogLevel 为 None 就不会输出日志了(貌似有个别日志禁用不了)。

运行程序后,控制台打印出这样的内容:

这个流程现在是不是很清晰了?咱们画图表了,直接这样表达就好:

Author
Resource Executing
Action Executing
Action Running
Action Executed
Result Executing
Result Running
Result Executed
Resource Executed

局部过滤器的运行过程与全局过滤器相同,如果局部和全局过滤器同时使用,那会发生什么呢?咱们试试。

接下来我们为授权过滤、资源过滤、操作过滤、结果过滤各创建两个类——用于局部和全局。实际开发中一般不需要这样搞,通常全局和局部写一个类就行,毕竟过滤器类型在全局和局部是通用的。我这里只为了演示。局部过滤器是通过特性类的方式应用到 MVC 方法上的,所以,局部过滤器除了实现过滤器接口,还要从 Attribute 类派生。

1、实现局部、全局授权过滤器。

// 授权过滤器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyAuthorFilterAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
Console.WriteLine("局部:授权过滤器运行");
}
} // 授权过滤器-全局
public class GlobAuthorFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
Console.WriteLine("全局:授权过滤器运行");
}
}

2、实现局部、全局资源过滤器。

// 资源过滤器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
Console.WriteLine("局部:资源过滤器-Executed");
} public void OnResourceExecuting(ResourceExecutingContext context)
{
Console.WriteLine("局部:资源过滤器-Executing");
}
} // 资源过滤器-全局
public class GlobResourceFilter : IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
Console.WriteLine("全局:资源过滤器-Executed");
} public void OnResourceExecuting(ResourceExecutingContext context)
{
Console.WriteLine("全局:资源过滤器-Executing");
}
}

3、实现局部、全局操作过滤器。

// 操作过滤器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyActionFilterAttribute : Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("局部:操作过滤器-Executed");
} public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("局部:操作过滤器-Executing");
}
} // 操作过滤器-全局
public class GlobActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("全局:操作过滤器-Executed");
} public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("全局:操作过滤器-Executing");
}
}

4、实现局部、全局结果过滤器。

// 结果过滤器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyResultFilterAttribute : Attribute, IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
Console.WriteLine("局部:结果过滤器-Executed");
} public void OnResultExecuting(ResultExecutingContext context)
{
Console.WriteLine("局部:结果过滤器-Executing");
}
} // 结果过滤器-全局
public class GlobResultFilter : IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
Console.WriteLine("全局:结果过滤器-Executed");
} public void OnResultExecuting(ResultExecutingContext context)
{
Console.WriteLine("全局:结果过滤器-Executing");
}
}

先注册全局过滤器。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
// 添加全局过滤器
options.Filters.Add<GlobActionFilter>();
options.Filters.Add<GlobAuthorFilter>();
options.Filters.Add<GlobResourceFilter>();
options.Filters.Add<GlobResultFilter>();
});
var app = builder.Build();

局部过滤器以特性方式应用于 MVC 操作方法。

public class SpiderController : ControllerBase
{
[MyResourceFilter]
[MyResultFilter]
[MyActionFilter, MyAuthorFilter]
public IActionResult Index()
{
Console.WriteLine("Index操作被调用");
return Content("大火烧了毛毛虫");
}
}

和上一个例子一样,禁用日志输出(appsettings.json文件)。

{
"Logging": {
"LogLevel": {
"*": "None"
}
},
……
}

程序运行后,控制台打印以下内容:

过滤器按 授权->资源->操作->结果 运行的次序不变。在同种过滤器中,全局过滤器优先运行。

全局授权过滤器
局部授权过滤器
全局资源过滤器 - 前
局部资源过滤器 - 前
全局操作过滤器 - 前
局部操作过滤器 - 前
【调用 MVC 操作方法】
局部操作过滤器 - 后
全局操作过滤器 - 后
全局结果过滤器 - 前
局部结果过滤器 - 前
【执行操作结果】
局部结果过滤器 - 后
全局结果过滤器 - 后
局部资源过滤器 - 后
全局资源过滤器 - 后

另外,有一件事要注意:如果你的控制器的基类是 Controller,那么,还有优先更高的 Action Filter。看看 Controller 类它实现了啥接口。

public abstract class Controller : ControllerBase, IActionFilter, IFilterMetadata, IAsyncActionFilter, IDisposable

咱们把刚才的控制器代码改一下,让它继承 Controller 类,并重写 OnActionExecuting、OnActionExecuted 方法。

public class SpiderController : Controller
{
…… public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("控制器实现的操作过滤器-Executing");
base.OnActionExecuting(context);
} public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("控制器实现的操作过滤器-Executed");
base.OnActionExecuted(context);
}
}

然后再次运行程序,控制台将打印以下内容:

看,这个由控制器类实现的 Action 过滤器比全局的还早运行。

【ASP.NET Core】MVC过滤器:运行流程的更多相关文章

  1. 解说asp.net core MVC 过滤器的执行顺序

    asp.net core MVC 过滤器会在请求管道的各个阶段触发.同一阶段又可以注册多个范围的过滤器,例如Global范围,controller范围等.以ActionFilter为例,我们来看看过滤 ...

  2. ASP.NET Core MVC 过滤器介绍

    过滤器的作用是在 Action 方法执行前或执行后做一些加工处理.使用过滤器可以避免Action方法的重复代码,例如,您可以使用异常过滤器合并异常处理的代码. 过滤器如何工作? 过滤器在 MVC Ac ...

  3. ASP.NET Core MVC 过滤器

    参考网址:https://www.cnblogs.com/dotNETCoreSG/p/aspnetcore-4_4_3-filters.html ASP.NET Core有五种类型的过滤器,每个过滤 ...

  4. 扒一扒asp.net core mvc控制器的寻找流程

    不太会排版,大家将就看吧. asp.net core mvc和asp.net mvc中都有一个比较有意思的而又被大家容易忽略的功能,控制器可以写在非Web程序集中,比如Web程序集:"MyW ...

  5. ASP.NET Core MVC 源码学习:详解 Action 的激活

    前言 在 上一篇 文章中,我们已经学习了 ASP.NET Core MVC 的启动流程,那么 MVC 在启动了之后,当请求到达过来的时候,它是怎么样处理的呢? 又是怎么样把我们的请求准确的传达到我们的 ...

  6. ASP.NET Core MVC 源码学习:详解 Action 的匹配

    前言 在 上一篇 文章中,我们已经学习了 ASP.NET Core MVC 的启动流程,那么 MVC 在启动了之后,当请求到达过来的时候,它是怎么样处理的呢? 又是怎么样把我们的请求准确的传达到我们的 ...

  7. Asp.Net Core MVC框架内置过滤器

    第一部分.MVC框架内置过滤器 下图展示了Asp.Net Core MVC框架默认实现的过滤器的执行顺序: Authorization Filters:身份验证过滤器,处在整个过滤器通道的最顶层.对应 ...

  8. [ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?

    昨天有个朋友在微信上问我一个问题:他希望通过动态脚本的形式实现对ASP.NET Core MVC应用的扩展,比如在程序运行过程中上传一段C#脚本将其中定义的Controller类型注册到应用中,问我是 ...

  9. ASP.NET Core MVC 源码学习:MVC 启动流程详解

    前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...

  10. asp.net core MVC 全局过滤器之ExceptionFilter异常过滤器(一)

    本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter异常过滤器(一) asp.net cor ...

随机推荐

  1. AI 一键去水印:教你无限量使用商业图片的技巧

    场景再现 刚开始注册账号(啥账号具体不表了,小编不喜欢的那个),想弄个闪亮,好看,有个性化的 Logo.作为一名非专美工小白人员,网上翻了很久作图工具,要么就是不好用,好用的大部分都收费.最后没办法, ...

  2. 基于proxysql实现MySQL读写分离

    前言 环境: 系统版本:CentOS 7 MySQL版本:5.7.35 MySQL主从配置略过. 安装 # 安装 yum localinstall -y ./proxysql-2.2.0-1-cent ...

  3. 数据库中limit 和 offset 使用区别

    题:查找最晚入职员工的所有信息 1,SELECT * FROM employees ORDER BY hire_date DESC LIMIT 0,1; 解:对列hire_date分组后升序,从下标( ...

  4. 【io_uring】简介和使用

    文章目录 简介 使用 系统调用 liburing 样例 代码流程 编译 参考资料 简介 io_uring 是 Linux 在 5.1 版本引入的一套新的异步 IO 实现.相比 Linux 在 2.6 ...

  5. 使用API接口获取商品数据

    ​ 在当今的数字化时代,商品数据的获取对于各种规模的企业来说都至关重要.这些数据可以帮助企业进行市场分析,制定销售策略,优化库存管理,以及实现精准营销.API(应用程序编程接口)是一种便捷的方式来获取 ...

  6. 按关键字API接口搜索天眼查企业数据

    一.如果你想要查找某一个企业的基本信息或是对行业中的企业进行筛选,那么使用API接口搜索天眼查企业数据会非常方便. 首先,你需要获取天眼查API的access_token,这可以通过注册账号获取.一旦 ...

  7. shopee的前景以及商用API(代码封装)

    Shopee平台是东南亚和台湾地区最具代表性的电商平台之一,在过去几年里取得了巨大的成功.以下是Shopee平台的发展前景: 电商市场的快速增长:东南亚和台湾地区是人口众多.市场潜力巨大的区域,电商市 ...

  8. 一个.NET 7 + DDD + CQRS +React+Vite的实战项目

    项目简介 基于SignalR实现聊天通信,支持横向扩展,可支撑上万用户同时在线聊天 后端架构 后端技术栈采用 .NET 7 PostgreSQL (业务数据库) Redis(用于存放热点数据,和支持S ...

  9. App性能指标(安装、冷启动、卸载、平均内存/cpu/fps/net)测试记录

    [需求背景] 需要针对产品以及竞品做出横向对比,输出对应的比对测试报告,供产研进行产品性能优化依据 [测试方案] 对于主流的厂商和系统版本进行多维度的横向对比 厂商:华为系.小米系.蓝绿系.三星系.苹 ...

  10. 【Python爬虫】使用代理ip进行网站爬取

    使用代理IP进行网站爬取可以有效地隐藏你的真实IP地址,让网站难以追踪你的访问行为.本文将介绍Python如何使用代理IP进行网站爬取的实现,包括代理IP的获取.代理IP的验证.以及如何把代理IP应用 ...