Introduzione
Eccezioni ed errori sono destinati a verificarsi mentre gli utenti interagiscono con qualsiasi applicazione, spetta agli ingegneri del software scegliere un mezzo per gestire qualsiasi errore che potrebbe sorgere, consapevolmente o inconsapevolmente. Di conseguenza, gli sviluppatori back-end che creano API con Express si ritrovano a lavorare per assicurarsi di creare un'API utile, efficiente e utilizzabile. Ciò che è più importante è gestire gli errori in modo tale da costruire un sistema robusto perché questo aiuta a ridurre i tempi di sviluppo, gli errori veri e propri, i problemi di produttività e determina il successo o la scalabilità dello sviluppo del software.
È necessario registrare il messaggio di errore, sopprimere l'errore, informare gli utenti dell'errore o scrivere codice per gestire gli errori? Non chiederti più.
In questa guida, impareremo come creare una solida base di codice per la gestione degli errori per le applicazioni Express, che servirà per aiutare a rilevare gli errori dell'applicazione e intraprendere azioni ottimali per ripristinare qualsiasi applicazione da un errore normale durante il runtime.
Nota: Utilizzeremo Postman per testare l'API nella nostra demo. Puoi scaricarlo sul Pagina di download del postino. In alternativa, puoi semplicemente utilizzare il browser, la riga di comando curl
strumento o qualsiasi altro strumento con cui potresti avere familiarità.
Cos'è la gestione degli errori?
Nello sviluppo del software, ci sono due diversi tipi di eccezioni: operativa ed programmatica.
- Potrebbero verificarsi errori operativi durante il runtime e, per evitare che l'applicazione si interrompa bruscamente, dobbiamo gestire con garbo queste eccezioni attraverso metodi di gestione degli errori efficienti.
- Le eccezioni programmatiche vengono lanciate manualmente da un programmatore, quando si verifica uno stato eccezionale.
Puoi pensare alle eccezioni operative come eccezioni "impreviste, ma previste" (come l'accesso a un indice fuori dai limiti) e alle eccezioni programmatiche come eccezioni "previste e previste" (come un'eccezione di formattazione dei numeri).
La gestione delle eccezioni è la procedura utilizzata per trovare e correggere i difetti all'interno di un programma. La gestione degli errori invia messaggi che includono il tipo di errore verificatosi e lo stack in cui si è verificato l'errore.
Nota: In informatica, le eccezioni sono recuperabili e in genere derivano da problemi operativi o programmatici durante il runtime. Gli errori in genere derivano da fattori esterni, come limitazioni hardware, problemi di connettività, mancanza di memoria, ecc. In JavaScript, i termini sono spesso usati in modo intercambiabile e le eccezioni personalizzate derivano dal Error
classe. Il Error
class stessa rappresenta sia gli errori che le eccezioni.
In Express, la gestione delle eccezioni si riferisce al modo in cui Express si configura per rilevare ed elaborare le eccezioni sincrone e asincrone. L'aspetto positivo della gestione delle eccezioni in Express è che come sviluppatore non è necessario scrivere i propri gestori di eccezioni; Express viene fornito con un gestore di eccezioni predefinito. Il gestore delle eccezioni aiuta a identificare gli errori e a segnalarli all'utente. Fornisce inoltre varie strategie correttive e le implementa per mitigare le eccezioni.
Sebbene queste possano sembrare molte cose nascoste, la gestione delle eccezioni in Express non rallenta il processo complessivo di un programma né ne sospende l'esecuzione.
Comprensione della gestione delle eccezioni in Express
Con il gestore degli errori predefinito fornito con Express, abbiamo in mano una serie di funzioni middleware che aiutano a rilevare automaticamente gli errori nei gestori di route. Presto creeremo un progetto per mettere in pratica la teoria su come restituire errori corretti in un'app Express e su come non far trapelare informazioni sensibili.
Definizione della funzione middleware in Express
Le funzioni middleware di gestione degli errori sono definite in modo tale da accettare un Error
object come primo parametro di input, seguito dai parametri predefiniti di qualsiasi altra funzione middleware: request
, response
e next
. next()
La funzione salta tutto il middleware corrente al gestore degli errori successivo per il router.
Impostazione della gestione degli errori in Express
Esegui il seguente comando nel tuo terminale per creare un'app Node and Express:
$ mkdir error-handling-express
Nella cartella appena creata, inizializziamo un nuovo progetto Node:
$ cd error-handling-express && npm init -y
Questo crea a package.json
file nella nostra cartella.
Per creare un server Express nella nostra app Node, dobbiamo installare il file express
pacchetto, dotenv
per caricare automaticamente le variabili di ambiente in .env
file in process.env
oggetto, e nodemon
per riavviare l'app del nodo se viene annotata una modifica del file nella directory.
$ npm install express dotenv nodemon
Quindi, crea un app.js
file nella cartella del progetto che fungerà da file di indice per l'app.
Ora che abbiamo installato tutte le dipendenze necessarie per la nostra app Express, dobbiamo impostare lo script per leggere l'app nel package.json
file. Per raggiungere questo obiettivo, il package.json
file, in modo che il scripts
oggetto è come mostrato di seguito:
"scripts": {
"start": "nodemon app.js"
},
In alternativa, puoi saltare l'uso nodemon
e utilizzare node app.js
anziché.
Configurazione di un server Express
Per configurare il server, dobbiamo prima importare i vari pacchetti in app.js
. Creeremo anche un .env
file nella directory del progetto - per memorizzare tutte le variabili di ambiente per l'applicazione:
const express = require('express')
require('dotenv').config
PORT=4000
Abbiamo definito il numero di porta per l'app in .env
, che viene caricato e letto da dotenv
, ed è possibile accedervi successivamente.
Inizializzazione dell'Express Server
Ora, dobbiamo inizializzare il server Express e fare in modo che la nostra app ascolti il numero di porta dell'app, insieme a una richiesta a un percorso di prova: /test
. Aggiorniamo app.js
, sotto le dichiarazioni di importazione:
const app = express();
const port = process.env.PORT || 4000;
app.get("/test", async (req, res) => {
return res.status(200).json({ success: true });
});
app.listen(port, () => {
console.log(`Server is running at port ${port}`);
});
Da qui in poi impareremo come gestire i vari casi d'uso di errori operativi che si possono incontrare in Express.
Gestione degli errori non trovati in Express
Supponiamo di dover recuperare tutti gli utenti da un database di utenti, è possibile gestire in modo efficiente un potenziale scenario di errore in cui non esistono dati nel database, avvolgendo la logica in un try/catch
block - sperando di rilevare qualsiasi errore che potrebbe proiettare nel file catch
bloccare:
const getUser = () => undefined;
app.get("/get-user", async (req, res) => {
try {
const user = getUser();
if (!user) {
throw new Error('User not found');
}
} catch (error) {
console.log(error);
res.status(400).send(error.message)
}
return res.status(200).json({
success: true
});
});
Questo risulta in:
User not found
Ora, quando viene effettuata questa richiesta (puoi testare usando Postman) e nessun utente esiste nel database, il client riceve un messaggio di errore che dice "Utente non trovato". Inoltre, noterai che l'errore viene registrato anche nella console.
Ottimizzazione della gestione degli errori con il middleware del gestore degli errori
Possiamo ottimizzare lo sviluppo creando un middleware di gestione degli errori che verrebbe alla fine di tutti i percorsi definiti, in modo che se viene generato un errore in uno dei percorsi, Express darà automaticamente un'occhiata al middleware successivo e continuerà a scorrere l'elenco finché non raggiunge il gestore degli errori. Il gestore degli errori elaborerà l'errore e invierà anche una risposta al client.
Dai un'occhiata alla nostra guida pratica e pratica per l'apprendimento di Git, con le migliori pratiche, gli standard accettati dal settore e il cheat sheet incluso. Smetti di cercare su Google i comandi Git e in realtà imparare esso!
Per iniziare, crea una cartella chiamata middleware
nella directory del progetto, e in questa cartella, crea un file chiamato errorHandler.js
che definisce il gestore degli errori:
const errorHandler = (error, req, res, next) => {
console.log(error);
res.status(400).send(error.message);
}
module.exports = errorHandler;
Nella nostra funzione middleware, abbiamo reso Express consapevole che questa non è una funzione middleware di base, ma un gestore di errori, aggiungendo il error
parametro prima dei 3 parametri di base.
Ora useremo il gestore degli errori nella nostra demo app.js
e gestire l'errore iniziale di recupero degli utenti con il middleware del gestore degli errori, come mostrato di seguito:
const getUser = () => undefined;
app.get("/get-user", async (req, res, next) => {
try {
const user = getUser();
if (!user) {
throw new Error("User not found");
}
} catch (error) {
return next(error);
}
});
app.use(errorHandler);
Possiamo ottimizzare ancora di più il nostro codice, creando un abtsraction attorno al try/catch
logica. Possiamo ottenere ciò creando una nuova cartella nella directory del progetto chiamata utils
, e in esso, crea un file chiamato tryCatch.js
.
Per astrarre il try-catch
logica: possiamo definire una funzione che accetta un'altra funzione (nota come controllore) come parametro e restituisce an async
funzione che manterrà a try/catch
per qualsiasi controller ricevuto.
Se si verifica un errore nel controller, viene rilevato nel file catch
block e viene chiamata la funzione successiva:
const tryCatch = (controller) => async (req, res, next) => {
try {
await controller(req, res);
} catch (error) {
return next(error);
}
};
module.exports = tryCatch;
Grazie alla try/catch
astrazione, possiamo eseguire il refactoring del nostro codice per renderlo più succinto saltando il file try-catch
clausola in modo esplicito durante il recupero degli utenti in app.js
:
const getUser = () => undefined;
app.get(
"/get-user",
tryCatch(async (req, res) => {
const user = getUser();
if (!user) {
throw new Error("User not found");
}
res.status(400).send(error.message);
})
);
Abbiamo estratto con successo la logica try-catch e il nostro codice funziona ancora come prima.
Gestione degli errori di convalida in Express
Per questa demo, creeremo un nuovo percorso nella nostra app Express per l'accesso, per convalidare un ID utente al momento dell'accesso. Innanzitutto, installeremo il joi
pacchetto, per aiutare con la creazione di uno schema, con il quale possiamo applicare i requisiti:
$ npm i joi
Quindi, crea uno schema che è a Joi.object
con una userId
che deve essere un numero ed è obbligatorio, il che significa che la richiesta deve corrispondere a un oggetto con un ID utente su di esso.
Possiamo usare il validate()
metodo nell'oggetto schema per convalidare ogni input rispetto allo schema:
const schema = Joi.object({
userId: Joi.number().required(),
});
app.post(
"/login",
tryCatch(async (req, res) => {
const {error, value} = schema.validate({});
if (error) throw error;
})
);
Se un oggetto vuoto viene passato nel file validate()
metodo, l'errore verrebbe gestito con garbo e il messaggio di errore verrebbe inviato al client:
Sulla console, abbiamo anche accesso a a details
array che include vari dettagli sull'errore che potrebbero essere comunicati all'utente se necessario.
Per gestire in modo specifico gli errori di convalida in modo tale da passare il dettaglio dell'errore appropriato per errore di convalida, è possibile eseguire il refactoring del middleware del gestore degli errori:
const errorHandler = (error, req, res, next) => {
console.log(error);
if (error.name === "ValidationError") {
return res.status(400).send({
type: "ValidationError",
details: error.details,
});
}
res.status(400).send(error.message);
};
module.exports = errorHandler;
Con errorHandler.js
ora personalizzato, quando facciamo la stessa richiesta con un oggetto vuoto passato al validate()
Metodo:
Ora abbiamo accesso a un oggetto personalizzato che restituisce i messaggi in modo più leggibile/amichevole. In questo modo siamo in grado di inviare e gestire diversi tipi di errore in base al tipo di errore in arrivo.
Conclusione
In questa guida, abbiamo esaminato ogni aspetto della gestione degli errori di Express.js, incluso il modo in cui il codice sincrono e asincrono viene gestito per impostazione predefinita, come creare le proprie classi di errore, come scrivere funzioni middleware personalizzate per la gestione degli errori e fornire next
come responsabile finale delle catture
Come per ogni attività là fuori, ci sono anche best practice durante lo sviluppo che includono un'efficace gestione degli errori, e oggi abbiamo imparato come possiamo gestire gli errori in un'app Express in modo affidabile.
Gestire correttamente gli errori non significa solo ridurre i tempi di sviluppo trovando facilmente bug ed errori, ma anche sviluppare una solida base di codice per applicazioni su larga scala. In questa guida abbiamo visto come configurare il middleware per la gestione degli errori operativi. Alcuni altri modi per migliorare la gestione degli errori includono: non inviare tracce dello stack, arrestare i processi con garbo per gestire le eccezioni non rilevate, fornire messaggi di errore appropriati, inviare registri degli errori e impostare una classe che estenda il Error
classe.
Spero che gli esempi che ho usato in questo tutorial siano stati piacevoli per te. Ho coperto vari scenari che potresti potenzialmente incontrare durante la scrittura di un'applicazione Express da utilizzare nel mondo reale sulla gestione degli errori. Per favore, fammi sapere se c'è qualcosa che mi sono perso. Ci avvantaggerà e aiuterà anche me a saperne di più. Buona giornata e grazie per aver letto.
Puoi fare riferimento a tutto il codice sorgente utilizzato nell'articolo su Github.