Gestione degli errori
Fa parte della sezione Fondamenti di Express del percorso Node.js di Coddy. Lezione 18 di 23.
Quando una rotta genera un errore, Express risponde con una propria pagina di errore, che non è JSON e mostra dettagli pensati per te.
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 con quattro argomenti, (err, req, res, next), è un gestore degli errori. Express conta gli argomenti: con tre è un middleware ordinario e gli errori lo saltano. Quando una rotta genera un errore, Express salta il middleware ordinario e chiama il gestore degli errori passandogli l’errore. Aggiungilo per ultimo, dopo il gestore 404. Registra i dettagli per te; invia al client un breve messaggio.
throw new Error('text') interrompe la rotta e passa l’errore a Express, e chiamare next(err) fa lo stesso. Rispondi con 400 quando il visitatore ha inviato qualcosa di sbagliato e può correggerlo. Genera un errore quando la responsabilità è del server.
Sfida
FacileContinua con Recipe Box. Puoi ignorare i file separati practice-errors/; ora Run avvia il server.js del progetto.
Chi cucina vuole ridimensionare una ricetta, per esempio raddoppiare le porzioni per una festa. Una ricetta salvata senza porzioni non può essere ridimensionata. È un errore nei tuoi dati, non qualcosa che chi visita il sito può correggere, quindi viene gestito da un gestore degli errori che invia una risposta JSON 500 pulita.
- In
server.js, sopra il gestore 404, aggiungi una route per/api/recipes/:id/scale. Legge il fattore dalla stringa di query, come in/api/recipes/1/scale?by=2. - Quando non esiste una ricetta con quell'id, rispondi con lo stato 404 e
{"error": "No such recipe"}. - Converti
byin un numero. Se non è un numero, oppure è 0 o inferiore, rispondi con lo stato 400 e{"error": "Scale by a positive number"}.Number('lots')èNaNeNumber.isNaNlo rileva. - Quando la ricetta non ha
servings, genera un nuovoErroril cui messaggio èRecipe 5 has no servings, sostituendo 5 con l'id della ricetta. - Altrimenti, rispondi con un JSON che contiene solo il titolo e le porzioni ridimensionate. La ricetta 1, Tomato Soup, ha 4 porzioni, quindi
/api/recipes/1/scale?by=2invia{"title": "Tomato Soup", "servings": 8}. Non aggiungere altri campi. - Alla fine del middleware del file, dopo il gestore 404, aggiungi un gestore degli errori: un
app.usela cui funzione accetta quattro argomenti,(err, req, res, next). Stampa l'errore conconsole.errore rispondi con lo stato 500 e{"error": "Something went wrong"}.
Prova: premi Run e invia /api/recipes/1/scale?by=2. Dovresti vedere {"title":"Tomato Soup","servings":8}. Invia /api/recipes/2/scale?by=lots: stato 400 e {"error":"Scale by a positive number"}. Invia /api/recipes/99/scale?by=2: stato 404. Ora scegli POST e invia {"title": "Toast"} a /api/recipes. Diventa la ricetta 5, senza porzioni. Invia /api/recipes/5/scale?by=2: stato 500 e {"error":"Something went wrong"}, e la Console mostra l'errore Recipe 5 has no servings.
Provalo tu
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') invia testo semplice, quindi ogni titolo appare su una riga a sé.
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');
});
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Fondamenti di Express
Esercitati da solo: Playground Express