.NET Core开发日志——Filter
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产生影响。所以务必对其小心使用。
这三种范围的执行顺序在设计程序的时候也需要多作考虑:
- global范围的前置处理代码
- controller范围的前置处理代码
- action范围的前置处理代码
- action范围的后置处理代码
- controller范围的后置处理代码
- global范围的后置处理代码
典型的前置处理代码如常见的onactionexecuting方法,而常见的后置处理代码,则是像onactionexecuted方法这般的。
上一篇: 为啥看领域驱动设计
下一篇: CDC+ETL实现数据集成方案
推荐阅读