目录

一.过滤器如何工作

  不同的过滤器类型在管道中的不同阶段执行,因此具有各自的与其场景。根据需要执行的任务以及需要执行的请求管道中的位置,选择要创建的过滤器类型。过滤器在 mvc 操作调用管道中运行,有时也称为过滤管道,在 mvc 中选择要执行的操作后,执行操作上的过滤器,如图:

  不同的过滤器在管道内的不同位置执行。像授权过滤器这样的过滤器只在管道中靠前的位置执行。其他过滤器,如操作(action)过滤器,可以在管道执行的其他部分之前和之后执行,如图:

1.选择过滤器

  授权过滤器用于确定当前请求用户是否被授权。

  资源过滤器是在授权之后第一个处理请求的过滤器,也是最后一个在请求离开过滤管道时接触请求的过滤器。在性能方面,对实现缓存或者对过滤管道进行短路 特别有用。

  操作过滤器包装对单个操作方法的调用,并且可以处理传递到操作的参数以及从操作返回的操作结果。

  异常过滤器用于对 mvc 应用程序中未处理的异常应用全局策略。

  结果过滤器包装单个操作结果的执行,并且尽在操作执行成功时运行。它们必须是围绕视图执行或格式化程序执行的逻辑的理想选择。 

2.实现过滤器

  所有过滤器均可通过不同的接口定义支持同步和异步的实现。根据需要执行的任务类型,选择同步或异步实现。从框架的角度看,它们是可以互换的。

  同步过滤器定义了 onstageexecuting onstageexecuted 方法(也有例外)。onstageexecuting 方法在事件管道阶段之前通过阶段名称来调用,而 onstageexecuted 方法将在阶段名称命名的管道阶段之后调用。

 public class sampleactionfilter:iactionfilter
    {
        public void onactionexecuting(actionexecutingcontext context)
        {
            //操作执行前做的事情
        }
        public void onactionexecuted(actionexecutedcontext context)
        {
            //操作执行后做的事情
        }
    }

  异步过滤器定义了一个单一的 onactionexecutionasync  方法,可以在具体管道阶段的前后运行。 onactionexecutionasync 方法提供了一个 actionexecutiondelegate 委托,调用时该委托会执行具体管道阶段的工作,然后等待完成。

public class sampleasyncactionfilter: iasyncactionfilter
    {
        public async task onactionexecutionasync(actionexecutingcontext context, actionexecutiondelegate next)
        {
            //操作执行前做的事情
            await next();
            //操作执行后做的事情
        }
    }

3.过滤器作用域

  过滤器有三种不同级别的作用域。你可以在特定的操作上用特性(attribute)的方式使用特定的过滤器。也可以在控制器上用特性的方式使用过滤器,这样就可以将效果作用在控制器内的所有操作上。或者注册一个全局过滤器,它将作用于整个 mvc 应用程序的每一个操作。

  如果想要使用全局过滤器,可以在配置 mvc 时,在 startup configureservices 方法中添加:

services.addmvc(options =>
            {
                options.filters.add(typeof(sampleactionfilter));//通过类型
                options.filters.add(new sampleactionfilter());//注册实例
            }).setcompatibilityversion(compatibilityversion.version_2_1);


  过滤器既可以通过类型添加,也可以通过实例添加。如果通过实例添加,则该实例会被使用于每一个请求。如果通过类型添加,则在每次请求后都会创建一个实例,其所有构造函数依赖项都将通过 di 来填充。

  把过滤器接口的实现当作特性使用也非常方便。过滤器特性可应用于控制器和操作方法。框架包含了内置的基于特性的过滤器,可以继承他们或者另外定制。例如,下面的过滤器继承了 resultfilterattribute,并重写 onresultexecuting 方法(在响应中增加一个信息头): 

public class addheaderattribute: resultfilterattribute
    {
        private readonly string _name;
        private readonly string _value;

        public addheaderattribute(string name, string value)
        {
            _name = name;
            _value = value;
        }

        public override void onresultexecuting(resultexecutingcontext context)
        {
            context.httpcontext.response.headers.add(
                _name,new string[] { _value });
            base.onresultexecuting(context);
        }
    }

  特性允许过滤器接受参数,如下,可将此特性添加到控制器或操作中,并为其指定所需 http 头的名称和值:

[addheader("author", "ruby lu")]
 public class homecontroller : controller
{
}

以下几种过滤器接口可以自定义为相应特性的实现:

  • actionfilterattribute
  • exceptionfilterattribute
  • resultfilterattribute
  • formatfilterattribute
  • servicefilterattribute
  • typefilterattribute

 4.取消和短路

  通过设置传入过滤器方法的上下文参数中的 result 属性,可以在过滤器管道的任意一点短路管道。比如,下面的 shortcircuitingresourcefilter 将阻止它之后管道内的所有过滤器,包括所有操作过滤器:

public class shortcircuitingresourcefilter:attribute,iresourcefilter
    {
        public void onresourceexecuting(resourceexecutingcontext context)
        {
            context.result = new contentresult() {
                content = "短路"
            };
        }

        public void onresourceexecuted(resourceexecutedcontext context)
        {

        }
    }

二.配置过滤器

  全局过滤器在 startup 中配置。基于特性的过滤器如果不需要任何依赖,可以简单地继承一个已存在地过滤器相对应地特性类型。如果要创建一个非全局作用域,但需要从依赖注入中获得依赖项的过滤器,那么在它们上面加上 servicefilterattribute typefilterattribute 特性,这样就可用于控制器或操作了。

1.依赖注入

  以特性形式实现的,直接添加到控制器或操作的过滤器,其构造函数不得由依赖注入提供依赖项。其原因在于,特性所需的构造函数参数必须由使用处直接提供。这是特性原型机理的限制。

  如果过滤器需要从 di 中获得依赖项,那么可以用以下几种方法在类或操作方法使用:

  • servicefilterattribute
  • typefilterattribute
  • ifilterfactory 实现特性

  typefilter 将为其依赖项从 di 中使用服务来实例化一个实例。 servicefilter 则从 di 中获取一个过滤器实例。下面演示 servicefilter

  先在 configureservices 中注册 addheaderfilterwithdi 类型:services.addscoped<addheaderfilterwithdi>();

然后使用:

   [servicefilter(typeof(addheaderfilterwithdi))]

    public iactionresult index()

    {  

    }

  servicefilterattribute 实现了ifilterfactory 接口,它公开了一个创建 ifilter 实例的方法。在 servicefilterattribute 中,ifilterfactory 接口的 createinstance 方法被实现为从服务容器加载指定的类型。

  typefilterattribute 非常类似 servicefilterattribute (也实现 ifilterfactory 接口),但它的类型不是直接从 di 容器中解析,相反,它使用 microsoft.extensions.dependencyinjection.objectfactory 实例化类型。

  由于这种差异,使用 typefilterattribute 引用的类型不需要在使用前向容器注册,但它们仍由容器来填充其依赖项。此外,typefilterattribute 可以可选的接受该类型的构造函数参数。下面是 typefilterattribute 演示:

  

      [typefilter(typeof(addheaderattribute),arguments =new object[] { "author","ruby" })]
        public iactionresult index()
        {
            return view();
        }

   如果有一个简单的过滤器,不需要任何参数,但有构造函数需要通过 di 填充依赖项,那么可以继承 typefilterattribute,允许使用自己命名的特性类和方法(而不是 [typefilterattribute(typeof(filtertype))])。下面的过滤器显示了如何实现此功能:

 public class sampleactionfilterattribute:typefilterattribute
    {
        public sampleactionfilterattribute() : base(typeof(sampleactionfilterimpl))
        {
        }
        private class sampleactionfilterimpl:iactionfilter
        {
            public void onactionexecuting(actionexecutingcontext context)
            {
                //操作执行前做的事情
            }
            public void onactionexecuted(actionexecutedcontext context)
            {
                //操作执行后做的事情
            }
        }
    }

  该过滤器可通过使用 [sampleactionfilter] 这样的语法应用于类或方法,而不必使用 [typefilter] 或 [servicefilter] 。

  ifilterfactory 实现 ifilter ,因此在过滤器管道中,任何位置的  ifilterfactory 实例都可当作 filter 实例来使用。当框架准备调用过滤器时,将尝试将其转换为 ifilterfactory 。如果转换成功, 则调用 createinstance 方法来创建将被调用的 ifilter 实例。这是一种非常灵活的设计,因为当应用程序启动时,不需要明确地设置精确地过滤器。

  你可以在自己地特性中实现 ifilterfactory 几口,作为另一种创建过滤器的方法:

public class addheadwithfactoryattribute:attribute, ifilterfactory
    {
        public bool isreusable { get; }
        //实现ifilterfactory
        public ifiltermetadata createinstance(iserviceprovider serviceprovider)
        {
            return new internaladdheaderfilter();
        }
    }

    public class internaladdheaderfilter : iresultfilter
    {
        public  void onresultexecuting(resultexecutingcontext context)
        {
            context.httpcontext.response.headers.add(
                "internal", new string[] { "header add" });
        }
        public void onresultexecuted(resultexecutedcontext context)
        {

        }
    }

 

2.排序

   过滤器可以应用于操作方法或控制器(通过特性)或添加到全局过滤器集合中。作用域通常也决定了排序,最接近操作的过滤器首先运行。

  除了作用域,过滤器还可以通过实现 iorderedfilter 来重写它们的执行顺序。此接口简单的暴露了一个 int order 属性,并且过滤器基于该属性以数字升序执行。所有内置的过滤器,包括 typefilterattribute servicefilterattribute ,都实现 iorderedfilter  接口。,因此当将过滤器特性应用于类或方法时,可以指定过滤器执行顺序。默认情况下,所有内置过滤器的 order 属性都为0,因此范围用作分隔符,并且是决定性因素(除非 order 设置为 0)。

  每个从 controller 基类继承的控制器都包含 onactionexecuting onactionexecuted 方法。这些方法为给定操作包装了过滤器,它们分别最先运行和最后运行。假设没有为任何过滤器设置 order 舒总,那么单纯基于范围的顺序为:

  • 控制器的 onactionexecuting
  • 全局过滤器的onactionexecuting
  • 类过滤器的onactionexecuting
  • 方法过滤器的onactionexecuting
  • 方法过滤器的onactionexecuted
  • 类过滤器的onactionexecuted
  • 全局过滤器的onactionexecuted
  • 控制器过滤器的onactionexecuted

   要修改默认的基于范围的顺序,则应显示设置类级别或者方法级别过滤器的 order 属性。例如,将 order = -1 添加到方法级属性:

  [myfilter (name = "...",order = -1)]

在这种情况下,小于零的值将确保此过滤器在全局和类级过滤器之前运行:

  • 控制器的 onactionexecuting
  • 方法过滤器的onactionexecuting
  • 全局过滤器的onactionexecuting
  • 类过滤器的onactionexecuting
  • 类过滤器的onactionexecuted
  • 全局过滤器的onactionexecuted
  • 控制器过滤器的onactionexecuted
  • 方法过滤器的onactionexecuted

  controller 类的方法总是在所有过滤器之前和之后运行。这些方法不作为ifilter实例实现。也不参与ifilter排序算法。

 3.对比中间件

  一般来说,过滤器用于处理业务与应用程序的横切关注点,用法和功能很像中间件,但过滤器允许你将作用范围缩小,并将其插入到应用程序中有意义的位置,例如视图之前或模型绑定之后。过滤器是 mvc 的一部分,可以访问其上下文和构造函数。例如,中间件很难检测到请求的模型验证是否产生错误,并且做出相应的响应。

到此这篇关于asp.net core mvc 过滤器(filter)的文章就介绍到这了,更多相关asp.net core mvc 过滤器内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!