Traduire cette page

Gestion des erreurs

Error Handling fait référence à la façon dont Express attrape et traite les erreurs que se produisent de manière synchronisée et asynchrone. Express comes with a default error handler so you don’t need to write your own to get started.

Erreurs de capture

Il est important de s’assurer qu’Express attrape toutes les erreurs qui se produisent lorsque exécute des gestionnaires de routes et des middleware.

Errors in synchronous code

Les erreurs qui se produisent dans le code synchrone dans les gestionnaires de route et les middleware ne nécessitent aucun travail supplémentaire. If synchronous code throws an error, then Express will catch and process it. Par exemple :

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. Par exemple :

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

Si getUserById lance une erreur ou un rejet, next sera appelé avec soit l’erreur émise ou la valeur rejetée. Si aucune valeur rejetée n’est fournie, next sera appelée avec un objet Error par défaut fourni par le routeur 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. Par exemple :

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 le callback dans une séquence ne fournit aucune donnée, seulement des erreurs, vous pouvez simplifier ce code comme suit:

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

Dans l’exemple ci-dessus, next est fourni comme callback pour fs.writeFile, qui est appelé avec ou sans erreurs. S’il n’y a pas d’erreur, le second est exécuté, sinon Express attrape et traite l’erreur.

Vous pouvez également utiliser une chaîne de gestionnaires pour vous fier à l’erreur synchrone attrapant, en réduisant le code asynchrone à quelque chose de trivial. Par exemple :

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 cause une erreur, alors il passe l’erreur à Express, sinon vous revenez rapidement au monde de la gestion des erreurs synchrones dans le prochain gestionnaire de la chaîne. Ensuite, l’exemple ci-dessus tente de traiter les données. Si cela échoue, alors le gestionnaire d’erreurs synchrone l’attrapera. Si vous aviez fait ce traitement à l’intérieur de la callback readFile, alors l’application pourrait quitter et les gestionnaires d’erreur Express ne s’exécuteraient pas.

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);
});

L’exemple ci-dessus utilise un bloc try...catch pour attraper des erreurs dans le code asynchrone et les passer à Express. Si le bloc try...catch était omis, Express ne attrapera pas l’erreur car il ne fait pas partie du code du gestionnaire synchrone.

Quelle que soit la méthode que vous utilisez, si vous voulez que les gestionnaires d’erreur Express soient appelés et que l’application survive, vous devez vous assurer qu’Express reçoit l’erreur.

Le gestionnaire d’erreur par défaut

Express est livré avec un gestionnaire d’erreur intégré qui prend en charge toutes les erreurs qui peuvent être rencontrées dans l’application. Cette fonction middleware par défaut est ajoutée à la fin de la pile de fonctions du middleware.

Si vous passez une erreur à next() et que vous ne la gérez pas dans un gestionnaire d’erreur personnalisé , il sera géré par le gestionnaire d’erreur intégré ; l’erreur sera écrite au client avec la trace de la pile. La trace de la pile n’est pas incluse dans l’environnement de production.

Définissez la variable d’environnement NODE_ENV à production, pour exécuter l’application en mode production.

Lorsqu’une erreur est écrite, les informations suivantes sont ajoutées à la réponse :

  • Le res.statusCode est défini à partir de err.status (ou err.statusCode). Si cette valeur est en dehors de la plage 4xx ou 5xx, elle sera définie à 500.
  • Le res.statusMessage est défini selon le code de statut.
  • Le corps sera le HTML du message de code de statut lorsque l’environnement de production , sinon sera err.stack.
  • N’importe quel en-tête spécifié dans un objet err.headers.

Si vous appelez next() avec une erreur après avoir commencé à écrire la réponse (par exemple, si vous rencontrez une erreur lors du streaming de la réponse au client), le gestionnaire d’erreur par défaut Express ferme la connexion et échoue la requête.

Ainsi, lorsque vous ajoutez un gestionnaire d’erreurs personnalisé, vous devez déléguer à le gestionnaire d’erreur Express par défaut, lorsque les en-têtes ont déjà été envoyés au client :

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

Notez que le gestionnaire d’erreur par défaut peut être déclenché si vous appelez next() avec une erreur dans votre code plusieurs fois, même si une gestion personnalisée des erreurs est en place.

D’autres erreurs de gestion du middleware peuvent être trouvées dans middleware Express.

Écriture des gestionnaires d’erreurs

Définissez les fonctions du middleware de la même manière que les autres fonctions du middleware, excepté les fonctions de gestion des erreurs ont quatre arguments au lieu de trois: (err, req, res, next). Par exemple :

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

Vous définissez le middleware en dernier lieu, après d’autres appels app.use() et routage des appels; par exemple:

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
});

Les réponses provenant d’une fonction middleware peuvent être dans n’importe quel format, comme une page d’erreur HTML, un message simple ou une chaîne JSON.

Pour des raisons organisationnelles (et pour un cadre de plus haut niveau), vous pouvez définir plusieurs fonctions de gestion des erreurs du middleware, tout comme vous le feriez avec les fonctions régulières du middleware. Par exemple, pour définir un gestionnaire d’erreurs pour les requêtes faites en utilisant XHR et ceux sans :

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);

Dans cet exemple, les génériques logErrors peuvent écrire la requête et les informations d’erreur à stderr, par exemple :

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

Aussi dans cet exemple, clientErrorHandler est défini comme suit ; dans ce cas, l’erreur est explicitement passée au suivant.

Notice that when you do not call next in an error-handling function, you are responsible for writing (and ending) the response. Sinon, ces demandes seront « bloquées » et ne seront pas admissibles au ramassage des déchets.

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

Implémenter la fonction “catch-all” errorHandler comme suit (par exemple):

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

Si vous avez un gestionnaire de route avec plusieurs fonctions de rappel, vous pouvez utiliser le paramètre route pour passer au gestionnaire de route suivant. Par exemple :

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);
});
}
);

Dans cet exemple, le gestionnaire getPaidContent sera ignoré, mais tous les gestionnaires restants dans app pour /a_route_behind_paywall continueront d’être exécutés.

Les appels à next() et next(err) indiquent que le gestionnaire actuel est complet et dans quel état. next(err) sautera tous les gestionnaires restants de la chaîne, à l’exception de ceux qui sont configurés à gérer les erreurs comme décrit ci-dessus.