我有这段代码:
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( .....
我评论为“从服务器内存发送文件的聪明方式”的代码来自这篇文章:Node Express.js - Download file from memory - 'filename must be a string'
它的作用是从内存中获取一个字符串,并将其作为.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');
从来没有工作过,尽管它是否在那段代码之前放置。
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方法添加要表达的中间件(在连接上构建)时,您将在connect中将项目附加到Server.prototype.stack
。当服务器获得请求时,它会迭代堆栈,调用(request,response,next)方法。
问题是,如果其中一个中间件项目写入响应主体或标题(它看起来像是/或由于某种原因),但不调用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);
有问题的中间件设置响应头而不调用response.end()
并调用next(),这会混淆express的服务器。所以你通过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
你必须检查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
。然后它会在res.send()
回调中触发done
,这会阻止你在此之后设置标题。
res.render()函数编译你的模板,在那里插入locals,并从这两件事创建html输出。这就是错误来临的原因。不要使用它两次因为它发送响应。