ASP.NET Core 使用Filter和Redis实现接口防重

news2025/1/15 14:04:37

背景

日常开发中,经常需要对一些响应不是很快的关键业务接口增加防重功能,即短时间内收到的多个相同的请求,只处理一个,其余不处理,避免产生脏数据。

这和幂等性(idempotency)稍微有点区别,幂等性要求的是对重复请求有相同的效果结果,通常需要在接口内部执行业务操作前检查状态;而防重可以认为是一个业务无关的通用功能,在ASP.NET Core中我们可以借助过Filter和redis实现。

关于Filter

Filter的由来可以追溯到ASP.NET MVC中的ActionFilter和ASP.NET Web API中的ActionFilterAttribute。ASP.NET Core将这些不同类型的Filter统一为一种类型,称为Filter,以简化API和提高灵活性。

ASP.NET Core中Filter可以用于实现各种功能,例如身份验证、日志记录、异常处理、性能监控等。

通过使用Filter,我们可以在请求处理管道的特定阶段之前或者之后运行自定义代码,达到AOP的效果。

编码实现

防重组件的思路很简单,将第一次请求的某些参数作为标识符存入redis中,并设置过期时间,下次请求过来,先检查redis相同的请求是否已被处理;

作为一个通用组件,我们需要能让使用者自定义作为标识符的字段以及过期时间,下面开始实现。

PreventDuplicateRequestsActionFilter

public class PreventDuplicateRequestsActionFilter : IAsyncActionFilter{    public string[] FactorNames { get; set; }    public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }     private readonly IDistributedCache _cache;    private readonly ILogger<PreventDuplicateRequestsActionFilter> _logger;     public PreventDuplicateRequestsActionFilter(IDistributedCache cache, ILogger<PreventDuplicateRequestsActionFilter> logger)    {        _cache = cache;        _logger = logger;    }     public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)    {        var factorValues = new string?[FactorNames.Length];         var isFromBody =            context.ActionDescriptor.Parameters.Any(r => r.BindingInfo?.BindingSource == BindingSource.Body);        if (isFromBody)        {            var parameterValue = context.ActionArguments.FirstOrDefault().Value;            factorValues = FactorNames.Select(name =>                parameterValue?.GetType().GetProperty(name)?.GetValue(parameterValue)?.ToString()).ToArray();        }        else        {            for (var index = 0; index < FactorNames.Length; index++)            {                if (context.ActionArguments.TryGetValue(FactorNames[index], out var factorValue))                {                    factorValues[index] = factorValue?.ToString();                }            }        }         if (factorValues.All(string.IsNullOrEmpty))        {            _logger.LogWarning("Please config FactorNames.");             await next();            return;        }         var idempotentKey = $"{context.HttpContext.Request.Path.Value}:{string.Join("-", factorValues)}";        var idempotentValue = await  _cache.GetStringAsync(idempotentKey);        if (idempotentValue != null)        {            _logger.LogWarning("Received duplicate request({},{}), short-circuiting...", idempotentKey, idempotentValue);            context.Result = new AcceptedResult();        }        else        {            await _cache.SetStringAsync(idempotentKey, DateTimeOffset.UtcNow.ToString(),                new DistributedCacheEntryOptions {AbsoluteExpirationRelativeToNow = AbsoluteExpirationRelativeToNow});            await next();        }    }}

PreventDuplicateRequestsActionFilter里,我们首先通过反射从 ActionArguments拿到指定参数字段的值,由于从request body取值略有不同,我们需要分开处理;接下来开始拼接key并检查redis,如果key已经存在,我们需要短路请求,这里直接返回的是 Accepted (202)而不是Conflict (409)或者其它错误状态,是为了避免上游已经调用失败而继续重试。

PreventDuplicateRequestsAttribute

防重组件的全部逻辑在PreventDuplicateRequestsActionFilter中已经实现,由于它需要注入 IDistributedCacheILogger对象,我们使用IFilterFactory实现一个自定义属性,方便使用。

[AttributeUsage(AttributeTargets.Method)]public class PreventDuplicateRequestsAttribute : Attribute, IFilterFactory{    private readonly string[] _factorNames;    private readonly int _expiredMinutes;     public PreventDuplicateRequestsAttribute(int expiredMinutes, params string[] factorNames)    {        _expiredMinutes = expiredMinutes;        _factorNames = factorNames;    }     public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)    {        var filter = serviceProvider.GetService<PreventDuplicateRequestsActionFilter>();        filter.FactorNames = _factorNames;        filter.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_expiredMinutes);        return filter;    }    public bool IsReusable => false;}

注册

为了简单,操作redis,直接使用微软Microsoft.Extensions.Caching.StackExchangeRedis包;注册PreventDuplicateRequestsActionFilterPreventDuplicateRequestsAttribute无需注册。

builder.Services.AddStackExchangeRedisCache(options =>{    options.Configuration = "127.0.0.1:6379,DefaultDatabase=1";});builder.Services.AddScoped<PreventDuplicateRequestsActionFilter>();

使用

假设我们有一个接口CancelOrder,我们指定入参中的OrderId和Reason为因子。

namespace PreventDuplicateRequestDemo.Controllers{    [Route("api/[controller]")]    [ApiController]    public class OrderController : ControllerBase    {        [HttpPost(nameof(CancelOrder))]        [PreventDuplicateRequests(5, "OrderId", "Reason")]        public async Task<IActionResult> CancelOrder([FromBody] CancelOrderRequest request)        {            await Task.Delay(1000);            return new OkResult();        }    }     public class CancelOrderRequest    {        public Guid OrderId { get; set; }        public string Reason { get; set; }    }}
启动程序,多次调用api,除第一次调用成功,其余请求皆被短路

查看redis,已有记录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/557723.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

凌恩生物文献分享 | 16S全长扩增子+代谢组/转录组多组学!高分paper的绝佳拍档!

16S全长扩增子联合代谢组/转录组多组学研究&#xff0c;是目前微生态研究趋势。单一研究方法较难发表高水平文章&#xff0c;多组学关联研究逐渐成为“快速”发表高分paper的绝佳拍档&#xff01; 小编精心挑选了3篇医学微生态研究的高分文章&#xff0c;希望能帮助大家激发科…

二线程序员的出路

最近长沙不太平。去年被动离职一拨人之后&#xff0c;HR一直强调降本增效&#xff0c;人人自危&#xff0c;挤走一拨人&#xff0c;反正会有大量内卷失败的一线程序员进来填坑。当然留就有人走&#xff0c;前同事除了几个出去搞培训创业&#xff08;后面解散了&#xff09;的之…

chatgpt赋能Python-python_for_倒序

Python的倒序功能 - 从SEO角度分析 在Web开发中&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;是一个关键的指标。如果你的网站能够被搜索引擎高效地索引&#xff0c;它就会排在搜索结果的前面&#xff0c;带来更多的流量和曝光率。Python&#xff0c;作为一种广泛应用…

【LED子系统】七、触发器实现

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强公司&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录…

探索未来:人工智能如何改变室内外设计

目录 注册&#xff1a; 使用RoomGPT进行室内和室外设计的步骤&#xff1a; 结果如下&#xff1a; RoomGPT是一款基于人工智能的设计工具&#xff0c;可以帮助专业人士和个人在几秒钟内生成照片般逼真的图像。以下是使用RoomGPT进行室内和室外设计的步骤&#xff1a; 注册&am…

聚观早报 | ChatGPT炒股回报率超500%;网易发布11新游戏

今日要闻&#xff1a;微信支付正式发布“微信刷掌”产品&#xff1b;ChatGPT炒股回报率超500%&#xff1b;网易发布11新游戏&#xff1b;国家超算中心发布中文大语言模型&#xff1b;B站试水付费专属视频 微信支付正式发布“微信刷掌”产品 5 月 21 日&#xff0c;北京轨道交通…

时序电路详解

1、什么是时序电路&#xff1f; 组合电路是根据当前输入信号的组合来决定输出电平的电路&#xff0c;换言之&#xff0c;就是现在的输出不会被过去的输入所左右&#xff0c;也可以说成是&#xff0c;过去的输入状态对现在的输出状态没有影响的电路。 时序电路和组合电路不同&…

「角」毫米波雷达前装增速放缓?哪些供应商位居TOP10

作为传统BSD&#xff08;盲区监测、并线辅助&#xff09;、DOW&#xff08;开门预警&#xff09;功能以及高阶智能驾驶的主要传感器之一&#xff0c;角&#xff08;盲区&#xff09;毫米波雷达在2022年实现了前装搭载的大幅增长。 高工智能汽车研究院监测数据显示&#xff0c;2…

Qt编写视频监控系统74-悬浮工具栏(半透明/上下左右位置/自定义按钮)

一、前言 在监控系统中一般在视频实时预览的时候&#xff0c;希望提供一个悬浮工具条&#xff0c;可以显示一些提示信息比如分辨率、码率、帧率&#xff0c;提供一堆快捷操作按钮&#xff0c;可以录像、抓拍、云台控制、关闭等操作&#xff0c;参考了国内很多监控厂商客户端软…

Spring Boot 整合 分布式搜索引擎 Elastic Search 实现 搜索、分页与结果过滤

文章目录 ⛄引言一、酒店搜索和分页⛅需求分析⚡源码编写 二、酒店结果过滤⌚需求分析⏰修改搜索业务 ✅效果图⛵小结 ⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据…

【社群运营】AI智能对话,打造自动化社群

人工智能大背景下&#xff0c;各行各业都在往智能化发展&#xff0c;无论是办公产品&#xff0c;还是生产器械都选择接入了更加智能的AI来提高生产效率。那么&#xff0c;在日常的社群管理工作中&#xff0c;我们又能否跟上这一波热度&#xff0c;让社群自动化高效运营&#xf…

深度学习实战32-构建ChatT5模型,实现智能问答系统,类ChatGPT(CPU部署)

大家好,我是微学AI,今天给大家介绍一下深度学习实战32-构建ChatT5模型,实现智能问答系统,类ChatGPT(CPU部署),ChatT5使用了T5架构来处理输入文本,具有高度的并行性和扩展性,使其能够快速处理大规模数据集。与传统NLP模型不同,ChatT5采用端到端的方式进行训练,从而可以…

ov2640子设备核心操作详细分析

ov2640子设备核心操作详细分析 文章目录 ov2640子设备核心操作详细分析ov2640_subdev_core_ops核心操作获取寄存器值ov2640_g_register设置寄存器值ov2640_s_registeri2c_smbus_xferi2c_imx_xferi2c_smbus_xfer_emulatedi2c_transfer__i2c_transfer 设置ov2640的电源ov2640_s_p…

解决城市内涝的措施有哪些?需要用到哪些监测设备?

随着城市化的不断推进&#xff0c;城市内涝问题日益凸显。极端天气事件如暴雨、台风等对城市基础设施和居民生活造成了严重影响。那么&#xff0c;解决城市内涝的措施有哪些?需要用到哪些监测设备?针对上述问题&#xff0c;本文会为大家一一进行讲解。 解决城市内涝的措施有哪…

全志V3S嵌入式驱动开发(uboot移植)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 安装了ubuntu操作系统&#xff0c;有了开发板&#xff0c;下面就可以进行我们的开发工作了。第一步&#xff0c;我们要面临的问题就是uboot移植。一…

2098-DSD-020X 具有集成的DeviceNet通信接口

描述:2098-DSD-020X-DN是艾伦-布拉德利Ultra 3000运动控制系列的一部分。该产品是一种数字伺服驱动器&#xff0c;可在120VAC / 240 VAC、单相、50-60 Hz的输入电源电压和18安培的输入电流下运行。该伺服驱动器提供120 / 240 VAC的输出电压、3相、0-400 Hz的可编程频率范围、10…

APACHE-ATLAS-2.1.0简介(二)

APACHE-ATLAS-2.1.0简介(一) 什么是元数据&#xff1f; 元数据(METADATA)&#xff0c;用一句话定义就是&#xff1a;描述数据的数据。元数据打通了数据源、数据仓库、数据应用之间的壁垒&#xff0c;记录了数据从产生到消费的全过程。 ATLAS的问题列表 APACHE-ATLAS-STACKO…

【CANN训练营0基础赢满分秘籍】应用开发深入讲解→模型推理

1 模型离线推理 各步要解析如下: Host&Device内存管理与数据传输: Host&Device上的内存申请与释放&#xff0c;内存间的相互拷贝;模型加载:将离线的om文件加载到Device上;在样例的资源初始化模块中进行。模型输入输出准备∶根据禹线om的输入输出&#xff0c;在Device…

【记者团】社团管理手册

志愿时长&#x1f381;&#xff1a;团内有时会有志愿服务等活动&#xff0c;志愿时长可以找自己班长或班上负责人统计&#xff0c;记者团孙老师会和团委老师对接&#xff0c;团委会记录志愿时长。 志愿时长用于校级奖学金、班级奖学金、校评优评先、青马班面试(青马对入党有帮助…

大数据应用——Hive操作示例

启动Hive完成如下任务: &#xff08;1&#xff09;新建member表&#xff08;2&#xff09;将本地文件“/home/hadoop/member.txt”导入 member表中 (3&#xff09;查询member表中所有记录 &#xff08;4&#xff09;查询member表中男同学&#xff08;性别值为1&#xff09;数…