You need to enable JavaScript to run this app. Express 4项目中如何为特定路由配置独立的Body解析中间件
优惠活动
大模型
产品
解决方案
定价
更多

Express 4项目中如何为特定路由配置独立的Body解析中间件

问题根因

Express 的中间件严格按照注册顺序匹配路径执行,HTTP 请求体本质是只可单次读取的 Stream 流,一旦被某个中间件消费完毕,后续所有中间件都无法再读取到原始流内容。
你当前的代码里,全局挂载的express.json()位于所有路由注册之前,请求到达 webhooks 路由时:

  1. 首先被全局 JSON 解析器拦截,完整读取请求流并解析为 JSON 对象挂在req.body上
  2. 等请求走到 webhooks 路由上挂载的express.raw()时,请求流已经被读空,根本拿不到原始 Buffer 内容,自然无法完成 HMAC 校验。

分步落地的解决方案

1. 调整全局 JSON 解析器的拦截范围

修改server.js中全局express.json()的配置,增加跳过逻辑,让它不对 Shopify Webhook 路径的请求做流解析,保留完整原始请求流给后续的 webhooks 路由使用:

// 替换原来的 app.use(express.json()) 行
app.use(express.json({
  // 匹配到webhooks路径时直接跳过,不消费请求流
  skip: (req) => req.originalUrl.startsWith('/shopify/app/webhooks')
}))

2. 修正 webhooks 控制器的解析与校验逻辑

调整webhooks.controller.js的代码,把 raw 解析统一挂载到 webhooks 路由入口,同时修正 HMAC 计算的细节问题:

const express = require('express');
const router = express.Router();
const crypto = require('crypto');

const SHOPIFY_API_SECRET_KEY = process.env.SHOPIFY_API_SECRET_KEY;

// 所有webhooks路由统一用raw格式解析,拿到原始Buffer
router.use(express.raw({ type: 'application/json' }));

router.post('/app/uninstalled', async (req, res) => {
  const hmac = req.get('X-Shopify-Hmac-Sha256');

  // 直接用Buffer计算HMAC,不要手动做字符串转换/格式化
  const hash = crypto
    .createHmac('sha256', SHOPIFY_API_SECRET_KEY)
    .update(req.body)
    .digest('base64');

  if (hash === hmac) {
    console.log('Webhook来源校验通过');
    return res.sendStatus(200);
  }

  console.log('Webhook来源校验失败,非Shopify发起的请求');
  res.sendStatus(403);
});

module.exports = router;

关键注意事项
  • 计算 HMAC 时必须直接使用express.raw()返回的 Buffer 对象,不要做toString()、JSON 序列化/反序列化、首尾 trim 等操作,任何格式改动都会导致计算出的哈希和 Shopify 端的值不匹配。
  • 确保 skip 判断的路径和实际挂载 webhooks 路由的路径完全一致,不要误将其他需要 JSON 解析的业务接口跳过,导致普通 API 拿不到解析后的请求参数。
  • Webhook 路由的链路里不要再挂载其他会读取请求流的中间件,避免再次出现流被提前消费的问题。

内容的提问来源于stack exchange,提问作者ahmetkca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.08 16:24:04