7.5筛选器(过滤器)

7.5筛选器(过滤器)

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

异常筛选器

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

  1. 编写筛选器
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
//异步异常筛选器要实现IAsyncExceptionFilter接口
public class MyExceptionFilter : IAsyncExceptionFilter
{
    //由于筛选器中需要把异常写入日志,并且要判断运行环境,所以加入这两个服务
    private readonly ILogger<MyExceptionFilter> logger;
    private readonly IHostEnvironment env;
    public MyExceptionFilter(ILogger<MyExceptionFilter> logger, IHostEnvironment env)
    {
        this.logger = logger;
        this.env = env;
    }
    public Task OnExceptionAsync(ExceptionContext context)
    {
        Exception exception = context.Exception;//获取异常对象
        logger.LogError(exception, "UnhandledException occured");//将异常写入日志
        string message;
        if (env.IsDevelopment())//如果是开发环境
        {
            message = exception.ToString();
        }
        else
        {
            message = "程序中出现未处理异常";
        }
        //响应报文头文件
        ObjectResult result = new ObjectResult(new { code = 500, message = message });
        result.StatusCode = 500;
        context.Result = result;
        //告诉ASP.NET Core不再执行默认的响应逻辑
        context.ExceptionHandled = true;
        return Task.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
using Microsoft.AspNetCore.Mvc.Filters;

public class MyActionFilter1 : IAsyncActionFilter
{
	public async Task OnActionExecutionAsync(ActionExecutingContext context, 
		ActionExecutionDelegate next)
	{
		Console.WriteLine("MyActionFilter 1:开始执行");
        //next之前的代码是在操作方法执行之前要执行的代码
		ActionExecutedContext r = await next();//执行下一个筛选器,
         //如果next出现异常,则ActionExecutedContext.Exception则为异常对像,如果没有异常
        	//则使用ActionExecutedContext.Result获取
         //next之后的代码是在操作方法执行之后要执行的代码
		if (r.Exception != null)
		{
			Console.WriteLine("MyActionFilter 1:执行失败");
		}
		else
		{
			Console.WriteLine("MyActionFilter 1:执行成功");
		}
	}
}
  1. 编写操作筛选器2
using Microsoft.AspNetCore.Mvc.Filters;

public class MyActionFilter2 : IAsyncActionFilter
{
	public async Task OnActionExecutionAsync(ActionExecutingContext context, 
		ActionExecutionDelegate next)
	{
		Console.WriteLine("MyActionFilter 2:开始执行");
		ActionExecutedContext r = await next();
		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]
public string GetData()
{
    Console.WriteLine("执行GetData");
    return "hello";
}
  1. 结果

在这里插入图片描述

案例1 自动启用事务

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

  1. 我们定义数据库操作都默认使用事务,对于不使用事务的方法,使用NotTransactionAttribute来标记
[AttributeUsage(AttributeTargets.Method)]
public class NotTransactionalAttribute : Attribute{}
  1. 实现筛选器
public class TransactionScopeFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
		bool hasNotTransactionalAttribute = false;
        //判断是否标注了NotTransactionalAttribute
		if (context.ActionDescriptor is ControllerActionDescriptor)
		{
			var actionDesc = (ControllerActionDescriptor)context.ActionDescriptor;
			hasNotTransactionalAttribute = actionDesc.MethodInfo
				.IsDefined(typeof(NotTransactionalAttribute));
		}
        //如果不想添加事务则直接使用next执行
		if (hasNotTransactionalAttribute)
		{
			await next();
			return;
		}
        //用using声明作用域,将所有数据库操作都包含进该作用域中
        //因为OnActionExecutionAsync是异步,所以要使用TransactionScopeAsyncFlowOption.Enabled
        //表示作用域会跨线程
		using var txScope =
				new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
		var result = await next();
		if (result.Exception == null)
		{
			txScope.Complete();//如果没有异常则提交事务
		}
	}
}
  1. 注册到Program.cs中
builder.Services.Configure<MvcOptions>(options =>
{
    options.Filters.Add<TransactionScopeFilter>();
});

案例2:请求限流器

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

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Memory;

public class RateLimitFilter : IAsyncActionFilter
{
	private readonly IMemoryCache memCache;//记录上一次的访问时间
	public RateLimitFilter(IMemoryCache memCache)
	{
		this.memCache = memCache;
	}
	public Task OnActionExecutionAsync(ActionExecutingContext context,
			ActionExecutionDelegate next)
	{
        //获取用户IP
		string removeIP = context.HttpContext.Connection.RemoteIpAddress.ToString();
		string cacheKey = $"LastVisitTick_{removeIP}";
		long? lastTick = memCache.Get<long?>(cacheKey);//从缓存中获取上次访问的时间
		if (lastTick == null || Environment.TickCount64 - lastTick > 1000)//不存在或者大于1s
		{
			memCache.Set(cacheKey, Environment.TickCount64,TimeSpan.FromSeconds(10));//设置缓存
			return next();//执行操作方法
		}
		else//频繁访问
		{//不执行next(),即不再执行操作方法
			context.Result = new ContentResult { StatusCode= 429 } ;
			return Task.CompletedTask;
		}
	}
}

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

builder.Services.Configure<MvcOptions>(options =>
{
    options.Filters.Add<RateLimitFilter>();
});
builder.Services.AddMemoryCache();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步、步、为营

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值