Il mio caso d'uso è l'invio di un messaggio di errore JSON personalizzato, poiché utilizzo Express per alimentare la mia API REST. Penso che questo sia uno scenario abbastanza comune, quindi mi concentrerò su quello nella mia risposta.
Versione breve:
Gestione rapida degli errori
Definisci il middleware di gestione degli errori come un altro middleware, tranne con quattro argomenti invece di tre, in particolare con la firma (err, req, res, next). ... Definisci il middleware di gestione degli errori per ultimo, dopo altre chiamate app.use () e instradati
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
});
Genera errori da qualsiasi punto del codice eseguendo:
var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Versione lunga
Il modo canonico di lanciare errori è:
var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)
Per impostazione predefinita, Express gestisce questo impacchettandolo ordinatamente come una risposta HTTP con codice 404 e il corpo costituito dalla stringa del messaggio aggiunta con un'analisi dello stack.
Questo non funziona per me quando utilizzo Express come server REST, ad esempio. Voglio che l'errore venga restituito come JSON, non come HTML. Inoltre, sicuramente non voglio che la mia traccia dello stack venga trasferita al mio client.
Posso inviare JSON come risposta utilizzando req.json()
, ad es. qualcosa di simile req.json({ status: 404, message: 'Uh oh! Can't find something'})
. Facoltativamente, posso impostare il codice di stato utilizzando req.status()
. Combinando i due:
req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});
Funziona come un fascino. Detto questo, trovo piuttosto ingombrante digitare ogni volta che ho un errore e il codice non si documenta più come il nostro next(err)
. Sembra troppo simile al modo in cui viene inviato un JSON di risposta normale (cioè valido). Inoltre, qualsiasi errore generato dall'approccio canonico risulta comunque in output HTML.
È qui che entra in gioco il middleware di gestione degli errori di Express. Come parte dei miei percorsi, definisco:
app.use(function(err, req, res, next) {
console.log('Someone tried to throw an error response');
});
Ho anche sottoclasse Error in una classe JSONError personalizzata:
JSONError = function (status, message) {
Error.prototype.constructor.call(this, status + ': ' + message);
this.status = status;
this.message = message;
};
JSONError.prototype = Object.create(Error);
JSONError.prototype.constructor = JSONError;
Ora, quando voglio lanciare un errore nel codice, faccio:
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Tornando al middleware di gestione degli errori personalizzato, lo modifico in:
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
}
La creazione di sottoclassi di errore in JSONError è importante, poiché sospetto che Express esegua un instanceof Error
controllo sul primo parametro passato a a next()
per determinare se è necessario richiamare un gestore normale o un gestore di errori. Posso rimuovere il instanceof JSONError
controllo e apportare piccole modifiche per garantire che anche errori imprevisti (come un arresto anomalo) restituiscano una risposta JSON.