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. mac一键获取最新datagrid 2017.3注册码到剪贴板

    mac一键获取最新datagrid 2017.3注册码到剪贴板 近期datagrid 校验激活码合法性的频率提高,导致需要频繁输入激活码 遂整理一脚本,自动获取最新注册码,并拷贝到剪贴板. 打开dat ...

  2. React请求机制优化思路

    说起数据加载的机制,有一个绕不开的话题就是前端性能,很多电商门户的首页其实都会做一些垂直的定制优化,比如让请求在页面最早加载,或者在前一个页面就进行预加载等等.随着react18的发布,请求机制这一块 ...

  3. 三维模型OSGB格式轻量化纹理压缩关键技术分析

    三维模型OSGB格式轻量化纹理压缩关键技术分析 在三维模型应用中,纹理是一个十分重要的因素,可以使得模型更加真实.精细.随着移动设备和网络传输速度的限制,纹理数据也需要进行轻量化处理,而OSGB格式纹 ...

  4. 【微信自动化】使用c#实现微信自动化

    引言 上个月,在一个群里摸鱼划水空度日,看到了一个老哥分享的一个微信自动化的一个类库,便下载了他的Demo,其本意就是模拟鼠标来操作UI,实现UI自动化:然后自己在瞎琢磨研究,写了一个简单的例子,用来 ...

  5. API接口的研发与应用

    ​ API(Application Programming Interface,应用程序编程接口)指的是为不同的软件应用程序提供编程接口的一组协议.规则以及工具的集合,以便它们能够互相交互,实现数据通 ...

  6. git clone时报错:Permission denied

    一.问题简述: 执行git clone git@github.com:T-Better/Soft_test.git时报错:\302\226git@github.com: Permission deni ...

  7. Ionic 整合 pixi.js

    最近做了个app,上线google play不大顺利,说是有假冒行为,然后改了下icon和名字以及描述,但是没啥信息去上,于是暂时放下搞点别的. 因为近期看到个比较有趣的绘图创意, 于是想通过ioni ...

  8. MySQL系列3:缓冲池Buffer Pool的设计思想

    1. 回顾 上一篇我们主要讲了InnoDB的存储引擎,其中主要的一个组件就是缓存池Buffer Pool,缓存了磁盘的真实数据,然后基于缓存做增删改查操作,同时配合了后续的redo log.刷磁盘等机 ...

  9. 解密网络通信的关键技术(下):DNS、ARP、DHCP和NAT,你了解多少?

    引言 在上一章中,我们详细介绍了域名系统(DNS)和地址解析协议(ARP)的工作原理,从而对域名解析和介质访问控制(MAC)地址寻址有了更深入的了解.在今天的章节中,我们将继续探讨动态主机配置协议(D ...

  10. Cplex混合整数规划求解(Python API)

    绝对的原创!罕见的Cplex-Python API混合整数规划求解教程!这是我盯了一天的程序一条条写注释一条条悟出来的•́‸ก 一.问题描述 求解有容量限制的的设施位置问题,使用Benders分解.模 ...