Node.js CRUD API出现ERR_STREAM_WRITE_AFTER_END错误求助
解决Node.js API中POST/PUT/DELETE触发
write after end错误的问题 先直接点出核心:这个错误说明你的response对象在调用resp.end()之后,又被尝试写入内容了。结合你的代码和日志,我帮你拆解问题根源和具体解决办法:
问题根源分析
从你的日志输出能看到关键时序:
Before send Before Write Write Head Write body end After send events.js:174 throw er; // Unhandled 'error' event
明明已经执行了resp.end(),但后续又触发了一次resp.write()操作。具体有几个触发点:
- 错误处理时重复发送响应:你的
try/catch块和异步数据库回调里都有错误处理(msg.show500),如果数据库操作出错,可能已经发送过一次错误响应,后续又执行了正常路径的sendJSON,导致重复写入。 - 请求体过大时未终止后续逻辑:在
req.on("data")里触发show413后,没有终止请求的后续处理,req.on("end")依然会执行,继续调用staff.add/update,再次尝试发送响应。 - 异步回调未及时终止:数据库操作的回调里,出错后没有用
return终止后续代码,可能导致正常响应逻辑也被执行。
具体解决方案
1. 给所有响应方法加「防重复发送」校验
Node.js的ServerResponse对象自带headersSent属性,可以用来检查是否已经发送过响应头。修改你的httpMsgs.js里所有发送响应的方法:
exports.sendJSON = function(req, resp, data){ if(resp.headersSent) return; // 已发送过响应则直接返回 if(data){ console.log('Before Write') resp.writeHead(200, {"Content-type" : "application/json"}); console.log('Write Head') resp.write(JSON.stringify(data)); console.log('Write body') resp.end(); console.log('end'); } } // 同样修改show500、show413、show404 exports.show500 = function(req, resp, err){ if(resp.headersSent) return; resp.writeHead(500, {"Content-type" : "application/json"}); resp.write(JSON.stringify({error: err.message})); resp.end(); } exports.show413 = function(req, resp){ if(resp.headersSent) return; resp.writeHead(413, {"Content-type" : "application/json"}); resp.write(JSON.stringify({error: "Request entity too large"})); resp.end(); } exports.show404 = function(req, resp){ if(resp.headersSent) return; resp.writeHead(404, {"Content-type" : "application/json"}); resp.write(JSON.stringify({error: "Resource not found"})); resp.end(); }
这一步能从根源上避免重复写入响应。
2. 请求体过大时终止后续请求处理
当触发show413后,不仅要返回响应,还要停止接收请求数据、阻止end事件触发。修改controller.js里的POST/PUT逻辑:
case "POST": if (req.url === "/staff/add"){ var reqBody = ''; // 保存data事件回调的引用,方便后续移除 const onData = function (data){ reqBody += data; if(reqBody.length > 1e7){ msg.show413(req, resp); req.removeListener("data", onData); // 停止接收数据 req.destroy(); // 终止请求,阻止end事件触发 } }; req.on("data", onData); req.on("end", function() { if(resp.headersSent) return; // 已发送响应则跳过后续逻辑 staff.add(req, resp, reqBody) }); } break;
PUT部分做同样的修改,这样就不会在返回413后还执行后续的数据库操作。
3. 异步回调里加终止逻辑
在staff.js的数据库回调中,出错后要及时用return终止后续代码,避免正常响应逻辑被误执行:
db.executeSql("SELECT MAX(ID) AS ID FROM jarvisdb.staff",function (maxID, err){ if(err){ msg.show500(req, resp, err); return; // 终止后续代码,避免执行正常逻辑 } // 后续正常插入逻辑 }); // 插入数据的回调也要加 db.executeSql(sql, (data, err)=>{ if(err){ msg.show500(req, resp, err); return; } msg.sendJSON(req,resp,data) })
4. 小细节修正
你在POST的end事件里打印了console.log(req.data),但req.data并没有被赋值,建议去掉或者修正,避免混淆日志。
为什么GET接口正常?
因为GET接口没有处理请求体的异步data/end事件,逻辑是同步触发的,响应只会发送一次,不会出现重复写入的情况。而POST/PUT需要异步接收请求体+异步数据库操作,很容易出现多次调用响应方法的情况。
按上面的步骤修改后,应该就能彻底解决write after end的问题了。
内容的提问来源于stack exchange,提问作者AjDed
相关产品推荐
相关产品推荐

