当前位置: 首页 > 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/529398.html

相关文章:

  • ASP.NET Core 异常Filter
  • Web_php_unserialize
  • 【Redis】set 和 zset 类型的介绍和常用命令
  • 2025:影刀RPA使用新实践--CSDN博客下载
  • 快速提升网站收录:利用网站历史数据
  • Debian 10 中 Linux 4.19 内核在 x86_64 架构上对中断嵌套的支持情况
  • Python-基于PyQt5,pdf2docx,pathlib的PDF转Word工具(专业版)
  • Cypher入门
  • 两栏布局、三栏布局、水平垂直居中
  • docker gitlab arm64 版本安装部署
  • SpringBoot 整合 SpringMVC:配置嵌入式服务器
  • Redis集群理解以及Tendis的优化
  • nginx 报错404
  • 为AI聊天工具添加一个知识系统 之83 详细设计之24 度量空间之1 因果关系和过程:认知金字塔
  • Heptagon 迭代器
  • 【含文档+PPT+源码】基于微信小程序农家乐美食餐厅预约推广系统
  • P_all: 投影矩阵(Projection Matrix)
  • AI智慧社区--人脸识别
  • 详解CSS `clear` 属性及其各个选项
  • Rust 控制流语法详解
  • 蓝桥杯之c++入门(二)【输入输出(上)】
  • Lesson 129 70 miles an hour
  • 【C++】 STL -- 容器(2)
  • AI-on-the-edge-device - 将“旧”设备接入智能世界
  • 【开源免费】基于Vue和SpringBoot的校园网上店铺系统(附论文)
  • 区间加法 II - 解题思路与代码解析