Node.js Невозможно установить заголовки после их отправки Ошибка после введения res.render () - PullRequest
0 голосов
/ 28 августа 2018

У меня есть этот фрагмент кода:

   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 - загрузка файла из памяти - «имя файла должно быть строкой»

То, что это делает, это берет строку из памяти и передает ее клиенту как текстовый файл.

Этот код работал.

Затем я решил добавить строку res.render('waiting.ejs');, и я получил эту ошибку:

Error: Can't set headers after they are sent.

Затем я экспериментировал с добавлением еще одного res.render () [в этом случае res.render('success.ejs');] до и после кода, который отправляет файл .txt клиенту.

Ошибка осталась. Также нет перенаправления на success.ejs, другими словами, res.render('success.ejs'); никогда не работал, несмотря на то, помещается ли он перед ofr после этого куска кода.

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

   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( .....

Когда вы добавляете промежуточное программное обеспечение для Express (которое построено на Connect) с помощью метода app.use, вы добавляете элементы в Server.prototype.stack в Connect. Когда сервер получает запрос, он выполняет итерацию по стеку, вызывая метод (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
0 голосов
/ 31 августа 2018

Функция res.render () компилирует ваш шаблон, вставляет туда местные и создает html-вывод из этих двух вещей. вот почему приходит ошибка. не используйте это дважды, потому что это посылает ответ.

0 голосов
/ 28 августа 2018

Вам необходимо проверить исходный код express.js ( здесь ):

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(), он передаст обратный вызов done на app.render(...) ( исходный код ), затем он передаст done на tryInitView и т. Д.

В конце он вызовет done обратный вызов с str в случае успеха или err в случае сбоя. Затем он вызывает res.send() внутри done обратного вызова, который просто блокирует вас от установки заголовков после этого.

...