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?

Clint Harris
fonte

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.

Raynos
fonte
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
11

As pessoas da Joyent publicaram um documento de boas práticas realmente perspicaz sobre isso. Um artigo de leitura obrigatória para qualquer desenvolvedor do Node.js.

hthserhs
fonte
Ótimo artigo, corrigido o link para apontar para o documento atualizado de Joyent.
stephbu
2
artigo não é mau, mas muito texto e não o suficiente exemplos é um artigo para verdadeiros profissionais
Gerd
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 .

David
fonte