Mon cas d'utilisation est l'envoi d'un message d'erreur JSON personnalisé, puisque j'utilise express pour alimenter mon API REST. Je pense qu'il s'agit d'un scénario assez courant, je me concentrerai donc sur ce point dans ma réponse.
Version courte :
Gestion des erreurs express
Définir un intergiciel de gestion des erreurs comme les autres intergiciels, mais avec quatre arguments au lieu de trois, notamment avec la signature (err, req, res, next). ... Vous définissez l'intergiciel de gestion des erreurs en dernier, après les autres appels à app.use() et routes
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
});
Soulever des erreurs à partir de n'importe quel point du code en faisant :
var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Version longue
La façon canonique de lancer des erreurs est :
var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)
Par défaut, Express gère cette situation en la présentant comme une réponse HTTP avec un code 404 et un corps constitué de la chaîne de messages accompagnée d'un suivi de pile.
Cela ne me convient pas lorsque j'utilise Express comme serveur REST, par exemple. Je veux que l'erreur soit renvoyée en JSON, et non en HTML. De plus, je ne veux absolument pas que ma trace de pile soit envoyée à mon client.
Je peux envoyer JSON comme réponse en utilisant req.json()
par exemple, quelque chose comme req.json({ status: 404, message: 'Uh oh! Can't find something'})
. En option, je peux définir le code d'état en utilisant req.status()
. La combinaison des deux :
req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});
Cela fonctionne comme un charme. Cela dit, je trouve que c'est assez lourd à taper à chaque fois que j'ai une erreur, et le code n'est plus auto-documenté comme notre next(err)
était. Il ressemble beaucoup trop à la façon dont une réponse JSON normale (c'est-à-dire valide) est envoyée. De plus, toute erreur provoquée par l'approche canonique entraîne toujours une sortie HTML.
C'est là qu'intervient le middleware de gestion des erreurs d'Express. Dans le cadre de mes routes, je définis :
app.use(function(err, req, res, next) {
console.log('Someone tried to throw an error response');
});
Je sous-classe également Error dans une classe JSONError personnalisée :
JSONError = function (status, message) {
Error.prototype.constructor.call(this, status + ': ' + message);
this.status = status;
this.message = message;
};
JSONError.prototype = Object.create(Error);
JSONError.prototype.constructor = JSONError;
Maintenant, quand je veux lancer une erreur dans le code, je le fais :
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Pour revenir au middleware de gestion des erreurs, je le modifie comme suit :
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
}
La sous-classification de Error en JSONError est importante, car je soupçonne qu'Express fait un instanceof Error
sur le premier paramètre passé à un next()
pour déterminer si un gestionnaire normal ou un gestionnaire d'erreur doit être invoqué. Je peux supprimer le instanceof JSONError
et apporte des modifications mineures pour s'assurer que les erreurs inattendues (comme un crash) renvoient également une réponse JSON.