Node.js 发送后无法设置 headers 引入 res.render() 后出错
Node.js Can't set headers after they are sent Error after introducing res.render()
我有这段代码:
app.post('/pst', function(req, res) {
var data = req.body.convo;
res.render('waiting.ejs'); //ADDED THIS
myFunc(data).then(result => {
res.render('success.ejs'); //THEN THIS
//---------------------------------
//clever way to send text file to client from the memory of the server
var fileContents = Buffer.from(result, 'ascii');
var readStream = new stream.PassThrough();
readStream.end(fileContents);
res.set('Content-disposition', 'attachment; filename=' + fileName);
res.set('Content-Type', 'text/plain');
readStream.pipe(res);
//--------------------------------------
}).catch( .....
我评论为'clever way to send file from memory of the server'的代码来自这个post:
它的作用是从内存中获取一个字符串并将其作为 .txt 文件提供给客户端。
此代码曾经有效。
然后我决定添加 res.render('waiting.ejs');
行,但我得到了这个错误:
Error: Can't set headers after they are sent.
然后我尝试在将 .txt 文件发送到客户端的代码之前和之后添加另一个 res.render() [在本例中为 res.render('success.ejs');
]。
错误依旧。此外,没有重定向到 success.ejs,换句话说,res.render('success.ejs');
从未工作过,尽管它是否放在那段代码之后的 ofr 之前。
您必须检查 express.js 源代码 (here):
res.render = function render(view, options, callback) {
var app = this.req.app;
var done = callback;
var opts = options || {};
var req = this.req;
var self = this;
// support callback function as second arg
if (typeof options === 'function') {
done = options;
opts = {};
}
// merge res.locals
opts._locals = self.locals;
// default callback to respond
done = done || function (err, str) {
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, opts, done);
};
你可以看到,当你使用res.render()
方法时,它会将完成的回调传递给app.render(...)
(source code),然后它将done
传递给tryInitView
等
最后,它将调用 done
回调,如果成功则调用 str
,如果失败则调用 err
。然后它会在 done
回调中触发 res.send()
,这只会阻止您在那之后设置 headers。
app.post('/pst', function(req, res) {
var data = req.body.convo;
myFunc(data).then(result => {
//---------------------------------
//clever way to send text file to client from the memory of the server
var fileContents = Buffer.from(result, 'ascii');
var readStream = new stream.PassThrough();
readStream.end(fileContents);
res.set('Content-disposition', 'attachment; filename=' + fileName);
res.set('Content-Type', 'text/plain');
readStream.pipe(res);
res.redirect(`/success`); //THEN THIS
//--------------------------------------
}).catch( .....
当您使用 app.use 方法将中间件添加到 express(基于 connect)时,您是在 connect 中将项目附加到 Server.prototype.stack
。
当服务器收到请求时,它会遍历堆栈,调用 (request, response, next) 方法。
问题是,如果其中一个中间件项写入响应 body 或 headers(出于某种原因看起来像是 either/or),但实际上并没有t 调用 response.end()
然后调用 next()
然后当核心 Server.prototype.handle 方法完成时,它会注意到:
there are no more items in the stack, and/or
that response.headerSent is true.
因此,它会引发错误。但是它抛出的错误只是这个基本的响应(来自connect http.js源代码:
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Cannot ' + req.method + ' ' + req.url);
有问题的中间件设置响应header而不调用response.end()
并调用next(),这混淆了express的服务器。
所以你设置 header 到 res.render()
。现在如果你想再次渲染它会抛出一个错误。
app.get('/success',(req,res)=> {
res.render("container/index",{waiting:"waiting",......});
//handle your task then in client side index.ejs with appropriate setTimeout(()=>{},2000) for the waiting div , show waiting div for 2 seconds
});
//then your actual success gets render
res.render() 函数编译您的模板,在那里插入局部变量,并从这两个东西中创建 html 输出。这就是为什么会出现错误。
不要使用它两次,因为它会发送响应。
我有这段代码:
app.post('/pst', function(req, res) {
var data = req.body.convo;
res.render('waiting.ejs'); //ADDED THIS
myFunc(data).then(result => {
res.render('success.ejs'); //THEN THIS
//---------------------------------
//clever way to send text file to client from the memory of the server
var fileContents = Buffer.from(result, 'ascii');
var readStream = new stream.PassThrough();
readStream.end(fileContents);
res.set('Content-disposition', 'attachment; filename=' + fileName);
res.set('Content-Type', 'text/plain');
readStream.pipe(res);
//--------------------------------------
}).catch( .....
我评论为'clever way to send file from memory of the server'的代码来自这个post:
它的作用是从内存中获取一个字符串并将其作为 .txt 文件提供给客户端。
此代码曾经有效。
然后我决定添加 res.render('waiting.ejs');
行,但我得到了这个错误:
Error: Can't set headers after they are sent.
然后我尝试在将 .txt 文件发送到客户端的代码之前和之后添加另一个 res.render() [在本例中为 res.render('success.ejs');
]。
错误依旧。此外,没有重定向到 success.ejs,换句话说,res.render('success.ejs');
从未工作过,尽管它是否放在那段代码之后的 ofr 之前。
您必须检查 express.js 源代码 (here):
res.render = function render(view, options, callback) {
var app = this.req.app;
var done = callback;
var opts = options || {};
var req = this.req;
var self = this;
// support callback function as second arg
if (typeof options === 'function') {
done = options;
opts = {};
}
// merge res.locals
opts._locals = self.locals;
// default callback to respond
done = done || function (err, str) {
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, opts, done);
};
你可以看到,当你使用res.render()
方法时,它会将完成的回调传递给app.render(...)
(source code),然后它将done
传递给tryInitView
等
最后,它将调用 done
回调,如果成功则调用 str
,如果失败则调用 err
。然后它会在 done
回调中触发 res.send()
,这只会阻止您在那之后设置 headers。
app.post('/pst', function(req, res) {
var data = req.body.convo;
myFunc(data).then(result => {
//---------------------------------
//clever way to send text file to client from the memory of the server
var fileContents = Buffer.from(result, 'ascii');
var readStream = new stream.PassThrough();
readStream.end(fileContents);
res.set('Content-disposition', 'attachment; filename=' + fileName);
res.set('Content-Type', 'text/plain');
readStream.pipe(res);
res.redirect(`/success`); //THEN THIS
//--------------------------------------
}).catch( .....
当您使用 app.use 方法将中间件添加到 express(基于 connect)时,您是在 connect 中将项目附加到 Server.prototype.stack
。
当服务器收到请求时,它会遍历堆栈,调用 (request, response, next) 方法。
问题是,如果其中一个中间件项写入响应 body 或 headers(出于某种原因看起来像是 either/or),但实际上并没有t 调用 response.end()
然后调用 next()
然后当核心 Server.prototype.handle 方法完成时,它会注意到:
there are no more items in the stack, and/or
that response.headerSent is true.
因此,它会引发错误。但是它抛出的错误只是这个基本的响应(来自connect http.js源代码:
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Cannot ' + req.method + ' ' + req.url);
有问题的中间件设置响应header而不调用response.end()
并调用next(),这混淆了express的服务器。
所以你设置 header 到 res.render()
。现在如果你想再次渲染它会抛出一个错误。
app.get('/success',(req,res)=> {
res.render("container/index",{waiting:"waiting",......});
//handle your task then in client side index.ejs with appropriate setTimeout(()=>{},2000) for the waiting div , show waiting div for 2 seconds
});
//then your actual success gets render
res.render() 函数编译您的模板,在那里插入局部变量,并从这两个东西中创建 html 输出。这就是为什么会出现错误。 不要使用它两次,因为它会发送响应。