2012-04-30 11 views
22

Ho esaminato la documentazione di Express e lo the part describing error handling è completamente opaco per me.Come gestire le eccezioni di codice in node.js?

Ho pensato che il app a cui si riferiscono è un'istanza createServer(), giusto? Ma non ho idea di come impedire a node.js di far esplodere il processo dell'applicazione quando si verifica un'eccezione durante la gestione di una richiesta.

Non ho bisogno di niente di speciale; Voglio solo restituire uno stato di 500, più una risposta altrimenti vuota, ogni volta che c'è un'eccezione. Il processo del nodo non deve terminare solo perché da qualche parte c'era un'eccezione non rilevata.

Esiste un semplice esempio di come ottenere ciò?


var express = require('express'); 
var http = require('http'); 

var app = express.createServer(); 

app.get('/', function(req, res){ 
    console.log("debug", "calling") 
    var options = { 
     host: 'www.google.com', 
     port: 80, 
     path: "/" 
    }; 
    http.get(options, function(response) { 
     response.on("data", function(chunk) { 
      console.log("data: " + chunk); 
      chunk.call(); // no such method; throws here 
     }); 
    }).on('error', function(e) { 
     console.log("error connecting" + e.message); 
    }); 
}); 

app.configure(function(){ 
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
}); 

app.listen(3000); 

si blocca l'intera applicazione, producendo traceback

mypath/tst.js:16 
      chunk.call(); // no such method; throws here 
       ^TypeError: Object ... has no method 'call' 
    at IncomingMessage.<anonymous> (/Library/WebServer/Documents/discovery/tst.js:16:18) 
    at IncomingMessage.emit (events.js:67:17) 
    at HTTPParser.onBody (http.js:115:23) 
    at Socket.ondata (http.js:1150:24) 
    at TCP.onread (net.js:374:27) 
+0

'solo perché ci fu un'eccezione non rilevata somewhere.' Il processo *** *** saranno morire se c'è un'eccezione non rilevata * *. Se non si desidera che si interrompa quando si verifica un'eccezione, rilevare l'eccezione e restituire l'errore 500. – Chad

+2

Potresti essere interessato al modo non espresso: http://stackoverflow.com/questions/4213351/make-node-js-not-exit-on-error – Matt

+0

Eccellente, grazie mille @Matt! – user124114

risposta

2

È possibile utilizzare il gestore di errore predefinito che esprimono usi, che è in realtà connect error handler.

var app = require('express').createServer(); 

app.get('/', function(req, res){ 
    throw new Error('Error thrown here!'); 
}); 

app.configure(function(){ 
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
}); 

app.listen(3000); 

Aggiornamento Per il vostro codice, è in realtà bisogno di catturare l'errore e passarlo ad esprimere in questo modo

var express = require('express'); 
var http = require('http'); 

var app = express.createServer(); 

app.get('/', function (req, res, next) { 
    console.log("debug", "calling"); 
    var options = { 
    host:'www.google.com', 
    port:80, 
    path:"/" 
    }; 
    http.get(options, 
    function (response) { 
     response.on("data", function (chunk) { 
     try { 
      console.log("data: " + chunk); 
      chunk.call(); // no such method; throws here 

     } 
     catch (err) { 
      return next(err); 
     } 
     }); 
    }).on('error', function (e) { 
     console.log("error connecting" + e.message); 
    }); 
}); 

app.configure(function() { 
    app.use(express.errorHandler({ dumpExceptions:true, showStack:true })); 
}); 

app.listen(3000); 
+0

Il tuo esempio funziona (dopo aver aggiunto 'var express = require ('express');'), ma sfortunatamente quando faccio la stessa cosa nel mio codice si blocca ancora il processo. Ho aggiornato la domanda con un codice di esempio. – user124114

+0

Ho aggiornato la risposta mostrando come catturare l'errore e passarlo per esprimere usando next(). – 250R

+0

Grazie @ 250R! Sfortunatamente non posso permettermi di sparpagliare la logica del codice ovunque con tali costrutti, quindi andrò con la soluzione Matt collegata, nei commenti sopra. – user124114

38

Se si vuole veramente a catturare tutte le eccezioni e fornire qualche manipolazione oltre a uscendo dal processo Node.js, è necessario gestire l'evento uncaughtException del Node.

Se ci pensate, questa è una cosa Node, e non una cosa Express, perché se si lancia un'eccezione da qualche pezzo di codice arbitrario, non c'è garanzia che Express possa o lo vedrà mai, o che si trovi in ​​una posizione per intrappolarlo. (Perché? Le eccezioni non interagiscono molto bene con il codice callbacky asincrono basato sugli eventi che è lo stile del nodo.Le eccezioni risalgono allo stack di chiamate per trovare un blocco catch() che è nel campo di applicazione nel momento in cui viene generata l'eccezione.Se lo myFunction defeca un po 'di lavoro a una funzione di callback che viene eseguita quando si verifica un evento, quindi torna al ciclo degli eventi, quindi quando viene richiamata la funzione di callback, viene richiamata direttamente dal ciclo degli eventi principale e myFunction non è più nello stack delle chiamate; un'eccezione, anche se myFunction ha un blocco try/catch, non attirerà l'eccezione.)

Ciò significa in pratica che se si lancia un'eccezione e non la si cattura da sé e lo si fa in una funzione che è stata chiamata direttamente da Express, Express può catturare l'eccezione n e chiama il gestore degli errori che hai installato, supponendo che tu abbia configurato parte del middleware di gestione degli errori come app.use(express.errorHandler()). Ma se si lancia la stessa eccezione in una funzione chiamata in risposta a un evento asincrono, Express non sarà in grado di catturarla. (L'unico modo in cui potrebbe capirlo è ascoltando l'evento Node globale uncaughtException, che sarebbe una cattiva idea prima perché è globale e potrebbe essere necessario usarlo per altre cose, e secondo perché Express non ha idea di quale richiesta fosse associato con l'eccezione.)

Ecco un esempio.Aggiungo questo frammento di codice route-trattamento per un app Express esistente:

app.get('/fail/sync', function(req, res) { 
    throw new Error('whoops'); 
}); 
app.get('/fail/async', function(req, res) { 
    process.nextTick(function() { 
     throw new Error('whoops'); 
    }); 
}); 

Ora, se visito http://localhost:3000/fail/sync nel mio browser, il browser discariche uno stack di chiamate (mostrando express.errorHandler in azione). Se visito lo http://localhost:3000/fail/async nel mio browser, tuttavia, il browser si arrabbia (Chrome mostra un messaggio "Nessun dato ricevuto: Errore 324, rete :: ERR_EMPTY_RESPONSE: il server ha chiuso la connessione senza inviare alcun dato"), perché il processo del nodo è terminato , mostrando un backtrace su stdout nel terminale in cui l'ho invocato.

+5

Questa è una risposta davvero eccellente perché non è solo illuminante nella sua chiarezza su come express cattura le eccezioni e le gestisce con middleware degli errori (che è un comportamento predefinito imprevisto), ma anche perché spiega come usare 'throw new Error (" ... ")" nelle funzioni asincrone non attiverà il middleware degli errori e infatti non verrà nemmeno catturato da alcun gestore di eccezioni che avvolge la funzione asincrona. Non puoi trovare queste informazioni da nessun'altra parte. – fthinker

+2

L'utilizzo di [domini] (http://nodejs.org/api/domain.html) rappresenta un'alternativa migliore alla cattura di 'uncaughtException'. – James

+2

I domini sono in fase di deprecazione. Qual è l'API alternativa? –

10

Per essere in grado di rilevare errori asincroni, utilizzo il dominio. Con Express è possibile provare questo codice:

function domainWrapper() { 
    return function (req, res, next) { 
     var reqDomain = domain.create(); 
     reqDomain.add(req); 
     reqDomain.add(res); 

     res.on('close', function() { 
      reqDomain.dispose(); 
     }); 
     reqDomain.on('error', function (err) { 
      next(err);    
     }); 
     reqDomain.run(next) 
    } 
} 
app.use(domainWrapper()); 
//all your other app.use 
app.use(express.errorHandler()); 

Questo codice renderà il vostro errore asincrono essere catturato e inviato al gestore degli errori. In questo esempio io uso express.errorHandler, ma funziona con qualsiasi gestore.

Per maggiori informazioni sul dominio: http://nodejs.org/api/domain.html

+0

Ecco un pacchetto npm fare questo: https://www.npmjs.com/package/express-domain-middleware – pjincz

+0

migliore risposta/soluzione! – felixfbecker

+3

Il modulo 'domain' è in attesa di deprecazione. Quindi non lo consiglierei di usarlo. – mauvm

Problemi correlati