Node+Express修改res.send发送非原body对象报错及疑问
问题分析与解决方案
环境:Node 16.14.2, Express 4.18.1
核心原因:重写res.send触发的递归调用陷阱
你遇到的错误本质是Express原生res.send的内部逻辑,与你重写后的方法形成了无限递归,具体细节:
- Express的
res.send在接收非字符串/Buffer的对象时,会自动调用res.json()处理,而res.json()内部又会再次调用res.send()。 - 你重写
res.send后,原生方法内部触发的res.send()已经是你重写后的版本,而非最初的原生方法,这就形成了循环调用,最终导致栈溢出(Maximum call stack size exceeded)或枚举错误。 - 发送原
body不报错,是因为原body可能已经被Express预处理过(比如已转为字符串、或响应状态已标记为发送中),不会触发二次调用逻辑。
为什么转字符串就能正常运行?
当你把对象转为JSON字符串后,originalSend接收的是字符串类型,Express的res.send会直接处理字符串响应,不会触发res.json()的调用链,自然也就避开了递归循环。
正确的重写方式:阻断递归
要彻底解决问题,需要确保原生res.send内部调用的是最初的原生方法,而非你重写后的版本,以下是两种可行方案:
方案1:临时恢复原生res.send
app.use(function (req, res, next) { const originalSend = res.send; res.send = function (body) { // 临时恢复原生send方法,避免内部递归 res.send = originalSend; // 执行自定义逻辑,修改响应体 const modifiedBody = { ...body, "USER": req.user }; // 调用原生方法发送响应 const result = originalSend.call(this, modifiedBody); // 重新挂载重写后的send方法(如果后续请求还需要) res.send = arguments.callee; return result; }; next(); });
方案2:直接用res.json处理对象
如果你的场景主要返回JSON数据,可以直接针对对象类型调用res.json,绕开res.send的内部调用链:
app.use(function (req, res, next) { const originalSend = res.send; res.send = function (body) { if (typeof body === 'object' && body !== null) { // 处理对象类型,直接调用res.json return res.json({ ...body, "USER": req.user }); } // 非对象类型,调用原生send方法 return originalSend.call(this, body); }; next(); });
总结
重写Express响应方法时,必须注意原生方法的内部调用链,避免触发递归循环。转字符串只是临时规避方案,更稳妥的方式是通过恢复原生方法或直接调用对应类型的响应方法(如res.json)来处理。
内容的提问来源于stack exchange,提问作者Hello World
相关产品推荐
相关产品推荐

