Fehlerbehandlungsprinzipien für Node.js + Express.js-Anwendungen?

177

Es scheint, dass die Fehlerberichterstattung / -behandlung in Node.js + Express.js- Anwendungen anders erfolgt als in anderen Frameworks. Bin ich richtig im Verständnis, dass es wie folgt funktioniert?

A) Erkennen Sie Fehler, indem Sie sie als Parameter für Ihre Rückruffunktionen empfangen. Beispielsweise:

doSomethingAndRunCallback(function(err) { 
    if(err) {  }
});

B) Melden Sie Fehler in MIDDLEWARE, indem Sie next (err) aufrufen. Beispiel:

handleRequest(req, res, next) {
    // An error occurs…
    next(err);
}

C) Melden Sie Fehler in ROUTES, indem Sie den Fehler auslösen. Beispiel:

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

D) Behandeln Sie Fehler, indem Sie Ihren eigenen Fehlerbehandler über app.error () konfigurieren, oder verwenden Sie den generischen Connect-Fehlerbehandler. Beispiel:

app.error(function(err, req, res, next) {
    console.error(err);
    res.send('Fail Whale, yo.');
});

Sind diese vier Prinzipien die Grundlage für alle Fehlerbehandlungen / -berichte in Node.js + Express.js-Anwendungen?

Clint Harris
quelle

Antworten:

183

Die Fehlerbehandlung in Node.js hat im Allgemeinen das Format A). Die meisten Rückrufe geben ein Fehlerobjekt als erstes Argument oder zurück null.

Express.js verwendet Middleware und die Middleware-Syntax verwendet B) und E) (siehe unten).

C) ist eine schlechte Praxis, wenn Sie mich fragen.

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

Sie können die oben genannten leicht umschreiben als

app.get('/home', function(req, res, next) {
    // An error occurs
    next(err);
});

Die Middleware-Syntax ist in einer getAnforderung gültig .

Wie für D)

(07:26:37 PM) tjholowaychuk: app.error wurde in 3.x entfernt

TJ hat gerade bestätigt, dass dies app.errorzugunsten von E veraltet ist

E)

app.use(function(err, req, res, next) {
  // Only handle `next(err)` calls
});

Jede Middleware mit einer Länge von 4 (4 Argumente) wird als Fehler-Middleware betrachtet. Wenn man eine next(err)Verbindung aufruft, wird eine fehlerbasierte Middleware aufgerufen.

Raynos
quelle
11
Vielen Dank! Für jeden, der in Zukunft darauf stoßen könnte, sieht es so aus, als ob die Reihenfolge der Parameter für "Methode e" tatsächlich err, req, res, next ist (anstelle von req, res, next, err).
Clint Harris
9
Das sieht also gut aus, aber ich sehe ein Problem darin, dass einige Fehler niemals zu den von Ihnen beschriebenen Fehlerbehandlungsroutinen gelangen und nur von einem process.on-Handler ('uncaughtException', fn) abgefangen werden können. Die übliche Weisheit ist, dies zuzulassen und sich auf Forever oder ähnliches zu verlassen, um die App neu zu starten. Wenn Sie dies jedoch tun, wie können Sie eine freundliche Fehlerseite zurückgeben?
Paul
1
@chovy Auch nur ein Fyi. Der Fehlerbehandler muss nach dem ausgelösten / nächsten Fehler an die App übergeben werden. Wenn dies der Fall ist, wird der Fehler nicht abgefangen.
Lee Olayvar
3
next (err) ist im Wesentlichen die Express-Version des Auslösens eines Fehlers. Sie müssen ihn jedoch explizit in Ihrer eigenen Middleware
aufrufen
1
@qodeninja Diese Methode wird in Express als Best Practice angesehen.
David Oliveros
11

Die Mitarbeiter von Joyent haben hierzu ein wirklich aufschlussreiches Best-Practice-Dokument veröffentlicht . Ein Muss für jeden Node.js-Entwickler.

hthserhs
quelle
Großartiger Artikel, der Link wurde korrigiert, um auf Joyents aktualisiertes Dokument zu verweisen.
Stephbu
2
Artikel nicht schlecht: aber zu viel Text und zu wenig Beispiele Es ist ein Artikel für echte Profis
Gerd
3

Warum erster Parameter?

Aufgrund der asynchronen Natur von Node.js hat sich das Muster des ersten Parameters als Fehler als Konvention für die Fehlerbehandlung von Node.js im Benutzerland gut etabliert . Dies liegt daran, dass asynchron:

try {
    setTimeout(function() {
        throw 'something broke' //Some random error
    }, 5)
}
catch(e) {
   //Will never get caught
}

Stattdessen ist das erste Argument des Rückrufs so ziemlich die einzig sinnvolle Möglichkeit, Fehler asynchron zu übergeben, anstatt sie nur zu werfen.

Dies führt zu einem Ergebnis unhandled exception, das genau so klingt, wie es sich anhört, dass nichts unternommen wurde, um die Anwendung aus ihrem verwirrten Zustand zu bringen.

Ausnahmen, warum gibt es sie?

Es ist jedoch erwähnenswert, dass praktisch alle Teile von Node.js Ereignisemitter sind und das Auslösen einer Ausnahme ein Ereignis auf niedriger Ebene ist, das wie alle Ereignisse behandelt werden kann:

//This won't immediately crash if connection fails
var socket = require("net").createConnection(5000);
socket.on("error", function(err) {
    console.error("calm down...", err)
});

Dies kann, sollte aber nicht auf die Spitze getrieben werden, um alle Fehler abzufangen und eine Anwendung zu erstellen, die sich sehr bemüht, niemals abzustürzen. Dies ist in fast jedem Anwendungsfall eine schreckliche Idee, da der Entwickler keine Ahnung davon hat, was im Anwendungsstatus vor sich geht, und analog dazu ist, main in try-catch zu verpacken.

Domänen - Ereignisse logisch gruppieren

Im Rahmen der Bewältigung dieses Problems von Ausnahmen, bei denen Anwendungen umfallen, können Entwickler mithilfe von Domänen beispielsweise die Anwendung Express.js verwenden und versuchen, Verbindungen im Falle eines katastrophalen Ausfalls sinnvoll zu schließen.

ES6

Es wird wahrscheinlich erwähnt, dass sich dies erneut ändern wird, da ES6 es dem Generatormuster ermöglicht, asynchrone Ereignisse zu erstellen, die mit try / catch-Blöcken noch abfangbar sind.

Koa (geschrieben von TJ Holowaychuck, dem gleichen Originalautor von Express.js) tut dies merklich. Mit der ES6- yieldAnweisung werden Blöcke erstellt, die zwar nahezu synchron erscheinen, jedoch auf die übliche asynchrone Weise des Knotens behandelt werden:

app.use(function *(next) {
    try {
        yield next;
    } 
    catch (err) {
        this.status = err.status || 500;
        this.body = err.message;
        this.app.emit('error', err, this);
    }
});

app.use(function *(next) {
    throw new Error('some error');
})

Dieses Beispiel wurde von hier aus schamlos gestohlen .

David
quelle