You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js Express刷新后POST请求ERR_HTTP_HEADERS_SENT排查

问题根因

两个问题均由监听器生命周期管理缺失、全局变量污染导致,具体拆解:

1. ERR_HTTP_HEADERS_SENT报错触发逻辑

  • 代码中watcher未通过const/let做块级声明,直接赋值会挂载到全局作用域。每次/recieve收到请求时,全局watcher引用会被新值覆盖,但上一个请求绑定的fs.watch监听器不会自动销毁。
  • 现有逻辑仅在文件变更触发回调时才执行watcher.close(),在文件未变更的等待周期内,会同时存在多个未关闭的监听器,每个监听器都持有对应请求的res对象引用。
  • 当msg.json发生变更,所有堆积的未关闭监听器会同时触发、执行res.send():除了极少数仍处于待响应状态的res,其余res要么已经完成过响应,要么因客户端断开连接已失效,给已结束的请求重复发响应就会抛出ERR_HTTP_HEADERS_SENT错误。
  • 额外触发场景:fs.watch本身存在平台兼容问题,同一次文件保存操作可能触发2次及以上change事件,即使是同一个watcher实例,也可能在watcher.close()执行完成前多次进入回调,导致同一个res多次调用send方法。

2. 页面刷新后POST请求无响应触发逻辑

  • 该实现本质是简易长轮询:请求到达服务端后不立即返回,而是挂起等待文件变更事件才响应。但代码完全未处理客户端主动断开请求的场景:用户刷新页面时,浏览器会直接取消所有pending状态的HTTP请求,但服务端对应这些请求绑定的fs.watch监听器不会被自动清理,一直驻留在事件循环中。
  • 这些残留监听器持有已经失效的res引用,等到下一次msg.json变更时,残留监听器会优先触发,执行全局watcher的close()方法——此时全局watcher已经被刷新后新进入的POST请求覆盖,被关闭的其实是新请求刚绑定的监听器。残留监听器读完文件后,尝试给早已失效的res发响应必然失败,而新请求绑定的监听器已被提前关闭,永远等不到下一次文件变更事件,就会一直挂起无响应。
修正要点
  • 将watcher改为接口作用域内的局部变量,禁止全局存储和请求绑定的实例
  • 每个请求到达后,监听req的close事件,一旦客户端断开连接,立刻关闭当前请求对应的watcher、移除相关监听器
  • 给watcher回调加一次性触发锁,保证一次文件变更仅给对应请求发送一次响应
  • 禁止多请求共享同一个watcher实例,保证每个请求的watcher与res对象一一绑定、互不干扰

内容的提问来源于stack exchange,提问作者Dominic Johnston Whiteley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:33:44