Saltar al contenido

Error: no se pueden establecer encabezados después de enviarlos al cliente

Solución:

los res objeto en Express es una subclase de Node.js http.ServerResponse (lea la fuente http.js). Se le permite llamar res.setHeader(name, value) tantas veces como quieras hasta que llames res.writeHead(statusCode). Después writeHead, los encabezados están integrados y solo puede llamar res.write(data), y finalmente res.end(data).

El error “Error: no se pueden establecer los encabezados después de enviarlos”. significa que ya está en el estado Body o Finished, pero alguna función intentó establecer un encabezado o statusCode. Cuando vea este error, intente buscar cualquier cosa que intente enviar un encabezado después de que ya se haya escrito parte del cuerpo. Por ejemplo, busque devoluciones de llamada que se llaman accidentalmente dos veces o cualquier error que ocurra después de que se envíe el cuerpo.

En tu caso, llamaste res.redirect(), lo que provocó que la respuesta se convirtiera en Finalizada. Entonces su código arrojó un error (res.req es null). y dado que el error ocurrió dentro de su function(req, res, next) (no dentro de una devolución de llamada), Connect pudo detectarlo y luego intentó enviar una página de error 500. Pero dado que los encabezados ya se enviaron, Node.js’s setHeader arrojó el error que vio.

Lista completa de métodos de respuesta Node.js / Express y cuándo deben llamarse:

La respuesta debe estar en Cabeza y permanece en Cabeza:

  1. res.writeContinue()
  2. res.statusCode = 404
  3. res.setHeader(name, value)
  4. res.getHeader(name)
  5. res.removeHeader(name)
  6. res.header(key[, val]) (Solo expreso)
  7. res.charset="utf-8" (Solo Express; solo afecta a métodos específicos de Express)
  8. res.contentType(type) (Solo expreso)

La respuesta debe estar en Cabeza y se convierte en Cuerpo:

  1. res.writeHead(statusCode, [reasonPhrase], [headers])

La respuesta puede estar en Cabeza / Cuerpo y permanece en Cuerpo:

  1. res.write(chunk, encoding='utf8')

La respuesta puede estar en Cabeza / Cuerpo y se convierte en Finalizado:

  1. res.end([data], [encoding])

La respuesta puede estar en Cabeza / Cuerpo y permanece en su estado actual:

  1. res.addTrailers(headers)

La respuesta debe estar en Cabeza y se convierte en Finalizado:

  1. return next([err]) (Solo Connect / Express)
  2. Cualquier excepción dentro del middleware function(req, res, next) (Solo Connect / Express)
  3. res.send(body|status[, headers|status[, status]]) (Solo expreso)
  4. res.attachment(filename) (Solo expreso)
  5. res.sendfile(path[, options[, callback]]) (Solo expreso)
  6. res.json(obj[, headers|status[, status]]) (Solo expreso)
  7. res.redirect(url[, status]) (Solo expreso)
  8. res.cookie(name, val[, options]) (Solo expreso)
  9. res.clearCookie(name[, options]) (Solo expreso)
  10. res.render(view[, options[, fn]]) (Solo expreso)
  11. res.partial(view[, options]) (Solo expreso)

También encontré este error por un tiempo. Creo (espero) haberlo comprendido, quería escribirlo aquí como referencia.

Cuando agrega middleware para conectarse o expresarse (que se basa en la conexión) usando el app.use método, estás agregando elementos a Server.prototype.stack en conexión (al menos con la corriente npm install connect, que se ve bastante diferente del github de esta publicación). Cuando el servidor recibe una solicitud, itera sobre la pila, llamando al (request, response, next) método.

El problema es que, si en uno de los elementos de middleware se escribe en el cuerpo de la respuesta o en los encabezados (parece que es una de las dos o por alguna razón), pero no llama response.end() y tu llamas next() entonces como el núcleo Server.prototype.handle se completa el método, se dará cuenta de que:

  1. no hay más elementos en la pila, y / o
  2. ese response.headerSent es verdad.

Entonces, arroja un error. Pero el error que arroja es solo esta respuesta básica (de la conexión http.js código fuente:

res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Cannot ' + req.method + ' ' + req.url);

Justo ahí, está llamando res.setHeader('Content-Type', 'text/plain');, que probablemente haya establecido en su render método, sin llamar a response.end (), algo como:

response.setHeader("Content-Type", "text/html");
response.write("<p>Hello World</p>");

La forma en que todo debe estructurarse es así:

Buen middleware

// middleware that does not modify the response body
var doesNotModifyBody = function(request, response, next) {
  request.params = {
    a: "b"
  };
  // calls next because it hasn't modified the header
  next();
};

// middleware that modify the response body
var doesModifyBody = function(request, response, next) {
  response.setHeader("Content-Type", "text/html");
  response.write("<p>Hello World</p>");
  response.end();
  // doesn't call next()
};

app.use(doesNotModifyBody);
app.use(doesModifyBody);

Middleware problemático

var problemMiddleware = function(request, response, next) {
  response.setHeader("Content-Type", "text/html");
  response.write("<p>Hello World</p>");
  next();
};

El middleware problemático establece el encabezado de respuesta sin llamar response.end() y llama next(), que confunde al servidor de Connect.

Algunas de las respuestas de estas preguntas y respuestas son incorrectas. La respuesta aceptada tampoco es muy “práctica”, así que quiero publicar una respuesta que explique las cosas en términos más simples. Mi respuesta cubrirá el 99% de los errores que veo publicados una y otra vez. Para conocer las razones reales detrás del error, eche un vistazo a la respuesta aceptada.


HTTP usa un ciclo que requiere una respuesta por solicitud. Cuando el cliente envía una solicitud (por ejemplo, POST o GET), el servidor solo debe enviarle una respuesta.

Este mensaje de error:

Error: no se pueden configurar los encabezados después de enviarlos.

Suele ocurrir cuando envías varias respuestas para una solicitud. Asegúrese de que las siguientes funciones se invoquen solo una vez por solicitud:

  • res.json()
  • res.send()
  • res.redirect()
  • res.render()

(y algunos más que se usan con poca frecuencia, marque la respuesta aceptada)

La devolución de llamada de ruta no volverá cuando se invoquen estas funciones res. Continuará ejecutándose hasta que llegue al final de la función o una declaración de retorno. Si desea volver al enviar una respuesta, puede hacerlo así: return res.send().


Tomemos, por ejemplo, este código:

app.post('/api/route1', function(req, res) {
  console.log('this ran');
  res.status(200).json({ message: 'ok' });
  console.log('this ran too');
  res.status(200).json({ message: 'ok' });
}

Cuando se envía una solicitud POST a / api / route1 ejecutará todas las líneas de la devolución de llamada. A No se pueden establecer encabezados después de enviarlos se lanzará un mensaje de error porque res.json() se llama dos veces, lo que significa que se envían dos respuestas.

¡Solo se puede enviar una respuesta por solicitud!


El error en el ejemplo de código anterior era obvio. Un problema más típico es cuando tiene varias ramas:

app.get('/api/company/:companyId', function(req, res) {
  const { companyId } = req.params;
  Company.findById(companyId).exec((err, company) => {
      if (err) {
        res.status(500).json(err);
      } else if (!company) {
        res.status(404).json();      // This runs.
      }
      res.status(200).json(company); // This runs as well.
    });
}

Esta ruta con devolución de llamada adjunta encuentra una empresa en una base de datos. Al hacer una consulta para una empresa que no existe nos adentraremos en el else if sucursal y envíe una respuesta 404. Después de eso, continuaremos con la siguiente declaración que también envía una respuesta. Ahora hemos enviado dos respuestas y aparecerá el mensaje de error. Podemos corregir este código asegurándonos de enviar solo una respuesta:

.exec((err, company) => {
  if (err) {
    res.status(500).json(err);
  } else if (!company) {
    res.status(404).json();         // Only this runs.
  } else {
    res.status(200).json(company);
  }
});

o regresando cuando se envía la respuesta:

.exec((err, company) => {
  if (err) {
    return res.status(500).json(err);
  } else if (!company) {
    return res.status(404).json();  // Only this runs.
  }
  return res.status(200).json(company);
});

Un gran pecador son las funciones asincrónicas. Tome la función de esta pregunta, por ejemplo:

article.save(function(err, doc1) {
  if (err) {
    res.send(err);
  } else {
    User.findOneAndUpdate({ _id: req.user._id }, { $push: { article: doc._id } })
    .exec(function(err, doc2) {
      if (err) res.send(err);
      else     res.json(doc2);  // Will be called second.
    })

    res.json(doc1);             // Will be called first.
  }
});

Aquí tenemos una función asincrónica (findOneAndUpdate()) en el ejemplo de código. Si no hay errores (err) findOneAndUpdate() sera llamado. Debido a que esta función es asincrónica, res.json(doc1) será llamado inmediatamente. Suponga que no hay errores en findOneAndUpdate(). los res.json(doc2) en el else entonces será llamado. Ahora se han enviado dos respuestas y el No se pueden establecer encabezados aparece un mensaje de error.

La solución, en este caso, sería eliminar el res.json(doc1). Para enviar ambos documentos al cliente, res.json() en el otro podría escribirse como res.json({ article: doc1, user: doc2 }).

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *