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
问题原因及修复方案
核心原因
fetchUserDB的catch块捕获错误后未重新抛出,导致Promise状态变为resolved而非rejected,路由的then分支会被执行。此时data为undefined,执行data[0]会抛出TypeError,Express会自动返回一个HTML格式的错误响应,后续再调用res.json()就会触发重复发送响应的错误。client.connect()和client.end()未使用await,可能导致连接未就绪就执行查询,或连接未正确关闭引发异常。- 直接拼接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
相关产品推荐
相关产品推荐

