7.5筛选器(过滤器)

简介: 筛选器运行开发人员在ASP.NET Core特定的位置执行我们自己的代码,比如在控制器的操作方法之前执行数据检查,或者在ActionResult执行的时候向响应报文头中加入自定义数据。

7.5筛选器(过滤器)

筛选器运行开发人员在ASP.NET Core特定的位置执行我们自己的代码,比如在控制器的操作方法之前执行数据检查,或者在ActionResult执行的时候向响应报文头中加入自定义数据。

异常筛选器

系统中出现未处理异常的时候,就会自动执行异常筛选器

  1. 编写筛选器

usingMicrosoft.AspNetCore.Mvc;

usingMicrosoft.AspNetCore.Mvc.Filters;

//异步异常筛选器要实现IAsyncExceptionFilter接口

publicclassMyExceptionFilter : IAsyncExceptionFilter

{

   //由于筛选器中需要把异常写入日志,并且要判断运行环境,所以加入这两个服务

   privatereadonlyILogger<MyExceptionFilter>logger;

   privatereadonlyIHostEnvironmentenv;

   publicMyExceptionFilter(ILogger<MyExceptionFilter>logger, IHostEnvironmentenv)

   {

       this.logger=logger;

       this.env=env;

   }

   publicTaskOnExceptionAsync(ExceptionContextcontext)

   {

       Exceptionexception=context.Exception;//获取异常对象

       logger.LogError(exception, "UnhandledException occured");//将异常写入日志

       stringmessage;

       if (env.IsDevelopment())//如果是开发环境

       {

           message=exception.ToString();

       }

       else

       {

           message="程序中出现未处理异常";

       }

       //响应报文头文件

       ObjectResultresult=newObjectResult(new { code=500, message=message });

       result.StatusCode=500;

       context.Result=result;

       //告诉ASP.NET Core不再执行默认的响应逻辑

       context.ExceptionHandled=true;

       returnTask.CompletedTask;

   }

}

  1. 设置全局筛选器,在Program.cs的builder.Build之前添加

//MvcOptions是ASP.NET Core项目的主要配置对象,这是新的用法

builder.Services.Configure<MvcOptions>(options=>

{//注册全局筛选器,这样ASP.NET Core所有未处理的异常都可以被MyMyExceptionFilte处理

   options.Filters.Add<MyExceptionFilter>();

});

操作筛选器

控制器中操作方法执行的时候,操作筛选器就会被执行,通常可以在操作方法执行之前或者之后执行一些代码

操作筛选器要实现IAsyncActionFilter接口,接口中定义了

public async Task OnActionExecutionAsync(ActionExecutingContext context,   ActionExecutionDelegate next)方法

context:代表Action执行的上下文对象,从context中可以获取请求路径等信息

next:用来指向下一个操作器的委托,一个项目可以有多个操作筛选器,如果当前操作筛选器是最后一个筛选器则next就是要执行的操作方法

  1. 编写操作筛选器1

usingMicrosoft.AspNetCore.Mvc.Filters;

publicclassMyActionFilter1 : IAsyncActionFilter

{

   publicasyncTaskOnActionExecutionAsync(ActionExecutingContextcontext,

       ActionExecutionDelegatenext)

   {

       Console.WriteLine("MyActionFilter 1:开始执行");

       //next之前的代码是在操作方法执行之前要执行的代码

       ActionExecutedContextr=awaitnext();//执行下一个筛选器,

        //如果next出现异常,则ActionExecutedContext.Exception则为异常对像,如果没有异常

           //则使用ActionExecutedContext.Result获取

        //next之后的代码是在操作方法执行之后要执行的代码

       if (r.Exception!=null)

       {

           Console.WriteLine("MyActionFilter 1:执行失败");

       }

       else

       {

           Console.WriteLine("MyActionFilter 1:执行成功");

       }

   }

}

  1. 编写操作筛选器2

usingMicrosoft.AspNetCore.Mvc.Filters;

publicclassMyActionFilter2 : IAsyncActionFilter

{

   publicasyncTaskOnActionExecutionAsync(ActionExecutingContextcontext,

       ActionExecutionDelegatenext)

   {

       Console.WriteLine("MyActionFilter 2:开始执行");

       ActionExecutedContextr=awaitnext();

       if (r.Exception!=null)

       {

           Console.WriteLine("MyActionFilter 2:执行失败");

       }

       else

       {

           Console.WriteLine("MyActionFilter 2:执行成功");

       }

   }

}

  1. 在Program.cs注册筛选器

builder.Services.Configure<MvcOptions>(options=>

{

   options.Filters.Add<MyActionFilter1>();//注册顺序及时执行顺序

   options.Filters.Add<MyActionFilter2>();

});

  1. 在控制器中增加操作方法

[HttpGet]

publicstringGetData()

{

   Console.WriteLine("执行GetData");

   return"hello";

}

  1. 结果

案例1 自动启用事务

数据库事务保证了我们对数据的操作要么全部成功,要么全部失败,我们可以使用TransactionScope来操作数据库事务,将使用EF Core的数据库操作放到TransactionScope声明的范围内,则这段代码自动支撑事务。

  1. 我们定义数据库操作都默认使用事务,对于不使用事务的方法,使用NotTransactionAttribute来标记

[AttributeUsage(AttributeTargets.Method)]

publicclassNotTransactionalAttribute : Attribute{}

  1. 实现筛选器

publicclassTransactionScopeFilter : IAsyncActionFilter

{

   publicasyncTaskOnActionExecutionAsync(ActionExecutingContextcontext, ActionExecutionDelegatenext)

   {

       boolhasNotTransactionalAttribute=false;

       //判断是否标注了NotTransactionalAttribute

       if (context.ActionDescriptorisControllerActionDescriptor)

       {

           varactionDesc= (ControllerActionDescriptor)context.ActionDescriptor;

           hasNotTransactionalAttribute=actionDesc.MethodInfo

               .IsDefined(typeof(NotTransactionalAttribute));

       }

       //如果不想添加事务则直接使用next执行

       if (hasNotTransactionalAttribute)

       {

           awaitnext();

           return;

       }

       //用using声明作用域,将所有数据库操作都包含进该作用域中

       //因为OnActionExecutionAsync是异步,所以要使用TransactionScopeAsyncFlowOption.Enabled

       //表示作用域会跨线程

       usingvartxScope=

               newTransactionScope(TransactionScopeAsyncFlowOption.Enabled);

       varresult=awaitnext();

       if (result.Exception==null)

       {

           txScope.Complete();//如果没有异常则提交事务

       }

   }

}

  1. 注册到Program.cs中

builder.Services.Configure<MvcOptions>(options=>

{

   options.Filters.Add<TransactionScopeFilter>();

});

案例2:请求限流器

如果有客户端频繁发送请求而消耗服务器资源,则可以限制1s内只允许同一个IP的一次请求

usingMicrosoft.AspNetCore.Mvc;

usingMicrosoft.AspNetCore.Mvc.Filters;

usingMicrosoft.Extensions.Caching.Memory;

publicclassRateLimitFilter : IAsyncActionFilter

{

   privatereadonlyIMemoryCachememCache;//记录上一次的访问时间

   publicRateLimitFilter(IMemoryCachememCache)

   {

       this.memCache=memCache;

   }

   publicTaskOnActionExecutionAsync(ActionExecutingContextcontext,

           ActionExecutionDelegatenext)

   {

       //获取用户IP

       stringremoveIP=context.HttpContext.Connection.RemoteIpAddress.ToString();

       stringcacheKey=$"LastVisitTick_{removeIP}";

       long?lastTick=memCache.Get<long?>(cacheKey);//从缓存中获取上次访问的时间

       if (lastTick==null||Environment.TickCount64-lastTick>1000)//不存在或者大于1s

       {

           memCache.Set(cacheKey, Environment.TickCount64,TimeSpan.FromSeconds(10));//设置缓存

           returnnext();//执行操作方法

       }

       else//频繁访问

       {//不执行next(),即不再执行操作方法

           context.Result=newContentResult { StatusCode=429 } ;

           returnTask.CompletedTask;

       }

   }

}

注册筛选器和内存缓存服务

builder.Services.Configure<MvcOptions>(options=>

{

   options.Filters.Add<RateLimitFilter>();

});

builder.Services.AddMemoryCache();

相关文章
|
Java 容器
28JavaWeb基础 - 过滤器
28JavaWeb基础 - 过滤器
54 0
|
15天前
|
存储 缓存 安全
常用过滤器介绍
常用过滤器介绍
22 0
|
1月前
|
Java 数据挖掘 Android开发
如何添加一个过滤器
如何添加一个过滤器
|
6月前
|
Python
过滤器
过滤器
28 2
|
JSON 数据格式
屏蔽词过滤器 1
屏蔽词过滤器
124 0
|
Java Nacos 微服务
路由过滤器 GatewayFilter
路由过滤器 GatewayFilter
74 0
路由过滤器 GatewayFilter
|
API 微服务
全局过滤器 GlobalFilter
全局过滤器 GlobalFilter
265 0
|
监控 Java 数据库连接
过滤器的应用
在上一篇博客中,我们简单的学习了一下面向切面编程,而过滤器就是对这一思想的应用。那如何在项目中使用呢?
charles 过滤器-简单过滤和设置过滤
charles 过滤器-简单过滤和设置过滤