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.

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

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.