Node.js报Cannot set headers after they are sent错误排查
报错原因
ERR_HTTP_HEADERS_SENT 错误的触发逻辑非常明确:单次HTTP请求仅允许返回一次响应,当代码已经向客户端发送过响应后,再次尝试设置响应头、发送响应内容就会抛出该错误。
你的代码问题出在参数校验分支:当传入非法日期命中if判断时,代码执行了res.status(400).send("Start and/or End date is invalid")向客户端返回400响应,但没有终止后续逻辑执行,程序会继续往下运行数据库查询代码,之后执行res.send(vacationDays)尝试第二次返回响应,直接触发重复发送响应的错误。
另外代码还存在一个逻辑漏洞:校验空值时第二个判断条件写的是req.body.end == null,不是之前通过getBodyDates解析得到的dates.end,如果参数解析方法对传入值做过格式化、空值转换处理,这里会出现校验逻辑和实际使用参数不一致的问题。
修复方法
只需要做两处修改即可解决问题:
- 在参数非法返回400响应的分支末尾添加
return语句,直接终止函数后续逻辑执行,避免走到正常返回的代码分支 - 将空值判断中的
req.body.end替换为dates.end,统一使用解析后的参数做校验,避免逻辑不一致
修复后的完整代码如下:
async checkVacation(req: Request, res: Response) { try { let currentUser = req.user as User let dates = this.getBodyDates(req.body) if (dates.start == null || dates.end == null || this.beforeTodayCheck(dates.start.toString()) == true) { res.status(400).send("Start and/or End date is invalid") // 终止后续执行,防止重复发送响应 return } let vacationDays = await this.getDBVacationDay(dates.start, dates.end, currentUser.Id) res.send(vacationDays) } catch (err) { console.error(err) res.status(400).send(err) } }
编码建议
- 所有调用
res.send/res.json/res.status().send()等发送响应的代码分支,如果不是函数的最后一行执行逻辑,都建议在响应发送后加return,从编码习惯上彻底避免重复发响应的问题 - 修复后catch块的潜在风险也会同步消除:加return后参数校验分支会直接退出函数,不会继续执行后续数据库查询逻辑,也就不会出现在已经发送响应的情况下又进入catch块重复发响应的问题
内容的提问来源于stack exchange,提问作者user18460597
相关产品推荐
相关产品推荐

