Gestion des erreurs
Fait partie de la section Les bases d’Express du Journey Node.js de Coddy. Leçon 18 sur 23.
Lorsqu’une route déclenche une erreur, Express répond avec sa propre page d’erreur, qui n’est pas au format JSON et affiche des détails qui vous sont destinés.
app.get('/api/fines/:member', (req, res) => {
throw new Error('Fines database is down');
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Try again later' });
});Un middleware à quatre arguments, (err, req, res, next), est un gestionnaire d’erreurs. Express compte les arguments : avec trois arguments, il s’agit d’un middleware ordinaire, et les erreurs le contournent. Lorsqu’une route déclenche une erreur, Express contourne les middlewares ordinaires et appelle le gestionnaire d’erreurs en lui transmettant l’erreur. Ajoutez-le en dernier, après le gestionnaire 404. Consignez les détails pour vous-même ; envoyez un court message au client.
throw new Error('text') interrompt la route et transmet l’erreur à Express, et appeler next(err) fait la même chose. Répondez avec le code 400 lorsque le visiteur a envoyé une information incorrecte qu’il peut corriger. Déclenchez une erreur lorsque le serveur est en cause.
Défi
FacileContinue avec Recipe Box. Tu peux ignorer les fichiers distincts practice-errors/ ; « Exécuter » démarre maintenant le serveur server.js du projet.
Les cuisiniers veulent pouvoir adapter les quantités d’une recette, par exemple doubler le nombre de portions pour une fête. Une recette enregistrée sans nombre de portions ne peut pas être adaptée. Il s’agit d’un problème dans tes données, et non de quelque chose que le visiteur peut corriger ; l’erreur est donc transmise à un gestionnaire qui renvoie une réponse JSON 500 claire.
- Dans
server.js, au-dessus du gestionnaire 404, ajoute une route pour/api/recipes/:id/scale. Elle lit le facteur dans la chaîne de requête, comme dans/api/recipes/1/scale?by=2. - Lorsqu’aucune recette ne possède cet identifiant, réponds avec le statut 404 et
{"error": "No such recipe"}. - Convertis
byen nombre. Si ce n’est pas un nombre, ou si sa valeur est égale ou inférieure à 0, réponds avec le statut 400 et{"error": "Scale by a positive number"}.Number('lots')donneNaN, etNumber.isNaNpermet de le détecter. - Si la recette n’a pas de
servings, lance une nouvelleErrordont le message estRecipe 5 has no servings, en remplaçant 5 par l’identifiant de cette recette. - Sinon, réponds avec un JSON contenant uniquement le titre et le nombre de portions adapté. La recette 1, Tomato Soup, a 4 portions ;
/api/recipes/1/scale?by=2renvoie donc{"title": "Tomato Soup", "servings": 8}. N’ajoute aucun autre champ. - À la fin des middlewares du fichier, après le gestionnaire 404, ajoute un gestionnaire d’erreurs : un
app.usedont la fonction prend quatre arguments,(err, req, res, next). Il affiche l’erreur avecconsole.erroret répond avec le statut 500 et{"error": "Something went wrong"}.
Essaie : appuie sur « Exécuter » et envoie /api/recipes/1/scale?by=2. Tu devrais voir {"title":"Tomato Soup","servings":8}. Envoie /api/recipes/2/scale?by=lots : statut 400 et {"error":"Scale by a positive number"}. Envoie /api/recipes/99/scale?by=2 : statut 404. Maintenant, sélectionne POST et envoie {"title": "Toast"} à /api/recipes. Elle devient la recette 5, sans nombre de portions. Envoie /api/recipes/5/scale?by=2 : statut 500 et {"error":"Something went wrong"}, et la console affiche l’erreur Recipe 5 has no servings.
Essayez vous-même
import { escapeHtml } from './html.js';
import express from 'express';
import { randomUUID } from 'node:crypto';
import { recipes } from './data.js';
const app = express();
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
app.use((req, res, next) => {
res.set('X-Request-Id', randomUUID());
next();
});
app.use(express.json());
app.use(express.static('public'));
app.get('/', (req, res) => {
const items = recipes.map((r) => `<li><a href="/recipes/${r.id}">${escapeHtml(r.title)}</a></li>`);
res.send(`<link rel="stylesheet" href="/style.css"><h1>Recipe Box</h1><ul>${items.join('')}</ul>`);
});
// type('text') envoie du texte brut, donc chaque titre apparaît sur sa propre ligne.
app.get('/recipes', (req, res) => {
const { tag } = req.query;
const list = tag ? recipes.filter((recipe) => recipe.tags.includes(tag)) : recipes;
res.type('text').send(list.map((recipe) => recipe.title).join('\n'));
});
app.get('/recipes/:id', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).send('No such recipe');
return;
}
res.send(recipe.title);
});
app.get('/api/recipes', (req, res) => {
res.json(recipes);
});
app.get('/api/recipes/:id', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).json({ error: 'No such recipe' });
return;
}
res.json(recipe);
});
app.post('/api/recipes', (req, res) => {
const { title, tags } = req.body;
if (!title) {
res.status(400).json({ error: 'A recipe needs a title' });
return;
}
const recipe = { id: recipes.length + 1, title, tags: tags ?? [] };
recipes.push(recipe);
res.status(201).json(recipe);
});
app.get('/about', (req, res) => {
res.send('Recipe Box collects family recipes.');
});
app.get('/menu', (req, res) => {
res.redirect(301, '/recipes');
});
app.get('/menu/:id', (req, res) => {
res.redirect(301, `/recipes/${req.params.id}`);
});
app.use((req, res) => {
if (req.path.startsWith('/api/')) {
res.status(404).json({ error: 'Not found' });
return;
}
res.status(404).send('<h1>Page not found</h1><p><a href="/">Back to Recipe Box</a></p>');
});
app.listen(3000, () => {
console.log('Recipe Box is running on port 3000');
});
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Les bases d’Express
1Votre premier serveur
Bonjour, ExpressLe module httpPlus de routesParamètres de route3Middleware
Votre premier middlewareEntraînez-vous par vous-même : Playground Express