0

0

c# 幂等性在c#接口设计中的实现

幻夢星雲

幻夢星雲

发布时间:2026-01-08 08:52:02

|

604人浏览过

|

来源于php中文网

原创

幂等性指多次执行同一请求对系统状态的改变效果等价于执行一次;C# Web API 必须考虑它,因 POST 等非幂等操作(如创建订单)易因重试或连点导致重复提交,仅前端防抖不足,需后端通过 Idempotency-Key、分布式缓存与数据库约束协同保障。

c# 幂等性在c#接口设计中的实现

什么是幂等性,为什么 C# Web API 必须考虑它

幂等性不是“调用一次和调用十次结果一样”,而是“多次执行同一请求,对系统状态的改变效果等价于执行一次”。在 C# Web API 中,如果一个 POST /orders 接口没做幂等控制,用户连点两次提交按钮,就可能生成两条重复订单——这不是前端防抖能解决的,后端必须兜底。

HTTP 方法本身有语义约定:GETPUTDELETE 天然应是幂等的;但 POST 不是。而现实中大量业务操作(创建订单、发起支付、扣减库存)都用 POST,所以得自己实现幂等逻辑。

用 Idempotency-Key + 缓存实现最简可靠方案

主流做法是客户端在请求头带上唯一标识 Idempotency-Key,服务端用它作为键,缓存该请求的响应结果或执行状态。关键不在“怎么存”,而在“什么时候存、存什么、存多久”。

  • Idempotency-Key 应由客户端生成(如 UUID v4),服务端只校验格式和长度,不生成
  • 缓存建议用 IDistributedCache(如 Redis),避免单机内存缓存导致负载均衡下失效
  • 缓存值推荐存 (status code, response body, timestamp) 三元组,而非仅“已执行”,否则无法正确返回原始响应
  • 过期时间要大于业务最大处理耗时(比如支付回调最长 10s,那就设 30s),但不宜过长(防止 key 泄露占用资源)
public class IdempotencyFilter : ActionFilterAttribute
{
    private readonly IDistributedCache _cache;
public IdempotencyFilter(IDistributedCache cache) => _cache = cache;

public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
    var key = context.HttpContext.Request.Headers["Idempotency-Key"].FirstOrDefault();
    if (string.IsNullOrWhiteSpace(key))
    {
        context.Result = new BadRequestObjectResult("Missing Idempotency-Key header");
        return;
    }

    var cacheKey = $"idempotent:{key}";
    var cached = await _cache.GetAsync(cacheKey, context.HttpContext.RequestAborted);

    if (cached != null)
    {
        var result = JsonSerializer.Deserialize(cached);
        context.Result = new ObjectResult(result.Body) { StatusCode = result.StatusCode };
        return;
    }

    var exec = await next();
    if (exec.Exception == null && context.Result is ObjectResult obj && obj.Value != null)
    {
        var response = new IdempotentResponse
        {
            StatusCode = obj.StatusCode ?? 200,
            Body = obj.Value
        };
        await _cache.SetAsync(cacheKey, JsonSerializer.SerializeToUtf8Bytes(response),
            new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30) });
    }
}

}

数据库层面的幂等保障不能只靠 UNIQUE 约束

光靠给订单表加 UNIQUE (user_id, client_order_id) 是不够的:约束触发时抛的是 SqlException,直接 500,且无法区分是“重复提交”还是“其他冲突”。必须把约束失败转化为可控的业务响应。

BJXSHOP网上开店专家
BJXSHOP网上开店专家

BJXShop网上购物系统是一个高效、稳定、安全的电子商店销售平台,经过近三年市场的考验,在中国网购系统中属领先水平;完善的订单管理、销售统计系统;网站模版可DIY、亦可导入导出;会员、商品种类和价格均实现无限等级;管理员权限可细分;整合了多种在线支付接口;强有力搜索引擎支持... 程序更新:此版本是伴江行官方商业版程序,已经终止销售,现于免费给大家使用。比其以前的免费版功能增加了:1,整合了论坛

下载
  • TRY...CATCH 捕获 SQL Server 的错误号 2627(唯一键冲突)或 2601(主键重复)
  • PostgreSQL 对应捕获 23505(unique_violation)
  • EF Core 中更推荐用 ExecuteSqlRaw 执行带 ON CONFLICT DO NOTHING(PG)或 MERGE(SQL Server)的语句,避免异常路径
  • 注意:数据库幂等只保证“写入不重复”,不保证“响应一致”——仍需配合缓存返回原始成功响应

别忽略分布式锁和并发边界

当幂等校验 + 写入需要原子性(比如先查缓存、再写 DB、再存缓存),单纯用 IDistributedCacheGetAsync/SetAsync 无法防止竞态。两个相同请求几乎同时到达,都发现缓存无值,都会去执行业务逻辑。

这时候得加一层轻量级分布式锁:

  • Redis SET key value NX EX 10(NX=不存在才设,EX=10秒过期)抢锁
  • 抢到锁的请求走完整流程,释放锁前把结果写入缓存;没抢到的请求等待后重查缓存
  • 锁超时必须小于业务最大耗时,否则会误释放;也不宜太短(如 1s),否则频繁重试加重压力

真正难的不是代码怎么写,而是幂等键的设计粒度:是按用户+操作类型+时间戳?还是绑定具体业务实体 ID?一旦选错,要么锁住不该锁的请求,要么放行本该拦截的重复请求。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

676

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

320

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

346

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1093

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

357

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

674

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

571

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

412

2024.04.29

java学习网站推荐汇总
java学习网站推荐汇总

本专题整合了java学习网站相关内容,阅读专题下面的文章了解更多详细内容。

33

2026.01.08

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

Redis+MySQL数据库面试教程
Redis+MySQL数据库面试教程

共72课时 | 6.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号