Tratamento de erros no Express

Quando comecei a trabalhar com o Express e tentei descobrir como lidar com erros, tive dificuldades. Havia um sentimento de que ninguém escreveu sobre o que eu precisava. Como resultado, tive que procurar respostas para minhas perguntas. Hoje, quero contar tudo o que sei sobre o tratamento de erros nos aplicativos Express. Vamos começar com erros síncronos.



Tratamento de erros síncronos


Se você precisar manipular um erro síncrono, poderá, em primeiro lugar, usando a instrução throw , throw esse erro no manipulador de solicitações Express. Observe que os manipuladores de solicitações também são chamados de "controladores", mas eu prefiro usar o termo "manipulador de solicitações", como me parece mais claramente.

Aqui está o que parece:

 app.post('/testing', (req, res) => {  throw new Error('Something broke! ') }) 

Esses erros podem ser detectados usando o manipulador de erros Express. Se você não escreveu seu próprio manipulador de erros (falaremos mais sobre isso abaixo), o Express tratará o erro usando o manipulador padrão.

Aqui está o que o manipulador de erros Express padrão faz:

  1. Define o código de status da resposta HTTP como 500.
  2. Envia uma resposta de texto para a entidade que está executando a solicitação.
  3. Registra uma resposta de texto no console.


Mensagem de erro exibida no console

Manipulação de erro assíncrona


Para manipular erros assíncronos, você precisa enviar um erro ao manipulador de erros Express através do next argumento:

 app.post('/testing', async (req, res, next) => {  return next(new Error('Something broke again! ')) }) 

É isso que chega ao console ao registrar esse erro.


Mensagem de erro exibida no console

Se você usar a construção assíncrona / aguardada em um aplicativo Express, precisará usar uma função de wrapper como express-async-handler . Isso permite escrever código assíncrono sem blocos try / catch . Leia mais sobre assíncrono / espera no Express aqui .

 const asyncHandler = require('express-async-handler') app.post('/testing', asyncHandler(async (req, res, next) => {  //  - })) 

Depois que o manipulador de solicitações é agrupado no express-async-handler , é possível, como descrito acima, gerar um erro usando a instrução throw . Este erro irá para o manipulador de erros Express.

 app.post('/testing', asyncHandler(async (req, res, next) => {  throw new Error('Something broke yet again! ') })) 


Mensagem de erro exibida no console

Escrevendo seu próprio manipulador de erros


Os manipuladores de erro expressos usam 4 argumentos:

  1. erro
  2. req
  3. res
  4. próximo

Você precisa colocá-los após manipuladores e rotas intermediárias.

 app.use(/*...*/) app.get(/*...*/) app.post(/*...*/) app.put(/*...*/) app.delete(/*...*/) //           app.use((error, req, res, next) => { /* ... */ }) 

Se você criar seu próprio manipulador de erros, o Express deixará de usar o manipulador de erros padrão. Para lidar com o erro, você precisa gerar uma resposta para o aplicativo front-end que abordou o terminal no qual o erro ocorreu. Isso significa que você precisa fazer o seguinte:

  1. Gere e envie um código de status de resposta adequado.
  2. Forme e envie uma resposta adequada.

Qual código de status é apropriado em cada caso específico depende do que exatamente aconteceu. Aqui está uma lista de erros comuns que você deve estar preparado para manipular:

  1. Erro 400 Bad Request . Usado em duas situações. Primeiro, quando o usuário não incluiu o campo obrigatório na solicitação (por exemplo, o campo com informações do cartão de crédito não foi preenchido no formulário de pagamento enviado). Em segundo lugar, quando a solicitação contém dados incorretos (por exemplo, digitando senhas diferentes no campo de senha e no campo de confirmação de senha).
  2. Erro 401 Unauthorized . Este código de status de resposta será aplicado se o usuário tiver inserido credenciais incorretas (como nome de usuário, endereço de email ou senha).
  3. Erro 403 Forbidden . Usado quando o usuário não tem acesso permitido ao terminal.
  4. Erro 404 Not Found . É usado nos casos em que o terminal não pode ser detectado.
  5. Erro 500 Internal Server Error Erro 500 Internal Server Error . É aplicada quando a solicitação enviada pelo front-end é formada corretamente, mas ocorreu algum erro no back-end.

Depois que o código de status de resposta apropriado é definido, ele deve ser configurado usando res.status :

 app.use((error, req, res, next) => {  // ,         res.status(400)  res.json(/* ... */) }) 

O código de status da resposta deve corresponder à mensagem de erro. Para fazer isso, envie o código de status junto com o erro.

A maneira mais fácil de fazer isso é com o pacote de erros http . Ele permite enviar três informações erradas:

  1. O código de status da resposta.
  2. A mensagem associada ao erro.
  3. Qualquer dado que precise ser enviado (isso é opcional).

Veja como instalar o pacote de http-errors :

 npm install http-errors --save 

Veja como usar este pacote:

 const createError = require('http-errors') //   throw createError(status, message, properties) 

Considere um exemplo que permitirá que você entenda tudo isso corretamente.

Imagine que estamos tentando encontrar um usuário no endereço de e-mail dele. Mas este usuário não pode ser encontrado. Como resultado, decidimos enviar um erro de User not found em resposta à solicitação correspondente, informando ao chamador que o usuário não foi encontrado.

Aqui está o que precisamos fazer ao criar o erro:

  1. Defina o código de status da resposta como 400 Bad Request incorreta (afinal, o usuário inseriu dados incorretos). Este será o nosso primeiro parâmetro.
  2. Envie uma mensagem para o chamador como User not found . Este será o segundo parâmetro.

 app.put('/testing', asyncHandler(async (req, res) => {  const { email } = req.body  const user = await User.findOne({ email })  //     -    if (!user) throw createError(400, `User '${email}' not found`) })) 

Você pode obter o código de status usando a construção error.status e a mensagem de erro usando error.message :

 //   app.use((error, req, res, next) => {  console.log('Error status: ', error.status)  console.log('Message: ', error.message) }) 


O resultado de erros de log no console

Em seguida, o estado de resposta é definido usando res.status e a mensagem é gravada em res.json :

 app.use((error, req, res, next) => {  //      res.status(error.status)  //    res.json({ message: error.message }) }) 

Pessoalmente, prefiro enviar um código de status, mensagem e resultado de rastreamento de pilha em tais respostas. Isso facilita a depuração.

 app.use((error, req, res, next) => {  //      res.status(error.status)  //    res.json({    status: error.status,    message: error.message,    stack: error.stack  }) }) 

▍ Código de status da resposta padrão


Se a origem do erro não for createError , ela não terá a propriedade status . Aqui está um exemplo no qual foi feita uma tentativa de ler um arquivo inexistente usando fs.readFile :

 const fs = require('fs') const util = require('util') //  readFile  ,  ,  async/await-. //     : https://zellwk.com/blog/callbacks-to-promises const readFilePromise = util.promisify(fs.readFile) app.get('/testing', asyncHandler(async (req, res, next) => {  const data = await readFilePromise('some-file') }) 

Esse objeto de erro não terá a propriedade status :

 app.use((error, req, res, next) => {  console.log('Error status: ', error.status)  console.log('Message: ', error.message) }) 


O resultado de erros de log no console

Nesses casos, você pode definir o código de erro padrão. Ou seja, estamos falando sobre o 500 Internal Server Error :

 app.use((error, req, res, next) => {  res.status(error.status || 500)  res.json({    status: error.status,    message: error.message,    stack: error.stack  }) }) 

HanAlterando o código de status do erro


Suponha que vamos ler um determinado arquivo usando os dados fornecidos pelo usuário. Se esse arquivo não existir, significa que precisamos fornecer um erro 400 Bad Request . Afinal, o servidor não pode ser encontrado porque o arquivo não pode ser encontrado.

Nesse caso, você precisa usar a construção try / catch para capturar o erro original. Então você precisa recriar o objeto de erro usando createError :

 app.get('/testing', asyncHandler(async (req, res, next) => {  try {    const { file } = req.body    const contents = await readFilePromise(path.join(__dirname, file))  } catch (error) {    throw createError(400, `File ${file} does not exist`)  } }) 

▍ 404 tratamento de erros


Se a solicitação passou por todos os manipuladores e rotas intermediárias, mas não foi processada, isso significa que o terminal correspondente a essa solicitação não foi encontrado.

Para lidar com erros 404 Not Found , é necessário adicionar, entre as rotas e o manipulador de erros, um manipulador adicional. Veja como é a criação do objeto de erro 404:

 //  ... // ... app.use((req, res, next) => {  next(createError(404)) }) //  ... 


Detalhes do erro

Notes ERR_HTTP_HEADERS_SENT notas de erro


Não entre em pânico se aparecer a mensagem de erro ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client . Isso ocorre porque no mesmo manipulador o método que define os cabeçalhos de resposta é chamado repetidamente. Aqui estão os métodos que chamam para definir automaticamente os cabeçalhos de resposta:

  1. res.send
  2. res.json
  3. res.render
  4. res.sendFile
  5. res.sendStatus
  6. res.end
  7. res.redirect

Portanto, por exemplo, se você chamar os res.json e res.json no mesmo manipulador de respostas, receberá o erro ERR_HTTP_HEADERS_SENT :

 app.get('/testing', (req, res) => {  res.render('new-page')  res.json({ message: '¯\_(ツ)_/¯' }) }) 

Como resultado, se você encontrar esse erro, verifique cuidadosamente o código do manipulador de respostas e verifique se não há situações nas quais vários dos métodos descritos acima sejam chamados.

▍ Tratamento de erros e streaming de dados


Se algo der errado ao transmitir a resposta ao front-end, você poderá encontrar o mesmo erro ERR_HTTP_HEADERS_SENT .

Nesse caso, o tratamento de erros deve ser passado para os manipuladores padrão. Esse manipulador envia um erro e fecha automaticamente a conexão.

 app.use((error, req, res, next) => {  //       ,        if (res.headersSent) {    return next(error)  }  //     }) 

Sumário


Hoje eu contei tudo o que sei sobre tratamento de erros no Express. Espero que isso ajude você a escrever aplicativos Express mais confiáveis.

Caros leitores! Como você lida com erros em seus projetos Node.js.


Source: https://habr.com/ru/post/pt476290/


All Articles