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

请求API时触发ERR_HTTP_HEADERS_SENT错误,求排查解决

解决 [ERR_HTTP_HEADERS_SENT] 错误:重复发送响应的问题

这个错误你肯定眼熟——本质就是同一个HTTP请求里,你尝试多次发送响应(比如多次调用res.json()),而HTTP协议要求一个请求只能对应一个响应。咱们来拆解你的代码问题,一步步修复:

问题根源

  1. 不必要的forEach循环:你查询的是token='${token}',正常情况下token应该是唯一的,查询结果最多一条记录,但你用rows.forEach()遍历,只要有记录就会执行循环体里的逻辑。如果真的出现多条重复token(虽然不该发生),就会多次调用res.json(),直接触发错误。
  2. 异步操作的响应冲突:pool.query()是异步Promise,你在forEach里调用它,即使你用了return res.json(),但外层的代码可能还会继续执行其他res.json()分支,比如else { res.json(...) },导致重复发送。
  3. SQL注入风险:直接把用户传入的token和guildid拼进SQL语句,这是高危操作,别小看这个,很容易被攻击。

修复后的代码

router.get('/prefix', (req, res) => {
  // 先做参数校验,用res.status返回错误,而不是throw(避免未捕获异常)
  if (!req.query.token) {
    return res.status(400).json({ code: 400, message: 'NoTokenProvided' });
  }
  if (!req.query.guildid) {
    return res.status(400).json({ code: 400, message: 'NoGuildIDProvided' });
  }

  const token = req.query.token;
  const guildid = req.query.guildid;

  // 用参数化查询防SQL注入,PostgreSQL用$1占位符
  const authSql = 'SELECT token FROM auth WHERE token = $1';
  db.all(authSql, [token], (err, rows) => {
    if (err) {
      console.error('数据库查询错误:', err);
      return res.status(500).json({ code: 500, message: 'ErrorWithDatabase' });
    }

    // 直接判断是否存在有效token,不需要循环
    if (!rows[0]) {
      return res.status(401).json({ code: 401, message: 'That is not a valid token.' });
    }

    // 验证token通过后,查询前缀,同样用参数化查询
    pool.query('SELECT prefix FROM guild WHERE id = $1', [guildid])
      .then(response => {
        if (!response.rows[0]) {
          return res.json({ code: 200, message: 'User has not setup a custom prefix.' });
        } else {
          return res.json({ code: 200, prefix: response.rows[0].prefix });
        }
      })
      .catch(e => {
        console.error('前缀查询错误:', e);
        return res.status(500).json({ code: 500, message: 'Failed to fetch prefix' });
      });
  });
});

关键修改点说明

  1. 去掉forEach循环:因为token是唯一标识,查询结果最多一条,直接用rows[0]判断即可,避免循环导致的多次响应。
  2. 每个响应都加return:确保发送响应后立即终止后续代码执行,不会触发其他分支的res.json()。
  3. 替换为参数化查询:把用户输入作为参数传入,而不是直接拼进SQL,彻底杜绝SQL注入风险。
  4. 统一错误处理:用res.status()设置正确的HTTP状态码,同时用return终止后续逻辑,避免错误后继续执行代码。
  5. 避免直接throw:在Express路由里直接throw可能导致未捕获异常,最好用响应返回错误,或者配合全局错误处理中间件。

这样修改后,每个请求只会发送一次响应,ERR_HTTP_HEADERS_SENT错误就会消失,同时代码安全性和可读性也提升了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:36:21