webhook 路由必须禁用所有干扰中间件、手动读原始 body、签名验证后立即返回 200,耗时逻辑异步入队;需校验 content-type、空 body、限流防攻击,签名用 hmac.equal,时间戳容错±5分钟,密钥从环境变量读。

不能直接用 app.Post() 注册 Webhook 路由后就调用业务逻辑——必须禁用干扰中间件、手动读原始 body、签名验证后立即返回 200,耗时处理扔进队列。
Webhook 路由必须禁用 CSRF 和 JSON 自动解析中间件
Iris 默认不带 CSRF 中间件,但如果你手动加了 iris.Recovery 以外的请求预处理(比如自定义 body 解析、token 校验),它们会提前 consume ctx.Request().Body,导致后续读不到原始 payload。
- Webhook 请求来自第三方服务(如 Stripe、钉钉、GitHub),没有 cookie/session,也不带任何 Iris 期望的上下文
- 务必在注册路由时显式跳过所有可能干扰的中间件:
app.Party("/webhook").Disable(iris.NoLayout, iris.NoValidation) - 绝对不要用
ctx.PostValue("xxx")或ctx.ReadJSON()——这些依赖框架的自动解析,会破坏签名验证所需的原始字节流
必须用 ctx.Request().Body 读原始数据并校验签名
微信回调发 text/xml,Stripe 发 application/json 带 Stripe-Signature 头,钉钉发 application/json 但要求用 timestamp + sign 防重放——它们都要求你拿到未解码的原始字节再计算哈希。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 先调
body, _ := io.ReadAll(ctx.Request().Body),再用ctx.Request().Body = io.NopCloser(bytes.NewBuffer(body))恢复可重读(否则后续中间件或日志会失败) - 签名验证必须用
hmac.Equal()或crypto/subtle.ConstantTimeCompare(),禁止用==——防时序攻击 - 时间戳校验要宽松:允许 ±5 分钟偏差,用
time.Now().Unix() - timestamp - 密钥必须从环境变量读:
os.Getenv("WEBHOOK_SECRET"),绝不可硬编码
验证通过后必须立刻返回 200,异步处理交给任务队列
第三方服务对 Webhook 响应超时极其敏感(常见 3–10 秒),一旦超时就会重试,造成重复消费。Iris 的 handler 是同步阻塞的,不能在里面连数据库、发邮件、调外部 API。
- 验证成功后第一件事:
ctx.StatusCode(200)+ctx.WriteString("OK"),然后return - 把
body和事件类型(如"payment.succeeded")序列化后推入 Redis 队列或 SQLite WAL 表 - 用独立的 worker 进程监听队列,执行实际业务逻辑(更新订单状态、发通知等)
- 如果 worker 失败,记录错误并设置重试次数上限(比如最多 3 次),避免死循环
最易被忽略的是:你以为 signature 验证完就安全了,其实没校验 Content-Type 头是否匹配预期、没检查 body 是否为空、也没做 io.LimitReader 防止恶意超大 payload 打满内存——这些都得在读 Body 前补上。










