欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.NET Core开发日志——Filter

程序员文章站 2022-06-10 13:33:37
ASP.NET Core MVC中的Filter作用是在请求处理管道的某些阶段之前或之后可以运行特定的代码。 Filter特性在之前的ASP.NET MVC中已经出现,但过去只有Authorization,Exception,Action,Result四种类型,现在又增加了一种Resource类型。 ......

asp.net core mvc中的filter作用是在请求处理管道的某些阶段之前或之后可以运行特定的代码。

filter特性在之前的asp.net mvc中已经出现,但过去只有authorization,exception,action,result四种类型,现在又增加了一种resource类型。所以共计五种。

resource类型filter在authorization类型filter之后执行,但又在其它类型的filter之前。且执行顺序也在model binding之前,所以可以对model bindin*生影响。

asp.net core mvc框架中可以看到有consumesattribute及formatfilter两种实现iresourcefilter接口的类。

consumesattribute会按请求中的content-type(内容类型)进行过滤,而formatfilter能对路由或路径中设置了format值的请求作过滤。

一旦不符合要求,就对resourceexecutingcontext的result属性设置,这样可以达到短路效果,阻止进行下面的处理。

consumesattribute类的例子:

public void onresourceexecuting(resourceexecutingcontext context)
{
    ...

    // only execute if the current filter is the one which is closest to the action.
    // ignore all other filters. this is to ensure we have a overriding behavior.
    if (isapplicable(context.actiondescriptor))
    {
        var requestcontenttype = context.httpcontext.request.contenttype;

        // confirm the request's content type is more specific than a media type this action supports e.g. ok
        // if client sent "text/plain" data and this action supports "text/*".
        if (requestcontenttype != null && !issubsetofanycontenttype(requestcontenttype))
        {
            context.result = new unsupportedmediatyperesult();
        }
    }
}

filter在asp.net core mvc里除了保留原有的包含同步方法的接口,现在又增加了包含异步方法的接口。

同步

  • iactionfilter
  • iauthorizationfilter
  • iexceptionfilter
  • iresourcefilter
  • iresultfilter

异步

  • iasyncactionfilter
  • iasyncauthorizationfilter
  • iasyncexceptionfilter
  • iasyncresourcefilter
  • iasyncresultfilter

新的接口不像旧有的接口包含两个同步方法,它们只有一个异步方法。但可以实现同样的功能。

public class sampleasyncactionfilter : iasyncactionfilter
{
    public async task onactionexecutionasync(
        actionexecutingcontext context,
        actionexecutiondelegate next)
    {
        // 在方法处理前执行一些操作
        var resultcontext = await next();
        // 在方法处理后再执行一些操作。
    }
}

attribute形式的filter,其构造方法里只能传入一些基本类型的值,例如字符串:

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);
    }
}


[addheader("author", "steve smith @ardalis")]
public class samplecontroller : controller

如果想要在其构造方法里引入其它类型的依赖,现在可以使用servicefilterattribute,typefilterattribute或者ifilterfactory方式。

servicefilterattribute需要在di容器中注册:

public class greetingservicefilter : iactionfilter
{
    private readonly igreetingservice greetingservice;

    public greetingservicefilter(igreetingservice greetingservice)
    {
        this.greetingservice = greetingservice;
    }

    public void onactionexecuting(actionexecutingcontext context)
    {
        context.actionarguments["param"] = 
            this.greetingservice.greet("james bond");
    }

    public void onactionexecuted(actionexecutedcontext context)
    { }
}


services.addscoped<greetingservicefilter>();


[servicefilter(typeof(greetingservicefilter))]
public iactionresult greetservice(string param)

typefilterattribute则没有必要:

public class greetingtypefilter : iactionfilter
{
    private readonly igreetingservice greetingservice;

    public greetingtypefilter(igreetingservice greetingservice)
    {
        this.greetingservice = greetingservice;
    }

    public void onactionexecuting(actionexecutingcontext context)
    {
        context.actionarguments["param"] = this.greetingservice.greet("dr. no");
    }

    public void onactionexecuted(actionexecutedcontext context)
    { }
}

[typefilter(typeof(greetingtypefilter))]
public iactionresult greettype1(string param)

ifilterfactory也是不需要的:

public class greetingfilterfactoryattribute : attribute, ifilterfactory
{
    public bool isreusable => false;

    public ifiltermetadata createinstance(iserviceprovider serviceprovider)
    {
        var logger = (igreetingservice)serviceprovider.getservice(typeof(igreetingservice));
        return new greetingfilter(logger);
    }

    private class greetingfilter : iactionfilter
    {
        private igreetingservice _greetingservice;
        public greetingfilter(igreetingservice greetingservice)
        {
            _greetingservice = greetingservice;
        }
        public void onactionexecuted(actionexecutedcontext context)
        {
        }

        public void onactionexecuting(actionexecutingcontext context)
        {
            context.actionarguments["param"] = _greetingservice.greet("dr. no");
        }
    }
    }

[greetingfilterfactory]
public iactionresult greettype1(string param)

filter有三种范围:

  • global
  • controller
  • action

后两种可以通过attribute的方式附加到特定action方法或者controller类之上。对于global,则要在configureservices方法内部添加。

public void configureservices(iservicecollection services)
{
    services.addmvc(options =>
    {
        // by instance
        options.filters.add(new adddeveloperresultfilter("tahir naushad"));

        // by type
        options.filters.add(typeof(greetdeveloperresultfilter)); 
    });

}

顾名思义,global将对所有controller及action产生影响。所以务必对其小心使用。

这三种范围的执行顺序在设计程序的时候也需要多作考虑:

  1. global范围的前置处理代码
  2. controller范围的前置处理代码
  3. action范围的前置处理代码
  4. action范围的后置处理代码
  5. controller范围的后置处理代码
  6. global范围的后置处理代码

典型的前置处理代码如常见的onactionexecuting方法,而常见的后置处理代码,则是像onactionexecuted方法这般的。