Erro ao manipular princípios para aplicativos Node.js + Express.js?


177

Parece que o relatório / tratamento de erros é feito de maneira diferente nos aplicativos Node.js. + Express.js . em comparação com outras estruturas. Estou correto ao entender que funciona da seguinte maneira?

A) Detecte erros recebendo-os como parâmetros para suas funções de retorno de chamada. Por exemplo:

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

B) Relate erros no MIDDLEWARE chamando next (err). Exemplo:

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

C) Relate erros em ROTAS lançando o erro. Exemplo:

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

D) Manipule os erros configurando seu próprio manipulador de erros via app.error () ou use o manipulador de erros genérico do Connect. Exemplo:

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

Esses quatro princípios são a base para todo tratamento / relatório de erros nos aplicativos Node.js. Express.js?

Respostas:


183

A manipulação de erros no Node.js geralmente é do formato A). A maioria dos retornos de chamada retorna um objeto de erro como o primeiro argumento ounull .

O Express.js usa middleware e a sintaxe do middleware usa B) e E) (mencionados abaixo).

C) é uma má prática se você me perguntar.

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

Você pode reescrever facilmente o item acima como

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

A sintaxe do middleware é válida em um get solicitação.

Quanto a D)

(07:26:37 PM) tjholowaychuk: app.error é removido no 3.x

TJ acabou de confirmar que app.error está obsoleto em favor de E

E)

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

Qualquer middleware com comprimento de 4 (4 argumentos) é considerado erro de middleware. Quando uma next(err)ligação é conectada, o middleware é baseado em erros.


11
Obrigado! Para qualquer um que possa se deparar com isso no futuro, parece que a ordem dos parâmetros para o "método e" é realmente err, req, res, next (em vez de req, res, next, err).
Clint Harris

9
Portanto, isso parece ótimo, mas um problema que estou vendo é que alguns erros nunca chegam aos manipuladores de erros que você descreve e só podem ser detectados por um manipulador process.on ('uncaughtException', fn). A sabedoria convencional é deixar que isso aconteça e contar com a Forever ou algo semelhante para reiniciar o aplicativo, mas se você fizer isso, como retornará uma página de erro amigável?
Paul

1
@chovy Além disso, apenas um FYI. O manipulador de erros deve ser fornecido ao aplicativo após o erro lançado / próximo. Se for anterior, não detectará o erro.
amigos estão dizendo sobre lee olayvar

3
seguinte (err) é essencialmente a versão de expresso de lançar um erro, você tem que chamá-lo explicitamente dentro do seu próprio middleware embora
qodeninja

1
@qodeninja Esse método é considerado uma prática recomendada no Express.
David Oliveros


3

Por que o primeiro parâmetro?

Devido à natureza assíncrona do Node.js, o primeiro-parâmetro-as-err padrão tornou-se bem estabelecida como uma convenção para o tratamento de erros userland Node.js . Isso ocorre porque assíncrono:

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

Portanto, ter o primeiro argumento do retorno de chamada é a única maneira sensata de passar erros de forma assíncrona, além de apenas lançá-los.

Fazer isso resultará em um unhandled exceptionque, exatamente da maneira que parece, implica que nada foi feito para tirar o aplicativo de seu estado confuso.

Exceções, por que elas existem

Vale ressaltar, no entanto, que praticamente todas as partes do Node.js são emissoras de eventos e o lançamento de uma exceção é um evento de baixo nível que pode ser tratado como todos os eventos:

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

Isso pode, mas não deve ser levado ao extremo para detectar todos os erros e criar um aplicativo que se esforçará muito para nunca travar. Essa é uma péssima idéia em quase todos os casos de uso, porque deixará o desenvolvedor sem nenhuma idéia do que está acontecendo no estado do aplicativo e é análogo ao agrupamento principal no try-catch.

Domínios - agrupando eventos logicamente

Como parte do problema com exceções que fazem os aplicativos caírem, os domínios permitem que o desenvolvedor pegue, por exemplo, o aplicativo Express.js, e tente encerrar as conexões de maneira sensata em caso de falha catastrófica.

ES6

Provavelmente, está mencionando que isso mudará novamente, pois o ES6 permite que o padrão do gerador crie eventos assíncronos que ainda podem ser capturados com blocos try / catch.

Koa (escrito por TJ Holowaychuck, mesmo autor original do Express.js) faz isso notavelmente. Ele usa a yieldinstrução ES6 para criar blocos que, embora pareçam quase síncronos, são tratados da maneira assíncrona do nó usual:

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

Este exemplo foi descaradamente roubado daqui .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.