DEBUG.WIKI
晚安云;香港 CN2 ¥15/月
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →

Hono 4.12.8 调用 parseBody 后,text() 报 then is not a function

Hono 4.12.8 对非表单请求执行 parseBody 后,普通对象可能进入 Promise 缓存,使下游 text/json 报错。采用含正式修复的版本,并限定表单中间件的解析范围。

中间件先执行 c.req.parseBody(),下游再调用 c.req.text(),Hono 4.12.8 可能抛出:

TypeError: bodyCache[anyCachedKey].then is not a function

一个容易误判的条件是:请求根本不是表单。JSON 或没有 Content-Type 的请求,parseBody() 可以先返回空对象,随后读原文才出错。这与原生 Response 被 text/json 消费两次不是同一条处理路径。

空表单结果进入了只应放 Promise 的缓存

4.12.8 会把 parseBody() 的已解析对象写进 bodyCache.parsedBody。对于非表单请求,这个对象可能成为缓存里的第一项。随后 text/json 的缓存逻辑把它当作 Promise 调用 .then(),于是报错。

真实表单会先经过 formData(),缓存中的第一项通常已经是 Promise,所以同一套中间件可能在表单请求上正常、在 JSON webhook 上失败。给空对象加一个 .then,或手工把字符串写入内部 bodyCache,都不适合处理这个问题,会破坏其他正文读取方法的约定。

正式 4.12.9 移除了多余的 parsedBody 缓存;正式 4.13.0 也保留了这项修正。处理旧项目时,先核对实际解析到的 Hono 版本,再沿项目的依赖约束采用包含修复的受维护兼容版本。不要只修改 package.json 的范围,留下仍锁在 4.12.8 的构建产物。

npm ls hono

表单中间件只处理表单

暂时不能更新依赖时,可以让现有表单中间件先判断 Content-Type,避免对 JSON 请求无条件执行 parseBody。下面是合入已有 Hono 应用的片段,app 指原来的实例:

app.use('*', async (c, next) => {
  const type = (c.req.header('content-type') ?? '')
    .split(';', 1)[0]
    .trim()
    .toLowerCase();

  if (
    type === 'multipart/form-data' ||
    type === 'application/x-www-form-urlencoded'
  ) {
    await c.req.parseBody();
  }

  await next();
});

这个片段只展示解析分支,不会自动把表单结果写入业务上下文。已有业务若依赖这个结果,应保留原来的赋值与类型声明。也不要把 Content-Type 一律改成表单来迁就中间件,或把不认识的正文猜成 JSON。

对于需要原始正文验签的 webhook,要保留供应商要求的原始字节、编码和验证顺序。不要先解析 JSON 再 stringify 成另一份字符串验签;正文顺序、空白等变化都可能影响签名。这里修复的是读取入口与缓存问题,并没有提供或替代签名验证。

验收覆盖原来会失败的 JSON 请求、没有 Content-Type 的请求,以及两种真实表单。确认下游能读取预期内容,表单字段仍完整,非法格式仍按原规则被拒绝。若用的是 c.req.raw 的原生读取方法,还要单独检查正文是否已消耗;Hono 的缓存修复不会让原生流可以任意重复读取。遇到这种情况,可继续核对正文只消费一次的处理方式。