当前位置: 首页 > article >正文

ASP.NET Core Filter

目录

什么是Filter?

Exception Filter

实现

注意

ActionFilter

注意

案例:自动启用事务的筛选器

事务的使用

TransactionScopeFilter的使用


什么是Filter?

  1. 切面编程机制,在ASP.NET Core特定的位置执行我们自定义的代码。
  2. ASP.NET Core中的Filter的五种类型:Authorization filter、Resource filter、Action filter、Exception filter、Result filter。本书中重点讲解Exception filter和Action filter。
  3. 所有筛选器一般有同步和异步两个版本,比如IActionFilter、IAsyncActionFilter接口。

Exception Filter

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

实现

  1. 当系统中出现未处理异常的时候,我们需要统一给客户端返回如下格式的响应报文:{“code”:”500”,”message”:”异常信息”}。
    对于开发环境中message是异常堆栈,对于其他环境message用一个general的报错信息。
  2. 实现IAsyncExceptionFilter接口。注入IHostEnvironment得知运行环境。
public class LogExceptionFilter : IAsyncExceptionFilter
{
    public Task OnExceptionAsync(ExceptionContext context)
    {
        return File.AppendAllTextAsync("F:/error.log", context.Exception.ToString());
    }
}

public class MyExceptionFilter : IAsyncExceptionFilter
{
    private readonly IWebHostEnvironment hostEnv;

    public MyExceptionFilter(IWebHostEnvironment hostEnv)
    {
        this.hostEnv = hostEnv;
    }

    public Task OnExceptionAsync(ExceptionContext context)
    {
        //context.Exception代表异常信息对象
        //context.ExceptionHandled为true时,表示异常已经被处理,其他ExceptionFilter将不会再处理
        //context.Result的值会返回给客户端
        string msg;
        if (hostEnv.IsDevelopment())
        {
            msg = context.Exception.Message;
        }
        else
        {
            msg = "服务器发生了未处理异常";
        }
        ObjectResult objresult = new ObjectResult(new { code = 500, message = msg });
        context.Result = objresult;
        context.ExceptionHandled = true;
        return Task.CompletedTask;
    }
}

注意

ExceptionFilter执行顺序与注册顺序有关,后注册的先执行

builder.Services.Configure<MvcOptions>(opt =>
{
    opt.Filters.Add<MyExceptionFilter>();
    opt.Filters.Add<LogExceptionFilter>();
});

ActionFilter

IAsyncActionFilter接口

多个Action Filter的链式执行。

注意

ActionFilter执行顺序与注册顺序有关,先注册的先执行

public class MyActionFilter1 : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        Console.WriteLine("MyActionFilter1前代码");
        ActionExecutedContext result = await next();
        if (result.Exception != null)
        {
            Console.WriteLine("MyActionFilter1捕获到异常");
        }
        else
        {
            Console.WriteLine("MyActionFilter1后代码");
        }
    }
}
builder.Services.Configure<MvcOptions>(opt =>
{
    opt.Filters.Add<MyActionFilter1>();
    opt.Filters.Add<MyActionFilter2>();
});

案例:自动启用事务的筛选器

  1. 数据库事务:要么全部成功、要么全部失败。
  2. 自动化:启动、提交以及回滚事务。
  3. 当一段使用EF Core进行数据库操作的代码放到TransactionScope声明的范围中的时候,这段代码就会自动被标记为“支持事务”。
  4. TransactionScope实现了IDisposable接口,如果一个TransactionScope的对象没有调用Complete()就执行了Dispose()方法,则事务会被回滚,否则事务就会被提交。
  5. TransactionScope还支持嵌套式事务。
  6. .NET Core中的TransactionScope不像.NET FX一样有MSDTC分布式事务提升的问题。请使用最终一致性事务。

事务的使用

[HttpPost]
public async Task<string> Test2()
{
    using (TransactionScope tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        dbctx.Books.Add(new Book { Title = "计算机网络", Price = 12.6 });
        await dbctx.SaveChangesAsync();
        dbctx.People.Add(new Person { Name = "张三", Age = 20 });
        await dbctx.SaveChangesAsync();
        tx.Complete();
        return "OK";
    }
}

[HttpPost]
public string Test1()
{
    using (TransactionScope tx = new TransactionScope())
    {
        dbctx.Books.Add(new Book { Title = "计算机网络", Price = 12.6 });
        dbctx.SaveChangesAsync();
        dbctx.People.Add(new Person { Name = "张三", Age = 20 });
        dbctx.SaveChangesAsync();
        tx.Complete();
        return "OK";
    }
}

TransactionScopeFilter的使用

对于强制不进行事务控制的Action方法,请标注NotTransactionalAttribute。

开发筛选器TransactionScopeFilter;把TransactionScopeFilter注册到Program.cs中。

NotTransationAttribute.cs:
[AttributeUsage(AttributeTargets.Method)]
public class NotTransationAttribute:Attribute
{
}

TransactionScopeFilter.cs:
public class TransactionScopeFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        //context.ActionDescriptor中是当前被执行的Action的描述信息
        //context.ActionArguments中是当前被执行的Action的参数信息
        ControllerActionDescriptor controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
        //if (controllerActionDescriptor == null)//不是一个MVC的Action
        bool isTX = false;//是否进行事务控制
        if (controllerActionDescriptor != null)
        {
            //获取当前Action是否有NotTransationAttribute特性
            bool hasNotTransationAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(NotTransationAttribute), false).Any();
            //如果没有NotTransationAttribute特性,则进行事务控制
            isTX = !hasNotTransationAttribute;
        }
        if (isTX)
        {
            //创建一个异步的事务范围
            using (TransactionScope tx=new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                var r= await next();
                if (r.Exception==null)
                {
                    tx.Complete();
                }
            }
        }
        else
        {
            await next();
        }
    }
}

Program.cs:
builder.Services.Configure<MvcOptions>(opt =>
{
    opt.Filters.Add<TransactionScopeFilter>();
});

 案例:开发请求限流器

需求

  1. Action Filter可以在满足条件的时候终止操作方法的执行。
  2. 在Action Filter中,如果我们不调用await next(),就可以终止Action方法的执行了。
  3. 为了避免恶意客户端频繁发送大量请求消耗服务器资源,我们要实现“一秒钟内只允许最多有一个来自同一个IP地址的请求”。
public class ratelimitActionFilter : IAsyncActionFilter
{
    private readonly IMemoryCache memcache;

    public ratelimitActionFilter(IMemoryCache memcache)
    {
        this.memcache = memcache;
    }

    public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        //从HTTP请求的上下文中获取客户端的远程IP地址
        string removeIP = context.HttpContext.Connection.RemoteIpAddress.ToString();
        //context.ActionDescriptor中是当前被执行的Action的描述信息,转换为ControllerActionDescriptor类型
        ControllerActionDescriptor controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
        //构建缓存的Key
        string cacheKey = $"LastVisitTick_{controllerActionDescriptor.ControllerName}_{removeIP}";
        //从缓存中获取上次访问的时间戳
        long? lastTick = memcache.Get<long?>(cacheKey);
        //如果上次访问的时间戳不存在或者距离当前时间已经超过1秒
        if (lastTick == null || Environment.TickCount64 - lastTick > 1000)
        {
            //设置当前的时间戳到缓存中
            memcache.Set(cacheKey, Environment.TickCount64, TimeSpan.FromSeconds(10));//避免长期不访问的用户一直占用缓存
            return next();
        }
        else
        {
            context.Result = new ContentResult
            {
                StatusCode = 429,
                Content = "访问太频繁,请稍后再试!"
            };
            return Task.CompletedTask;
        }
    }
}


http://www.kler.cn/a/530160.html

相关文章:

  • Unity Shader Graph 2D - 角色身体电流覆盖效果
  • 如何配置Java JDK
  • Mybatis——sql映射文件中的增删查改
  • 携程Android开发面试题及参考答案
  • 科技快讯 | OpenAI首次向免费用户开放推理模型;特朗普与黄仁勋会面;雷军回应“10后小学生深情表白小米SU7”
  • linux 函数 sem_init () 信号量、sem_destroy()
  • 一文讲解Java中HashMap的put流程
  • 完全卸载mysql server步骤
  • Unity游戏(Assault空对地打击)开发(3) 摄像机的控制
  • C# 精炼题18道题(类,三木运算,Switch,计算器)
  • DeepSeek与OpenAI:谁是AI领域的更优选择?
  • 04树 + 堆 + 优先队列 + 图(D1_树(D8_B*树(B*)))
  • 书生大模型实战营7
  • openmv的端口被拆分为两个 导致电脑无法访问openmv文件系统解决办法 openmv USB功能改动 openmv驱动被更改如何修复
  • 人工智能学习(四)之机器学习基本概念
  • work-stealing算法 ForkJoinPool
  • 【C语言】填空题/程序填空题1
  • 第三百六十节 JavaFX教程 - JavaFX 进度显示器
  • 2025-工具集合整理
  • 2025年2月2日(网络编程 tcp)
  • LeetCode:300.最长递增子序列
  • 【Rust自学】19.3. 高级函数和闭包
  • 【TCP协议】流量控制 滑动窗口 拥塞控制
  • 第二篇:多模态技术突破——DeepSeek如何重构AI的感知与认知边界
  • Spring应用场景 特性
  • 【C语言】自定义类型讲解