Express使用JSON解析器时出现[ERR_STREAM_WRITE_AFTER_END]错误排查
你遇到的这个错误核心原因很明确:你在全局中间件里强行调用了res.end(),而路由处理逻辑中(或隐式的中间件操作中)已经完成了响应流的结束操作,重复调用res.end()就会触发Node.js的流操作错误。
咱们拆解一下你的代码流程,就能明白问题出在哪:
当你发送application/json请求时,请求会先进入全局中间件,执行到await next()后,会依次经过jsonParser中间件(解析请求体)、路由处理函数。路由里调用了res.write('ok')写入响应数据,但没有显式结束响应;等路由处理完成回到全局中间件时,你又调用了res.end()——这看似没问题,但结合jsonParser的行为,内部可能已经触发了响应流的状态变化,导致第二次调用res.end()时,Node.js检测到响应流已经被标记为结束,从而抛出错误。而当你不用jsonParser时,请求体没有被解析,响应流的状态没有被触发变化,所以重复调用res.end()暂时没报错,但这其实也是不安全的写法。
你不需要放弃全局中间件,只需要调整写法就能满足需求
你的核心需求是用全局中间件做请求前后的统一处理,这完全可以实现,只要避免在中间件里手动结束响应即可:
方案1:使用Express高层响应方法(推荐)
Express提供的res.send()、res.json()等方法会自动帮你设置响应头、写入数据并结束响应,不需要手动调用res.end()。修改你的代码如下:
import express from 'express'; const app = express(); const jsonParser = express.json(); // 全局中间件:只做前后置处理,不手动结束响应 app.use(async (req, res, next) => { // 前置操作:统一设置响应头、打印日志 console.log('Raw request:', req.method, req.path); res.setHeader('Content-Type', 'application/json'); await next(); // 后置操作:比如打印响应完成日志 console.log('Response finished for:', req.path); }); app.post('/example', jsonParser, async (req, res) => { console.log('Parsed request body:', req.body); // 使用res.json返回合法JSON,自动结束响应 res.json({ message: 'ok' }); }); app.listen(3000);
方案2:如果必须使用底层的res.write()
如果你确实需要分块写入响应(比如流式返回数据),那应该在路由处理函数中自己调用res.end(),不要依赖中间件:
import express from 'express'; const app = express(); const jsonParser = express.json(); app.use(async (req, res, next) => { console.log('Raw request:', req.method, req.path); res.setHeader('Content-Type', 'application/json'); await next(); console.log('Response finished for:', req.path); }); app.post('/example', jsonParser, async (req, res) => { console.log('Parsed request body:', req.body); // 分块写入数据后,手动结束响应 res.write('{"message": "ok"}'); res.end(); }); app.listen(3000);
额外提示
你之前设置了Content-Type: application/json,但路由里返回的是字符串ok,这不符合JSON格式规范,会导致客户端解析失败。用res.json()可以自动帮你把数据序列化为合法的JSON,同时保持响应头的一致性。
内容的提问来源于stack exchange,提问作者user14764

