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

Node.js报错Cannot set headers 重复设置响应头问题排查

Cannot set headers after they are sent to the client 报错定位

你的代码存在3处直接/间接导致该报错的问题,按影响优先级排序:

1. 链式重复调用响应方法

车辆占用校验分支存在明显语法错误:

// 错误写法
return response.send(403).send({ status: 403, message: `vehicle with id ${vehicleId} is not available for rental` })

这里连续调用了两次send():第一次send(403)已经将响应发送到客户端,第二次链式调用send()会尝试再次修改响应头,直接触发报错。设置HTTP状态码应该用status()方法,而非send()。

2. 异步调用模式混用,await完全失效

node-postgres的pool.query方法支持两种互不兼容的异步模式:

  • 传入回调函数时:方法不返回Promise,外层的await不会等待SQL执行完成,代码会直接往下运行
  • 不传回调函数时:方法返回Promise,可通过await接收执行结果
    你给所有SQL查询都同时加了await和回调函数,实际执行逻辑完全偏离预期:
  • 4条SQL语句会几乎同时发起执行,不会按你写的顺序等待上一条执行完再跑下一条
  • 只要任意一个前置校验分支触发了响应发送,后续SQL执行完成后,其回调里的response.send会再次尝试发送响应,直接触发报错
  • 插入行程的SQL执行时,driver、vehicle两个变量大概率还是undefined,会写入脏数据

3. 回调内抛出的异常无法被捕获

你在所有SQL回调里写的throw语句,无法被外层async函数捕获,会直接触发进程未捕获异常,不会按预期返回错误响应。

修正后参考实现

统一用Promise+async/await模式,去掉回调,加上事务保证数据一致性:

async function createTrip(request, response) {
    const { vehicleId, driverId, startedAt, expectedReturn } = request.body
    let driver, vehicle
    const poolClient = await pool.connect()
    try {
        await poolClient.query('BEGIN')
        // 车辆校验
        const vehicleRes = await poolClient.query(
            'SELECT * FROM Vehicle WHERE id = $1',
            [vehicleId]
        )
        if (vehicleRes.rowCount === 0) {
            await poolClient.query('ROLLBACK')
            return response.status(404).send({ status: 404, message: `vehicle with id ${vehicleId} does not exist.` })
        }
        if (vehicleRes.rows[0].in_use) {
            await poolClient.query('ROLLBACK')
            return response.status(403).send({ status: 403, message: `vehicle with id ${vehicleId} is not available for rental` })
        }
        vehicle = vehicleRes.rows[0]

        // 司机校验
        const driverRes = await poolClient.query(
            'SELECT * FROM Driver WHERE id = $1',
            [driverId]
        )
        if (driverRes.rowCount === 0) {
            await poolClient.query('ROLLBACK')
            return response.status(404).send({ status: 404, message: `driver with id ${driverId} does not exist` })
        }
        driver = driverRes.rows[0]

        // 创建行程
        const tripRes = await poolClient.query(
            'INSERT INTO Trip (status,started_at, expected_return, driver, vehicle) VALUES ($1,$2,$3,$4,$5) RETURNING *',
            ['active', startedAt, expectedReturn, driverId, vehicleId]
        )

        // 更新车辆状态
        await poolClient.query(
            'UPDATE Vehicle SET in_use = TRUE WHERE id = $1',
            [vehicleId]
        )

        await poolClient.query('COMMIT')
        return response.status(200).send({ 
            id: tripRes.rows[0].id, 
            status: tripRes.rows[0].status, 
            startedAt, 
            expectedReturn, 
            driver, 
            vehicle 
        })
    } catch (error) {
        await poolClient.query('ROLLBACK')
        return response.status(500).send({ status: 500, message: `createTrip failed: ${error.message}` })
    } finally {
        poolClient.release()
    }
}

内容的提问来源于stack exchange,提问作者Eli Wennberg Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 15:57:27