Iris的全局中间件无法自动处理幂等性,因其仅作用于HTTP handler goroutine,而幂等性是前置准入控制,须在业务执行前完成校验;Iris无内置支持,需自研Redis+Token中间件,严格校验头、设过期key、原子删除,并规避空token放行、无降级、时钟漂移等典型坑。

为什么Iris的全局中间件无法自动处理幂等性
因为Iris的中间件只作用于HTTP handler goroutine,而幂等性校验必须在业务逻辑执行前完成——它不是错误恢复机制,而是前置准入控制。你不能指望app.Use()里加个recover()来解决重复提交,那根本不是同一个问题域。
用Redis+Token实现最简幂等中间件
核心是拦截请求、校验token唯一性、原子删除。注意:Iris本身不内置幂等支持,必须自己写中间件,且要避开几个典型坑:
-
Idempotent-Token头字段名必须统一,建议全小写或按RFC规范用X-Idempotency-Key - Redis key必须带过期时间,否则脏数据堆积;推荐
"idempotent:" + token格式,避免key冲突 - 不能用
c.GetHeader("Idempotent-Token")后直接判空就放行——空token也要拒绝,防止绕过 - 校验失败时返回
409 Conflict比400 Bad Request更语义准确
示例中间件片段:
func IdempotentMiddleware(redisClient *redis.Client) iris.Handler {
return func(c iris.Context) {
token := c.GetHeader("X-Idempotency-Key")
if token == "" {
c.StatusCode(400)
c.WriteString("missing X-Idempotency-Key header")
return
}
key := "idempotent:" + token
exists, err := redisClient.Exists(c.Context(), key).Result()
if err != nil {
c.StatusCode(500)
c.WriteString("idempotency check failed")
return
}
if exists == 0 {
c.StatusCode(409)
c.WriteString("duplicate request detected")
return
}
// 原子删除,确保只执行一次
_, _ = redisClient.Del(c.Context(), key).Result()
c.Next()
}
}
Controller里调用幂等中间件的正确姿势
别在每个Post方法开头手动校验,中间件必须注册在路由级,且注意顺序:
- 幂等中间件必须放在认证中间件之后、业务Handler之前,否则未认证用户也能刷token
- 对GET/HEAD这类安全方法无需启用,避免误伤缓存和CDN行为
- 如果接口允许重试(如支付回调),需配合
idempotent:retry标记做二次放行,不能一刀切 - 不要在中间件里写DB日志——高并发下会拖慢整个链路;记录到异步队列或本地文件更稳妥
容易被忽略的边界点
真正的难点不在“怎么删token”,而在“删完之后怎么保证业务不翻车”:
- token校验通过后,业务逻辑仍可能panic或超时,此时token已被删,但操作未完成——需要配套补偿任务或状态机兜底
- 分布式环境下Redis单点故障会导致所有请求被拒,必须有降级开关(比如配置项
idempotent.enabled=false) - 前端生成token若用
Date.now()拼接,时钟漂移会导致重复;推荐用UUIDv4或服务端签发 - 如果业务本身含异步协程(如
go c.sendNotification()),token校验只保住了主流程,协程里的重复动作还得靠task_id+幂等表二次控制











