Menu
CoddyTech

Gestione degli errori

Fa parte della sezione Fondamenti di Express del percorso Node.js di Coddy. Lezione 15 di 20.

Quando una route genera un errore, Express risponde con la propria pagina di errore, che non è JSON e mostra dettagli destinati a 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 ignorano. Quando una route genera un errore, Express ignora 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 messaggio breve.

Un valore ?by= non valido è un errore del visitatore, quindi restituisce comunque un 400. Un errore riguarda qualcosa che il visitatore non può correggere, come una ricetta salvata senza il numero di porzioni.

challenge icon

Sfida

Facile
  • GET /api/recipes/:id/scale?by=2 restituisce { "title", "servings" }, con le porzioni moltiplicate.
  • Un fattore che non è un numero positivo: 400 con { "error": "Scale by a positive number" }.
  • Una ricetta senza servings genera Recipe <id> has no servings.
  • Un gestore degli errori registra l'errore e restituisce 500 con { "error": "Something went wrong" }.

Invia ?by=2 e ?by=lots. Poi invia { "title": "Toast" } a /api/recipes e ridimensiona la ricetta 5.

Provalo tu

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'));

const escapeHtml = (text) =>
    text
        .replaceAll('&', '&amp;')
        .replaceAll('<', '&lt;')
        .replaceAll('>', '&gt;')
        .replaceAll('"', '&quot;');

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 normale, 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');
});
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Fondamenti di Express