Express 4.17 单端点获取raw body 解决Stripe webhook签名校验问题
问题根因
- 你大概率在代码其他位置全局注册了
express.json()中间件,该中间件会提前读取并消费请求流,导致后续注册的express.raw或者自定义data事件监听器无法获取到原始请求体,所以出现空值 - 你自定义的rawBody存储中间件如果位于
express.json()之后,同样会因为请求流已被消费,无法触发data事件,最终rawBody为空
解决方案
方案1:调整路由和中间件注册顺序(侵入性最低)
Express中间件和路由按注册顺序执行,只需将webhook路由放在全局JSON解析中间件之前即可,其他接口不受影响:
const stripe = require('stripe')('apikey...'); const endpointSecret = 'whsec_...'; const express = require('express'); const app = express(); // 优先注册webhook路由,单独用express.raw解析请求体 app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => { const signature = request.headers['stripe-signature']; try { const event = stripe.webhooks.constructEvent(request.body, signature, endpointSecret); // 后续处理Stripe事件逻辑 } catch (err) { console.log(`⚠️ Webhook签名校验失败`, err.message); return response.sendStatus(400); } response.sendStatus(200); }); // 再注册全局JSON解析中间件,给其他业务接口使用 app.use(express.json()); // 其他普通业务路由正常使用req.body即可 app.post('/your/other/api', (req, res) => { console.log(req.body); // 正常获取解析后的JSON数据 res.send('ok'); }); app.listen(443, () => console.log('服务运行在端口443'));
方案2:全局兼容中间件(所有路由都可获取rawBody)
如果不想调整路由顺序,可以利用express.json的内置verify配置,解析请求时同步挂载原始请求体到req对象上,替代原有全局express.json注册即可:
// 替换原有app.use(express.json())为以下代码 app.use(express.json({ verify: (req, res, buf) => { // 将原始Buffer挂载到req.rawBody,所有路由都可直接调用 req.rawBody = buf; } })); // webhook路由无需单独加解析中间件,直接使用req.rawBody校验 app.post('/webhook', (request, response) => { const signature = request.headers['stripe-signature']; try { const event = stripe.webhooks.constructEvent(request.rawBody, signature, endpointSecret); // 后续处理Stripe事件逻辑 } catch (err) { console.log(`⚠️ Webhook签名校验失败`, err.message); return response.sendStatus(400); } response.sendStatus(200); }); // 其他普通业务路由无需调整,正常使用req.body即可
内容的提问来源于stack exchange,提问作者Kermit
相关产品推荐
相关产品推荐

