概览Server Actions 在弱网与并发场景可能出现重复提交。通过幂等键与请求指纹可消除重复执行,并以退避策略改善重试体验。后端幂等键app/actions.ts'use server'
const seen = new Map<string, number>()
export async function pay(data: FormData) {
const key = String(data.get('idempotency_key') || '')
if (!key) return { ok: false, message: '缺少幂等键' }
if (seen.has(key)) return { ok: true, message: '已处理' }
await new Promise((r) => setTimeout(r, 500))
seen.set(key, Date.now())
return { ok: true, message: '支付成功' }
}
客户端提交'use client'
import { useActionState } from 'react'
import { pay } from '../actions'
export default function PayForm() {
const [state, action, pending] = useActionState(async (_prev, formData) => {
'use server'
return pay(formData)
}, { ok: false, message: '' })
function genKey() {
return crypto.randomUUID()
}
return (
<form action={action} aria-describedby="status">
<input name="amount" type="number" required />
<input name="idempotency_key" type="hidden" value={genKey()} />
<button disabled={pending}>{pending ? '处理中…' : '支付'}</button>
<p id="status" role="status">{state.message || '请输入金额'}</p>
</form>
)
}
去重与退避// 对可能的重复提交进行退避重试
async function retry<T>(fn: () => Promise<T>, tries = 3) {
let i = 0
while (i < tries) {
try { return await fn() } catch (e) { await new Promise(r => setTimeout(r, 300 * (i + 1))) }
i++
}
throw new Error('重试失败')
}
治理要点幂等键在后端存储(KV/DB),确保跨实例一致;设置过期与清理。将去重与退避封装在 Server Actions 与客户端提交路径中,避免状态撕裂。在成功后触发标签/路径失效,保持数据一致性。验证与指标Next.js:15.0+;Node.js:20.x重复提交被吞并;用户反馈清晰、无重复扣款/记录

发表评论 取消回复