后端需用redis缓存+idempotency-key实现幂等性:视图开头校验key是否存在,存在则返回409并原样返回缓存结果,不存在则set带过期时间的key再执行业务;必须用cache.get()、禁用locmem、避免session存储、不回滚缓存。

提交表单时后端怎么拦截重复请求
直接在视图里用缓存做幂等性检查,不是靠前端防抖或禁用按钮——那只是掩耳盗铃。用户绕过 JS、用 curl 重放、甚至浏览器刷新 F5,都会触发重复提交。
核心思路:客户端在请求头或表单里带一个唯一标识(比如 idempotency-key),后端用它查缓存;存在就拒绝,不存在就写入并继续处理。
-
idempotency-key建议由前端生成(UUID v4 最稳妥),不要用时间戳或自增 ID - 缓存 key 格式建议为
f"idemp:{request.headers.get('Idempotency-Key', '')}",避免 key 冲突 - 缓存过期时间设为业务合理窗口,比如支付类操作设 10 分钟,普通表单设 2 分钟
- 必须在事务开始前完成幂等性校验,否则可能缓存写入了但 DB 操作失败,下次重试又因缓存被拒
Django 视图中怎么加幂等校验逻辑
别写装饰器套一层就完事——容易漏掉异常路径、忽略缓存写入失败、或者把校验放在 form.is_valid() 后面,导致表单已校验成功但幂等失败,状态不一致。
推荐在视图最开头手动控制流程:
def order_create(request):
key = request.headers.get("Idempotency-Key")
if not key:
return JsonResponse({"error": "Missing Idempotency-Key"}, status=400)
<pre class="brush:python;toolbar:false;">cache_key = f"idemp:{key}"
if cache.get(cache_key):
return JsonResponse({"error": "Duplicate request"}, status=409)
# 注意:这里才 set,且要带过期时间
cache.set(cache_key, "processed", timeout=600)
# 后续走正常逻辑:form 处理、DB 事务、发送消息等
# ...
-
cache.set()必须指定timeout,否则默认永不过期,缓存会越积越多 - 用
cache.get()而非cache.has_key(),后者在某些后端(如 Redis)不是原子操作,有竞态风险 - 如果用的是
django.core.cache.caches['redis'],确保配置了KEY_PREFIX避免和其他项目 key 冲突
为什么用 Redis 而不是 Django 默认的 locmem 缓存
locmem 缓存是进程级的,多 worker 或重启后就丢失,根本没法保证幂等性。线上必须用共享缓存。
- Redis 是首选:支持原子操作
SET key value EX 600 NX(即“仅当不存在时设置”,天然防竞态) - 如果硬要用数据库模拟幂等表,得加唯一索引 +
get_or_create(),但性能差、易锁表,只适合低频场景 - Django 的
cache.add()行为接近NX,但在某些缓存后端(如 memcached)不保证原子性,Redis 才真正可靠 - 别把幂等 key 存 session —— session 可能未登录、可能跨设备、也可能被清理
POST 提交后刷新页面 409 怎么友好提示用户
返回 409 Conflict 是标准做法,但用户看到白屏或报错弹窗体验很差。关键是前后端配合还原状态。
- 前端发请求时记录
idempotency-key到 localStorage,成功后清除;刷新时若发现 key 存在,直接读取上次响应结果(需后端把结果也缓存一份,key 加:result后缀) - 后端在幂等命中时,不只返回错误,而是读取
cache.get(f"{cache_key}:result")并原样返回(含 status、data) - 注意:结果缓存也要设相同 timeout,并和主 key 一起失效(可用 Redis pipeline 或 lua 脚本保证原子删除)
- 别在 409 响应里返回新数据——比如订单号,这会让前端误以为是新创建的,实际是旧的
幂等性不是加个 key 就完事,关键在缓存生命周期、原子性保障、以及失败路径下的状态一致性。最容易被忽略的是:缓存写入成功但后续 DB 报错,这时要不要回滚缓存?答案是不回滚——幂等性只要求“相同输入得到相同输出”,而不是“完全事务一致”。所以更稳妥的做法是:把幂等校验和业务执行拆成两个独立可重试步骤,中间用状态字段衔接。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











