当前位置: 代码网 > it编程>编程语言>Asp.net > ASP.NET Core MVC过滤器运行流程解析

ASP.NET Core MVC过滤器运行流程解析

2024年05月18日 Asp.net 我要评论
mvc 的过滤器mvc 的过滤器(filters)也翻译为“筛选器”。但是老周更喜欢翻译为“过滤器”,意思上更好理解。既然都叫过滤器了,就是在mvc的操

mvc 的过滤器

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

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

a、此调用是否已授权?

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

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

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

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

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

过滤器专属命名空间 microsoft.aspnetcore.mvc.filters

咱们写代码一般不会实现 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过滤器运行流程解析的详细内容,更多关于asp.net core mvc过滤器的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com