Federico Lelli / SchoolNode.js & Express
LEZIONE 30 · Affidabilità

Validazione e gestione degli errori

Prerequisiti: lezioni 19–22 e 25. Definisci un contratto verificabile anche quando il client sbaglia.

Dal formato al significato

Parsing
Trasforma il testo JSON in valori JavaScript; può fallire prima della route se il testo è malformato.
Validazione
Verifica forma, tipi, intervalli e regole del dato. La stringa vuota e un array sono JSON validi ma possono essere input non ammessi.
Normalizzazione
Converte input accettati in una forma uniforme, come un nome senza spazi esterni. Va documentata: non deve nascondere errori o trasformare qualunque valore in una stringa.
Errore atteso e inatteso
Un errore atteso, come un campo assente, può essere spiegato al client. Un errore inatteso richiede un log interno e una risposta che non esponga stack, credenziali o dettagli del filesystem.

Una funzione indipendente da Express

Salva come persona.mjs. È riutilizzata nella prossima lezione.

export function validaPersona(body) {
  if (!body || typeof body !== 'object' || Array.isArray(body)) {
    return { ok: false, error: 'È richiesto un oggetto' };
  }
  if (typeof body.nome !== 'string' ||
      body.nome.trim().length === 0 || body.nome.trim().length > 80) {
    return { ok: false, error: 'nome: da 1 a 80 caratteri' };
  }
  if (!Number.isInteger(body.eta) || body.eta < 0 || body.eta > 130) {
    return { ok: false, error: 'eta: intero da 0 a 130' };
  }
  return { ok: true, value: { nome: body.nome.trim(), eta: body.eta } };
}

La funzione non modifica il body e seleziona esplicitamente i campi ammessi. eta: "18" viene rifiutato: il contratto richiede un numero JSON. La lunghezza delle stringhe JavaScript misura unità UTF-16, non sempre caratteri percepiti dall’utente; chiarisci il requisito se gestisci nomi internazionali.

Collegarla a Express 5

In una cartella di esercizio esegui npm init -y e npm install express@5. Salva questo file come server.mjs accanto a persona.mjs; avvia con node server.mjs.

import express from 'express';
import { randomUUID } from 'node:crypto';
import { validaPersona } from './persona.mjs';
const app = express();
app.use(express.json({ limit: '16kb' }));
app.post('/persone', (req, res) => {
  const esito = validaPersona(req.body);
  if (!esito.ok) return res.status(400).json({ error: esito.error });
  // Dimostrazione del contratto: non salva ancora la persona.
  return res.status(201).json({ id: randomUUID(), ...esito.value });
});
app.use((req, res) => res.status(404).json({ error: 'Route assente' }));
app.use((err, req, res, next) => {
  if (res.headersSent) return next(err);
  if (err.type === 'entity.parse.failed') {
    return res.status(400).json({ error: 'JSON malformato' });
  }
  if (err.type === 'entity.too.large') {
    return res.status(413).json({ error: 'Body troppo grande' });
  }
  console.error(err);
  return res.status(500).json({ error: 'Errore interno' });
});
app.listen(3000, '127.0.0.1');

Matrice di verifica

Body di POST /personeRisposta attesa
{"nome":" Ada ","eta":20}201, nome normalizzato Ada e ID presente
{"nome":" ","eta":20}400
null oppure []400
{"nome":"Ada","eta":"20"}400
JSON incompleto400, JSON malformato

Invia Content-Type: application/json. Interrompi il server con Ctrl+C dopo le prove. La validazione non sostituisce autenticazione e autorizzazione.

Per i gestori asincroni, Express 5 propaga il rifiuto delle Promise restituite; callback esterne richiedono inoltro esplicito. Documentazione Express sugli errori.

Materiali didattici curati e rielaborati da Federico Lelli. Le fonti e i riferimenti specifici sono indicati nei materiali disponibili.