Traduzir esta página

Tratamento de erros

Tratamento de erros refere-se a como as capturas Expressas e processa erros que ocorrem de forma sincronizada e assíncrona. Expresso vem com um erro padrão manipulador para que você não precise escrever seu próprio para começar.

Capturando Erros

É importante garantir que Expresso pegue todos os erros que ocorrem enquanto rodando manipuladores de rotas e intermediários.

Errors in synchronous code

Erros que ocorrem no código síncrono dentro dos manipuladores de rota e middleware não exigem trabalho extra. If synchronous code throws an error, then Express will catch and process it. Por exemplo:

app.get('/', (req, res) => {
throw new Error('BROKEN'); // Express will catch this on its own.
});

Errors in asynchronous code

The recommended way to write asynchronous handlers is with async functions. Route handlers and middleware that return a Promise call next(value) automatically when they reject or throw an error, and async functions always return a Promise, so their errors reach Express with no extra work. Por exemplo:

app.get('/user/:id', async (req, res) => {
const user = await getUserById(req.params.id);
res.send(user);
});

Se getUserById lança um erro ou rejeita, next será chamado com o erro lançado ou o valor rejeitado. Se nenhum valor rejeitado for fornecido, next será chamado com um objeto de erro padrão fornecido pelo roteador Express.

Se você passar qualquer coisa para a função next() (exceto a string 'route'), Expressa que a requisição atual é um erro e irá pular quaisquer funções restantes que não sejam de manipulação de erros e middleware.

Working with promise chains

If you build a promise chain instead of using an async function, return the promise from the handler and Express will likewise call next automatically when it rejects:

app.get('/', (req, res) => {
return Promise.resolve().then(() => {
throw new Error('BROKEN'); // Express will catch this and call next.
});
});

If the promise is not returned, Express does not know it exists, and you must route the error yourself by providing next as the final catch handler. Without it, the rejection would be unhandled and crash the process:

app.get('/', (req, res, next) => {
Promise.resolve()
.then(() => {
throw new Error('BROKEN');
})
.catch(next); // Errors will be passed to Express.
});

This works because if a callback in the promise chain throws, the chain turns that exception into a rejection, which travels down to the final .catch. There, .catch calls its handler with the error as the first argument, which is exactly the argument next expects, so the error reaches Express.

Working with callback APIs

Errors produced by callback-based APIs, such as those in node:fs, are not thrown and are not part of any promise. The callback receives them as its first argument, and you must pass them to the next() function yourself, where Express will catch and process them. Por exemplo:

app.get('/', (req, res, next) => {
fs.readFile('/file-does-not-exist', (err, data) => {
if (err) {
next(err); // Pass errors to Express.
} else {
res.send(data);
}
});
});

Se o callback em uma sequência não fornecer dados, somente erros, você pode simplificar este código da seguinte forma:

app.get('/', [
function (req, res, next) {
fs.writeFile('/inaccessible-path', 'data', next);
},
function (req, res) {
res.send('OK');
},
]);

No exemplo acima, next é fornecido como o callback para fs.writeFile, que é chamado com ou sem erros. Se não houver erro, o segundo receptor é executado, caso contrário as pegadas do Express e processa o erro.

Se você tiver um gerenciador de rotas com múltiplas funções de retorno de chamada, você pode usar o parâmetro rota para pular para o próximo manipulador de rota. Por exemplo:

app.get('/', [
function (req, res, next) {
fs.readFile('/maybe-valid-file', 'utf-8', (err, data) => {
res.locals.data = data;
next(err);
});
},
function (req, res) {
res.locals.data = res.locals.data.split(',')[1];
res.send(res.locals.data);
},
]);

The above example contains a couple of trivial statements following the readFile call. If readFile causes an error, then it passes the error to Express, otherwise you quickly return to the world of synchronous error handling in the next handler in the chain. Então, o exemplo acima tenta processar os dados. Se isso falhar, o manipulador de erro síncrono irá pegá-lo. Se você tivesse feito este processamento dentro do o callback readFile, então o aplicativo poderia sair e o erro Express manipuladores não executariam.

Finally, for asynchronous code that provides no error-first callback, such as a timer, catch errors inside the asynchronous code itself and pass them to Express:

app.get('/', (req, res, next) => {
setTimeout(() => {
try {
throw new Error('BROKEN');
} catch (err) {
next(err);
}
}, 100);
});

O exemplo acima usa um bloco tentar... catch para capturar erros no código assíncrono e passá-los para o Express. Se o bloco tentar...catch fosse omitido, Express não pegaria o erro, uma vez que ele não faz parte do código de manipulador síncrono.

Seja qual for o método que você usar, se você quer que os manipuladores de erro Expressos sejam chamados para dentro e o aplicativo para sobreviver, você deve garantir que o Expresso receba o erro.

O manipulador de erro padrão

Expresso vem com um manipulador de erro embutido que cuida de quaisquer erros que possam ser encontrados no aplicativo. Esta função de middleware com erros padrão é adicionada no final da função middleware stack.

Se você passar um erro para next() e não lidar com ele em um erro personalizado handler, será tratado pelo manipulador de erro embutido; o erro será escrito no cliente com o rastreamento de pilha. O rastreamento da pilha não está incluído no ambiente de produção.

Defina a variável de ambiente NODE_ENV como production, para executar o aplicativo em modo de produção.

Quando um erro é escrito, a seguinte informação é adicionada a resposta:

  • O res.statusCode é definido em err.status (ou err.statusCode). Se este valor estiver fora do intervalo de 4xx ou 5xx, ele será definido como 500.
  • A mensagem ‘res.statusMessage’ é definida de acordo com o código de status.
  • O corpo será o HTML da mensagem do código de status quando em produção ambiente, caso contrário será err.stack.
  • Qualquer cabeçalho especificado em um objeto err.headers.

Se você chamar next() com um erro depois que você começou a escrever a resposta (por exemplo, se você encontrar um erro ao transmitir a resposta ao cliente), o Express default error handler fecha a conexão e falha na solicitação.

Então, quando você adicionar um manipulador de erro personalizado, você deve delegar para o manipulador de erro padrão Express, quando os cabeçalhos já foram enviados para o cliente:

function errorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}

Observe que o manipulador de erro padrão pode ser acionado se você chamar next() com um erro no seu código mais de uma vez. Mesmo que um erro personalizado de manipulação de middleware esteja no lugar.

Outro erro de manipulação de middleware pode ser encontrado em Express middleware.

Escrevendo manipuladores de erros

Definir funções intermediárias de manipulação de erros da mesma forma que outras funções de middleware, exceto funções de manipulação de erro possuem quatro argumentos ao invés de três: (err, req, res, next). Por exemplo:

app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});

Você define o middleware de erro duramente, depois de outro app.use() e roteia chamadas; por exemplo:

index.cjs
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use(bodyParser.json());
app.use(methodOverride());
app.use((err, req, res, next) => {
// logic
});

As respostas de dentro de uma função middleware podem estar em qualquer formato, como uma página de erro HTML, uma mensagem simples ou uma string JSON.

Para fins de estrutura organizacional (e de nível superior), você pode definir várias funções de middleware com manipulação de erros, tanto quanto você poderia com funções de intermediário regulares. Por exemplo, para definir um manipulador de erro para solicitações feitas usando XHR e aqueles sem:

index.cjs
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use(bodyParser.json());
app.use(methodOverride());
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);

In this example, the generic logErrors might write request and error information to stderr, for example:

function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}

Também neste exemplo, clientErrorHandler é definido como segue; neste caso, o erro é explicitamente passado para o próximo.

Notice that when you do not call next in an error-handling function, you are responsible for writing (and ending) the response. Caso contrário, esses pedidos ficarão “pendentes” e não serão elegíveis para a recolha de lixo.

function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' });
} else {
next(err);
}
}

Implementar a função “catch-all” errorHandler da seguinte forma (por exemplo):

function errorHandler(err, req, res, next) {
res.status(500);
res.render('error', { error: err });
}

If you have a route handler with multiple callback functions, you can use the route parameter to skip to the next route handler. Por exemplo:

app.get(
'/a_route_behind_paywall',
(req, res, next) => {
if (!req.user.hasPaid) {
// continue handling this request
next('route');
} else {
next();
}
},
(req, res, next) => {
PaidContent.find((err, doc) => {
if (err) return next(err);
res.json(doc);
});
}
);

Neste exemplo, o manipulador getPaidContent será ignorado, mas quaisquer manipuladores restantes em app para /a_route_behind_paywall continuariam sendo executados.

Chama para next() e next(err) indicam que o manipulador atual está completo e em que estado. next(err) pulará todos os manipuladores restantes na cadeia, exceto aqueles que estão configurados para lidar com erros conforme descrito acima.