Fehlerbehandlung in Express

Als ich anfing, mit Express zu arbeiten und herauszufinden, wie ich mit Fehlern umgehen sollte, hatte ich es schwer. Es gab das Gefühl, dass niemand darüber schrieb, was ich brauchte. Infolgedessen musste ich selbst nach Antworten auf meine Fragen suchen. Heute möchte ich alles erzählen, was ich über die Fehlerbehandlung in Express-Anwendungen weiß. Beginnen wir mit synchronen Fehlern.



Synchrone Fehlerbehandlung


Wenn Sie einen synchronen Fehler behandeln müssen, können Sie throw mithilfe der throw Anweisung throw solchen Fehler im Express-Request-Handler auslösen. Bitte beachten Sie, dass Request-Handler auch als "Controller" bezeichnet werden. Ich bevorzuge jedoch die Verwendung des Begriffs "Request-Handler", da mir dies klarer erscheint.

So sieht es aus:

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

Solche Fehler können mit dem Express Error Handler abgefangen werden. Wenn Sie keinen eigenen Fehlerbehandler geschrieben haben (wir werden weiter unten darauf eingehen), behandelt Express den Fehler mit dem Standardhandler.

Die Standard-Express-Fehlerbehandlungsroutine führt folgende Aktionen aus:

  1. Legt den HTTP-Antwortstatuscode auf 500 fest.
  2. Sendet eine Textantwort an die Entität, die die Anforderung ausführt.
  3. Protokolliert eine Textantwort an die Konsole.


Fehlermeldung in der Konsole angezeigt

Asynchrone Fehlerbehandlung


Um asynchrone Fehler zu behandeln, müssen Sie über das next Argument einen Fehler an den Express-Fehlerbehandler senden:

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

Dies ist, was auf die Konsole gelangt, wenn dieser Fehler protokolliert wird.


Fehlermeldung in der Konsole angezeigt

Wenn Sie das Konstrukt async / await in einer Express-Anwendung verwenden, müssen Sie eine Wrapper-Funktion wie express-async-handler verwenden . Auf diese Weise können Sie asynchronen Code ohne Try / Catch- Blöcke schreiben. Weitere Informationen zu async / await in Express finden Sie hier .

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

Nachdem der Anforderungshandler in den express-async-handler , können Sie wie oben beschrieben mit der throw Anweisung einen Fehler throw . Dieser Fehler wird an den Express-Fehlerbehandler weitergeleitet.

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


Fehlermeldung in der Konsole angezeigt

Schreiben Sie Ihren eigenen Fehlerbehandler


Express-Fehlerbehandlungsroutinen verwenden vier Argumente:

  1. Fehler
  2. req
  3. res
  4. als nächstes

Sie müssen nach Zwischenhandlern und Routen platziert werden.

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

Wenn Sie eine eigene Fehlerbehandlungsroutine erstellen, verwendet Express die Standard-Fehlerbehandlungsroutine nicht mehr. Um den Fehler zu behandeln, müssen Sie eine Antwort für die Front-End-Anwendung generieren, die den Endpunkt adressiert hat, an dem der Fehler aufgetreten ist. Dies bedeutet, dass Sie Folgendes tun müssen:

  1. Generieren und senden Sie einen geeigneten Antwortstatuscode.
  2. Bilden und senden Sie eine passende Antwort.

Welcher Statuscode im Einzelfall angemessen ist, hängt davon ab, was genau passiert ist. Hier ist eine Liste der häufigsten Fehler, auf die Sie vorbereitet sein sollten:

  1. Fehler 400 Bad Request Fehlerhafte 400 Bad Request . Wird in zwei Situationen verwendet. Erstens, wenn der Benutzer das erforderliche Feld nicht in die Anforderung aufgenommen hat (z. B. wurde das Feld mit den Kreditkarteninformationen in dem gesendeten Zahlungsformular nicht ausgefüllt). Zweitens, wenn die Anfrage falsche Daten enthält (z. B. unterschiedliche Passwörter in das Passwortfeld und in das Passwortbestätigungsfeld eingeben).
  2. Fehler 401 Unauthorized . Dieser Antwortstatuscode wird angewendet, wenn der Benutzer falsche Anmeldeinformationen eingegeben hat (z. B. Benutzername, E-Mail-Adresse oder Kennwort).
  3. Fehler 403 Forbidden . Wird verwendet, wenn der Benutzer keinen Zugriff auf den Endpunkt hat.
  4. Fehler 404 Not Found . Es wird in Fällen verwendet, in denen der Endpunkt nicht erkannt werden kann.
  5. Fehler 500 Internal Server Error . Es wird angewendet, wenn die vom Front-End gesendete Anforderung korrekt generiert wurde, im Back-End jedoch ein Fehler aufgetreten ist.

Nachdem der entsprechende Antwortstatuscode definiert wurde, muss er mit res.status :

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

Der Antwortstatuscode sollte der Fehlermeldung entsprechen. Senden Sie dazu den Statuscode zusammen mit dem Fehler.

Am einfachsten geht das mit dem http-errors- Paket. Es können drei fehlerhafte Informationen gesendet werden:

  1. Der Antwortstatuscode.
  2. Die mit dem Fehler verknüpfte Nachricht.
  3. Alle Daten, die gesendet werden müssen (dies ist optional).

So installieren Sie das http-errors :

 npm install http-errors --save 

So verwenden Sie dieses Paket:

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

Betrachten Sie ein Beispiel, mit dem Sie dies alles richtig verstehen können.

Stellen Sie sich vor, wir versuchen, einen Benutzer unter seiner E-Mail-Adresse zu finden. Dieser Benutzer kann jedoch nicht gefunden werden. Infolgedessen entscheiden wir uns, als Antwort auf die entsprechende Anforderung einen Fehler " User not found zu senden, um den Anrufer zu informieren, dass der Benutzer nicht gefunden wurde.

Folgendes müssen wir tun, um den Fehler zu erstellen:

  1. Setzen Sie den Antwortstatuscode auf 400 Bad Request (schließlich hat der Benutzer falsche Daten eingegeben). Dies wird unser erster Parameter sein.
  2. Senden Sie eine Nachricht an den Anrufer wie User not found . Dies wird der zweite Parameter sein.

 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`) })) 

Sie können den Statuscode mit dem Konstrukt error.status und die Fehlermeldung mit error.message :

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


Das Ergebnis von Protokollierungsfehlern in der Konsole

Anschließend wird der Antwortstatus mit res.status und die Nachricht in res.json :

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

Persönlich bevorzuge ich das Senden eines Statuscodes, einer Nachricht und eines Stack-Trace-Ergebnisses für solche Antworten. Dies erleichtert das Debuggen.

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

▍ Standardantwortstatuscode


Wenn die Fehlerquelle nicht createError , verfügt sie nicht über die Eigenschaft status . Hier ist ein Beispiel, in dem versucht wurde, eine nicht vorhandene Datei mit fs.readFile zu lesen:

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

Ein solches Fehlerobjekt hat nicht die Eigenschaft status :

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


Das Ergebnis von Protokollierungsfehlern in der Konsole

In solchen Fällen können Sie den Standardfehlercode festlegen. Wir sprechen nämlich über den 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  }) }) 

▍Ändern des Fehlerstatuscodes


Angenommen, wir lesen eine bestimmte Datei anhand der vom Benutzer bereitgestellten Daten. Wenn eine solche Datei nicht existiert, müssen wir einen 400 Bad Request Fehler 400 Bad Request . Immerhin kann der Server nicht gefunden werden, da die Datei nicht gefunden werden kann.

In diesem Fall müssen Sie das try / catch-Konstrukt verwenden, um den ursprünglichen Fehler abzufangen. Dann müssen Sie das createError mit createError neu 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 Fehlerbehandlung


Wenn die Anforderung alle Intermediate-Handler und -Routen durchlaufen hat, aber nicht verarbeitet wurde, bedeutet dies, dass der einer solchen Anforderung entsprechende Endpunkt nicht gefunden wurde.

Um 404 Not Found Fehler zu behandeln, müssen Sie zwischen den Routen und der Fehlerbehandlungsroutine eine zusätzliche Behandlungsroutine hinzufügen. So sieht die Erstellung des 404-Fehlerobjekts aus:

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


Fehlerdetails

▍ ERR_HTTP_HEADERS_SENT Fehlerhinweise


Keine Panik, wenn die Fehlermeldung ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client . Es entsteht, weil im selben Handler die Methode, die die Antwortheader setzt, wiederholt aufgerufen wird. Mit den folgenden Methoden werden die Antwortheader automatisch festgelegt:

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

Wenn Sie res.json res.render und res.json im selben res.render res.json , wird der Fehler ERR_HTTP_HEADERS_SENT :

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

Wenn dieser Fehler auftritt, überprüfen Sie den Code des Antworthandlers sorgfältig und stellen Sie sicher, dass es keine Situationen gibt, in denen mehrere der oben beschriebenen Methoden aufgerufen werden.

▍ Fehlerbehandlung und Datenstreaming


Wenn beim Streamen der Antwort auf das Frontend ein Fehler ERR_HTTP_HEADERS_SENT , tritt möglicherweise derselbe ERR_HTTP_HEADERS_SENT Fehler auf.

In diesem Fall muss die Fehlerbehandlung an Standardhandler übergeben werden. Ein solcher Handler sendet einen Fehler und beendet die Verbindung automatisch.

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

Zusammenfassung


Heute habe ich Ihnen alles gesagt, was ich über die Fehlerbehandlung in Express weiß. Ich hoffe, dies hilft Ihnen beim Schreiben zuverlässigerer Express-Anwendungen.

Sehr geehrte Leser! Wie gehen Sie mit Fehlern in Ihren Node.js-Projekten um?


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


All Articles