Zasady obsługi błędów dla aplikacji Node.js + Express.js?

177

Wygląda na to, że raportowanie / obsługa błędów przebiega inaczej w aplikacjach Node.js + Express.js w porównaniu do innych frameworków. Czy mam rację, rozumiejąc, że działa to w następujący sposób?

A) Wykryj błędy, otrzymując je jako parametry funkcji zwrotnych. Na przykład:

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

B) Zgłoś błędy w oprogramowaniu MIDDLEWARE, wywołując next (err). Przykład:

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

C) Zgłoś błędy w TRASACH, wyrzucając błąd. Przykład:

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

D) Uchwyt błędy przez konfigurowanie własnego obsługi błędów poprzez app.error () lub użyć rodzajowe obsługi błędów Connect. Przykład:

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

Czy te cztery zasady stanowią podstawę całej obsługi / raportowania błędów w aplikacjach Node.js + Express.js?

Clint Harris
źródło

Odpowiedzi:

183

Obsługa błędów w Node.js ma zazwyczaj format A). Większość wywołań zwrotnych zwraca obiekt błędu jako pierwszy argument lub null.

Express.js używa oprogramowania pośredniego, a składnia oprogramowania pośredniego używa B) i E) (wspomniane poniżej).

C) to zła praktyka, jeśli o mnie chodzi.

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

Możesz łatwo przepisać powyższe jako

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

W getżądaniu jest prawidłowa składnia oprogramowania pośredniczącego .

Jak dla D)

(19:26:37) tjholowaychuk: app.error jest usuwany w 3.x

TJ właśnie potwierdził, że app.errorjest to przestarzałe na korzyść E

MI)

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

Każde oprogramowanie pośredniczące o długości 4 (4 argumenty) jest uważane za błędne oprogramowanie pośredniczące. Gdy ktoś wywołuje next(err)połączenie, przechodzi i wywołuje oprogramowanie pośredniczące oparte na błędach.

Raynos
źródło
11
Dzięki! Dla każdego, kto może się z tym spotkać w przyszłości, wygląda na to, że kolejność parametrów dla „metody e” to w rzeczywistości err, req, res, next (zamiast req, res, next, err).
Clint Harris
9
Wygląda to świetnie, ale problem, który widzę, polega na tym, że niektóre błędy nigdy nie trafiają do opisanych przez ciebie procedur obsługi błędów i mogą zostać przechwycone tylko przez procedurę obsługi process.on ('uncaughtException', fn). Konwencjonalna mądrość polega na tym, aby pozwolić na to i polegać na Forever lub podobnym, aby ponownie uruchomić aplikację, ale jeśli to zrobisz, w jaki sposób zwrócisz przyjazną stronę błędu?
Paul
1
@chovy Również tylko fyi. Procedura obsługi błędów musi zostać przekazana do aplikacji po zgłoszonym / następnym błędzie. Jeśli jest wcześniej, błąd nie zostanie wychwycony.
Lee Olayvar
3
next (err) to zasadniczo wersja Express, która
generuje
1
@qodeninja Ta metoda jest uważana za najlepszą praktykę w programie Express.
David Oliveros,
11

Pracownicy firmy Joyent opublikowali w tej sprawie naprawdę wnikliwy dokument przedstawiający najlepsze praktyki . Artykuł obowiązkowy dla każdego programisty Node.js.

hthserhs
źródło
Świetny artykuł, poprawiono odsyłacz do zaktualizowanego dokumentu Joyent.
stephbu
2
artykuł niezły: ale za dużo tekstu i za mało przykładów to artykuł dla prawdziwych profesjonalistów
Gerd
3

Dlaczego pierwszy parametr?

Ze względu na asynchroniczną naturę node.js The pierwszy parametr-as-err wzór stał się uznanym konwencji dla obsługi błędów userland node.js . Dzieje się tak, ponieważ asynchroniczne:

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

Więc zamiast tego posiadanie pierwszego argumentu wywołania zwrotnego jest właściwie jedynym rozsądnym sposobem na asynchroniczne przekazywanie błędów, inny niż tylko ich zgłaszanie.

Spowoduje to unhandled exception, że w wyniku tego, tak jak to brzmi, nie zrobiono nic, aby wyprowadzić aplikację ze stanu pomieszania.

Wyjątki, dlaczego one istnieją

Warto jednak zauważyć, że praktycznie cała część Node.js to emitery zdarzeń, a zgłoszenie wyjątku jest zdarzeniem niskiego poziomu, które można obsłużyć jak wszystkie zdarzenia:

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

To może, ale nie powinno być doprowadzane do skrajności, aby wyłapać wszystkie błędy i stworzyć aplikację, która będzie bardzo się starała, aby nigdy się nie zawiesić. Jest to okropny pomysł w prawie każdym przypadku użycia, ponieważ pozostawi programistę bez pojęcia, co się dzieje w stanie aplikacji i jest analogiczne do pakowania main w try-catch.

Domeny - logiczne grupowanie zdarzeń

W ramach rozwiązywania problemu wyjątków powodujących przewracanie się aplikacji, domeny pozwalają deweloperowi wziąć na przykład aplikację Express.js i spróbować rozsądnie zamknąć połączenia w przypadku katastrofalnej awarii.

ES6

Prawdopodobnie wspomina, że ​​to się zmieni ponownie, ponieważ ES6 umożliwia wzorcowi generatora tworzenie zdarzeń asynchronicznych, które są nadal możliwe do złapania za pomocą bloków try / catch.

Koa (napisany przez TJ Holowaychucka, tego samego oryginalnego autora Express.js) wyraźnie to robi. Używa instrukcji ES6 yielddo tworzenia bloków, które wyglądają prawie synchronicznie, ale są obsługiwane w zwykły sposób asynchroniczny węzła:

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

Ten przykład został stąd bezwstydnie skradziony .

David
źródło