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.
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.});For errors returned from asynchronous functions invoked by route handlers
and middleware, you must pass them to the next() function, 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); } });});À partir de Express 5, les gestionnaires de route et les middleware qui retournent une Promise
appelleront automatiquement next(value) quand ils rejettent ou lancent une erreur.
Par exemple :
app.get('/user/:id', async (req, res, next) => { 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.
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 devez attraper les erreurs qui se produisent dans le code asynchrone invoqué par les gestionnaires de route ou middleware et les passer à Express pour le traitement. Par exemple :
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.
Utilise des promesses pour éviter les frais généraux du bloc essayer...catch ou lorsque tu utilises les fonctions
qui renvoient des promesses. Par exemple :
app.get('/', (req, res, next) => { Promise.resolve() .then(() => { throw new Error('BROKEN'); }) .catch(next); // Errors will be passed to Express.});Puisque les promesses attrapent automatiquement à la fois les erreurs synchrones et les promesses rejetées,
vous pouvez simplement fournir next car le gestionnaire de capture final et Express attrapera des erreurs,
parce que le gestionnaire de capture est donné l’erreur comme premier argument.
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); },]);L’exemple ci-dessus a quelques déclarations triviales de l’appel readFile
. 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.
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.statusCodeest défini à partir deerr.status(ouerr.statusCode). Si cette valeur est en dehors de la plage 4xx ou 5xx, elle sera définie à 500. - Le
res.statusMessageest 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:
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 :
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.
Notez que lorsque not appelez “next” dans une fonction de gestion des erreurs, vous êtes responsable de l’écriture (et de la fin) de la réponse. 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.