Traducir esta página

Manejo de errores

Error Handling refers to how Express catches and processes errors that occur both synchronously and asynchronously. Express viene con un manejador de error predeterminado , así que no necesitas escribir el tuyo propio para empezar.

Errores de captura

Es importante asegurarse de que Express capture todos los errores que ocurren mientras ejecuta los manejadores de rutas y el middleware.

Errors in synchronous code

Los errores que ocurren en el código sincrónico dentro de los manejadores de rutas y middleware no requieren trabajo extra. Si el código sincrónico arroja un error, entonces Express hará capturarlo y procesarlo. Por ejemplo:

app.get('/', (req, res) => {
throw new Error('BROKEN'); // Express will catch this on its own.
});

Errors in asynchronous code

The recommended way to write asynchronous handlers is with async functions. Route handlers and middleware that return a Promise call next(value) automatically when they reject or throw an error, and async functions always return a Promise, so their errors reach Express with no extra work. Por ejemplo:

app.get('/user/:id', async (req, res) => {
const user = await getUserById(req.params.id);
res.send(user);
});

Si getUserById arroja un error o rechaza, next será llamado con el error arrojado o el valor rechazado. Si no se proporciona ningún valor rechazado, next será llamado con un objeto de Error predeterminado proporcionado por el enrutador Express.

If you pass anything to the next() function (except the string 'route'), Express regards the current request as being an error and will skip any remaining non-error handling routing and middleware functions.

Working with promise chains

If you build a promise chain instead of using an async function, return the promise from the handler and Express will likewise call next automatically when it rejects:

app.get('/', (req, res) => {
return Promise.resolve().then(() => {
throw new Error('BROKEN'); // Express will catch this and call next.
});
});

If the promise is not returned, Express does not know it exists, and you must route the error yourself by providing next as the final catch handler. Without it, the rejection would be unhandled and crash the process:

app.get('/', (req, res, next) => {
Promise.resolve()
.then(() => {
throw new Error('BROKEN');
})
.catch(next); // Errors will be passed to Express.
});

This works because if a callback in the promise chain throws, the chain turns that exception into a rejection, which travels down to the final .catch. There, .catch calls its handler with the error as the first argument, which is exactly the argument next expects, so the error reaches Express.

Working with callback APIs

Errors produced by callback-based APIs, such as those in node:fs, are not thrown and are not part of any promise. The callback receives them as its first argument, and you must pass them to the next() function yourself, where Express will catch and process them. Por ejemplo:

app.get('/', (req, res, next) => {
fs.readFile('/file-does-not-exist', (err, data) => {
if (err) {
next(err); // Pass errors to Express.
} else {
res.send(data);
}
});
});

Si la devolución de llamada en una secuencia no proporciona datos, sólo errores, puede simplificar este código de la siguiente manera:

app.get('/', [
function (req, res, next) {
fs.writeFile('/inaccessible-path', 'data', next);
},
function (req, res) {
res.send('OK');
},
]);

En el ejemplo anterior, next es proporcionado como el callback para fs.writeFile, que es llamado con o sin errores. Si no hay error, se ejecuta el segundo manejador , de lo contrario Express catches y procesa el error.

También podría utilizar una cadena de manejadores para depender de la captura de errores sincrónicos , reduciendo el código asincrónico a algo trivial. Por ejemplo:

app.get('/', [
function (req, res, next) {
fs.readFile('/maybe-valid-file', 'utf-8', (err, data) => {
res.locals.data = data;
next(err);
});
},
function (req, res) {
res.locals.data = res.locals.data.split(',')[1];
res.send(res.locals.data);
},
]);

The above example contains a couple of trivial statements following the readFile call. Si readFile causa un error, entonces pasa el error a Express, de lo contrario regresa rápidamente al mundo del manejo sincrónico de errores en el siguiente manejador en la cadena. Luego, el ejemplo anterior intenta procesar los datos. Si esto falla, entonces el gestor de errores sincrónico lo capturará. Si hubiera hecho este procesamiento dentro de el callback readFile, entonces la aplicación podría salir y los manejadores de error Express no se ejecutarían.

Finally, for asynchronous code that provides no error-first callback, such as a timer, catch errors inside the asynchronous code itself and pass them to Express:

app.get('/', (req, res, next) => {
setTimeout(() => {
try {
throw new Error('BROKEN');
} catch (err) {
next(err);
}
}, 100);
});

El ejemplo anterior utiliza un bloque try...catch para capturar errores en el código asincrónico y pasarlos a Express. Si el bloque try...catch fuera omitido, Express no capturaría el error ya que no es parte del código de manejador sincrónico.

Whichever method you use, if you want Express error handlers to be called in and the application to survive, you must ensure that Express receives the error.

El gestor de errores por defecto

Express viene con un gestor de errores integrado que se encarga de cualquier error que pueda encontrarse en la aplicación. Esta función middleware por defecto se añade al final de la pila de funciones de middleware.

Si pasas un error a next() y no lo gestionas en un controlador error personalizado, será manejado por el gestor de errores integrado; el error será escrito al cliente con el stack trace. El stack trace no está incluido en el entorno de producción.

Note

Establece la variable de entorno NODE_ENV a production, para ejecutar la aplicación en el modo de producción.

When an error is written, the following information is added to the response:

  • El res.statusCode se establece desde err.status (o err.statusCode). Si este valor está fuera del rango 4xx o 5xx, se establecerá a 500.
  • El res.statusMessage se establece de acuerdo al código de estado.
  • El cuerpo será el HTML del mensaje del código de estado cuando en el entorno de producción, de lo contrario será err.stack.
  • Cualquier cabecera especificada en un objeto err.headers.

Si llama a next() con un error después de haber comenzado a escribir la respuesta (por ejemplo, si encuentras un error al transmitir la respuesta al cliente), el gestor de errores Express por defecto cierra la conexión y falla la solicitud.

Así que cuando añada un manejador de errores personalizado, debe delegar a el manejador de errores predeterminado Express cuando los encabezados ya han sido enviados al cliente:

function errorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}

Tenga en cuenta que el manejador de errores por defecto puede activarse si llama a next() con un error en su código más de una vez, incluso si el middleware está configurado para manejar errores personalizados.

Otro middleware de manejo de error se puede encontrar en Express middleware.

Manejadores de errores escritos

Define las funciones de middleware de la misma manera que otras funciones de middleware, excepto las funciones de manejo de errores tienen cuatro argumentos en lugar de tres: (err, req, res, next). Por ejemplo:

app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});

Usted define middleware manejado por errores últimamente, después de otras app.use() y llamadas de rutas; por ejemplo:

index.cjs
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use(bodyParser.json());
app.use(methodOverride());
app.use((err, req, res, next) => {
// logic
});

Las respuestas desde dentro de una función middleware pueden estar en cualquier formato, como una página de error HTML, un mensaje simple, o una cadena JSON.

For organizational (and higher-level framework) purposes, you can define several error-handling middleware functions, much as you would with regular middleware functions. Por ejemplo, para definir un manejador de errores para las solicitudes realizadas usando XHR y las que no tienen:

index.cjs
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use(bodyParser.json());
app.use(methodOverride());
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);

En este ejemplo, el genérico logErrors podría escribir una solicitud y información de error a stderr, por ejemplo:

function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}

También en este ejemplo, clientErrorHandler se define de la siguiente manera; en este caso, el error se pasa explícitamente al siguiente.

Notice that when you do not call next in an error-handling function, you are responsible for writing (and ending) the response. De lo contrario, esas solicitudes se “colgarán” y no serán elegibles para la recolección de basura.

function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' });
} else {
next(err);
}
}

Implementa la función “catch-all” errorHandler como sigue (por ejemplo):

function errorHandler(err, req, res, next) {
res.status(500);
res.render('error', { error: err });
}

Si tiene un manejador de ruta con múltiples funciones de callback, puede utilizar el parámetro route para saltar al siguiente manejador de ruta. Por ejemplo:

app.get(
'/a_route_behind_paywall',
(req, res, next) => {
if (!req.user.hasPaid) {
// continue handling this request
next('route');
} else {
next();
}
},
(req, res, next) => {
PaidContent.find((err, doc) => {
if (err) return next(err);
res.json(doc);
});
}
);

En este ejemplo, el manejador getPaidContent se omitirá pero cualquier manejador restante en app para /a_route_behind_paywall continuaría siendo ejecutado.

Note

Las llamadas a next() y next(err) indican que el controlador actual está completo y en qué estado. next(err) omitirá todos los manejadores restantes en la cadena, excepto aquellos que están configurados a errores de manejo como se describe arriba.