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

Express+node-postgres出现ERR_HTTP_HEADERS_SENT错误求助

问题:Express + node-postgres 返回响应时触发 ERR_HTTP_HEADERS_SENT 错误

现象

调用fetchUserDB后,无论查询成功或失败,响应头都会被自动写入,查看res._headers发现finished已设为true,后续执行res.json()时触发ERR_HTTP_HEADERS_SENT错误。此时响应头内容如下:

access-control-allow-origin:    '*'
content-length:    155
content-security-policy:    'default-src 'none''
content-type:    'text/html; charset=utf-8'
x-content-type-options:    'nosniff'
x-powered-by:    'Express'

相关代码

路由处理代码

user.get("/detail",async (req,res)=>{
    const {uid} = req.authToken

    fetchUserDB(uid).then(data=>{
        return res.status(200).json({
            data:data[0],
        })
    }).catch(err=>{
        switch (err.name) {
            case 'NoUserWithUid':
                break;
            default:
                break;
        }
        return res.status(404).json({
            error:"user not found"
        })
    })
})

fetchUserDB 实现代码

export const fetchUserDB = async (uid)=>{
    client.connect()
    try {
        const query = `SELECT * FROM api.users WHERE firebase_uid = '${uid}'`
        const res = await client.query(query)

        if (res.rows.length == 0) {
            throw new NoUserWithUid(uid)
        }
        return res.rows
    } catch(err){
        console.log(err);
        debugger
    }finally {
        client.end()
    }
}

报错信息

Uncaught Error Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

问题原因及修复方案

核心原因

  1. fetchUserDB的catch块捕获错误后未重新抛出,导致Promise状态变为resolved而非rejected,路由的then分支会被执行。此时data为undefined,执行data[0]会抛出TypeError,Express会自动返回一个HTML格式的错误响应,后续再调用res.json()就会触发重复发送响应的错误。
  2. client.connect()和client.end()未使用await,可能导致连接未就绪就执行查询,或连接未正确关闭引发异常。
  3. 直接拼接SQL字符串存在SQL注入风险,同时可能因uid含特殊字符导致查询失败。

修复步骤

1. 修正fetchUserDB的错误抛出逻辑

在catch块中重新抛出错误,让上层路由的catch分支能正确捕获:

export const fetchUserDB = async (uid)=>{
    await client.connect() // 添加await确保连接就绪
    try {
        // 使用参数化查询避免SQL注入
        const query = 'SELECT * FROM api.users WHERE firebase_uid = $1'
        const result = await client.query(query, [uid]) // 重命名变量避免混淆

        if (result.rows.length === 0) {
            throw new NoUserWithUid(uid)
        }
        return result.rows
    } catch(err){
        console.log(err);
        throw err; // 重新抛出错误,让路由层处理
    }finally {
        await client.end() // 添加await确保连接正确关闭
    }
}

2. 优化路由的错误处理逻辑

改用try/catch替代then/catch,代码更清晰且避免回调嵌套:

user.get("/detail", async (req, res) => {
    const { uid } = req.authToken;
    try {
        const data = await fetchUserDB(uid);
        return res.status(200).json({ data: data[0] });
    } catch (err) {
        if (err.name === 'NoUserWithUid') {
            return res.status(404).json({ error: "user not found" });
        }
        // 处理其他未知错误
        return res.status(500).json({ error: "服务器内部错误" });
    }
});

3. 额外建议

  • 如果使用的是连接池而非单个client,建议使用pool.connect()获取连接并在finally中释放,而非直接调用client.end()。
  • 确保NoUserWithUid自定义错误类正确定义,name属性设置为'NoUserWithUid'。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 20:25:11