Articles

Express-Middleware anhand von Beispielen aufbauen und verstehen

Posted on

Wenn Sie in den letzten sieben oder acht Jahren eine nennenswerte Node-Entwicklung durchgeführt haben, haben Sie wahrscheinlich irgendwann Express verwendet, um einen Webserver zu erstellen. Sie können zwar einen Server in Node erstellen, ohne eine Bibliothek zu verwenden, aber sie bietet Ihnen nicht viel von Haus aus und es kann ziemlich mühsam sein, Funktionalität hinzuzufügen. Express ist eine minimalistische, „rechthaberische“ Server-Bibliothek und hat sich zum De-facto-Standard für die Erstellung von Web-Apps in Node entwickelt. Um Express zu verstehen, müssen Sie Express Middleware verstehen.

Was ist Express Middleware?

Middleware bedeutet wortwörtlich alles, was man zwischen eine Schicht der Software und eine andere legt. Express-Middleware sind Funktionen, die während des Lebenszyklus einer Anfrage an den Express-Server ausgeführt werden. Jede Middleware hat Zugriff auf die HTTP request und response für jede Route (oder Pfad), mit der sie verbunden ist. In der Tat besteht Express selbst vollständig aus Middleware-Funktionen. Zusätzlich kann die Middleware die HTTP-Anfrage entweder beenden oder sie an eine andere Middleware-Funktion mit next weiterleiten (mehr dazu in Kürze). Diese „Verkettung“ von Middleware ermöglicht es Ihnen, Ihren Code aufzuteilen und wiederverwendbare Middleware zu erstellen.

In diesem Artikel erkläre ich, was Middleware ist, warum man sie verwenden sollte, wie man bestehende Express-Middleware nutzt und wie man seine eigene Middleware für Express schreibt.

Voraussetzungen zum Schreiben von Express-Middleware

Es gibt ein paar Dinge, die Sie installieren müssen, um Express-Middleware zu erstellen, zu verwenden und zu testen. Zunächst benötigen Sie Node und NPM. Um sicherzustellen, dass Sie diese installiert haben, können Sie folgendes ausführen:

npm -v && node -v

Sie sollten die Node- und NPM-Versionen sehen, die Sie installiert haben. Wenn Sie eine Fehlermeldung erhalten, müssen Sie Node installieren. Ich verwende die neueste Version von beiden zum Zeitpunkt dieses Artikels, also Node 10.9.0 und NPM 6.4.1, aber alle Beispiele sollten mit Node-Versionen 8+ und NPM-Versionen 5+ funktionieren.

Ich werde auch Express Version 4.x verwenden. Das ist wichtig, da von Version 3.x auf 4.x.

Es ist auch hilfreich, Postman installiert zu haben, um Routen zu testen, die andere HTTP-Verben als GET verwenden.

Express Middleware: Die Grundlagen

Für den Anfang werden Sie die grundlegendste der in Express eingebauten Middleware verwenden. Dies gibt Ihnen die Möglichkeit zu sehen, wie Middleware verwendet wird und wie Express Middleware strukturiert ist.

Erstellen Sie ein neues Projekt und npm init es…

npm initnpm install express --save

Erstellen Sie server.js und fügen Sie den folgenden Code ein:

const express = require('express');const app = express();app.get('/', (req, res, next) => { res.send('Welcome Home');});app.listen(3000);

Starten Sie den Server über node server.js, rufen Sie http://localhost:3000 auf, und Sie sollten „Welcome Home“ in Ihrem Browser angezeigt bekommen.

Die app.get() Funktion ist Middleware auf Anwendungsebene. Sie werden feststellen, dass die an die Methode übergebenen Parameter reqres und next sind. Dies sind die eingehende Anfrage, die zu schreibende Antwort und eine aufzurufende Methode, um den Aufruf an die nächste Middleware-Funktion zu übergeben, sobald die aktuelle Middleware beendet ist. In diesem Fall wird die Funktion beendet, sobald die Antwort gesendet wurde. Sie könnten hier auch andere Middleware verketten, indem Sie die Methode next() aufrufen.

Schauen wir uns ein paar weitere Beispiele für die verschiedenen Arten von Middleware an.

Express Request Logging Middleware Example

In Express können Sie Middleware so einrichten, dass sie „globale“ Middleware ist; das heißt, sie wird für jede eingehende Anfrage aufgerufen.

Ändern Sie den Inhalt von server.js zu:

const express = require('express');const app = express();app.use((req, res, next) => { console.log(req); next();});app.get('/', (req, res, next) => { res.send('Welcome Home');});app.listen(3000);

Wenn Sie jetzt auf http://localhost:3000 gehen, sollten Sie in Ihrem Browser-Fenster das Gleiche sehen, aber hinten im Konsolenfenster sehen Sie die Ausgabe des eingehenden Request-Objekts.

Die Middleware loggt das Request-Objekt aus und ruft dann next() auf. Die nächste Middleware in der Pipeline bearbeitet die get-Anfrage an die Stamm-URL und sendet die Textantwort zurück. Die Verwendung von app.use() bedeutet, dass diese Middleware bei jedem Aufruf der Anwendung aufgerufen wird.

Beispiel für den Inhaltstyp der Express-Anfrage

Zusätzlich zur Ausführung der Middleware bei allen Aufrufen können Sie auch festlegen, dass die Middleware nur bei bestimmten Aufrufen ausgeführt wird.

Ändern Sie die server.js-Datei erneut zu:

const express = require('express');const app = express();const requireJsonContent = () => { return (req, res, next) => { if (req.headers !== 'application/json') { res.status(400).send('Server requires application/json') } else { next() } }}app.get('/', (req, res, next) => { res.send('Welcome Home');});app.post('/', requireJsonContent(), (req, res, next) => { res.send('You sent JSON');})app.listen(3000);

Diesmal starten Sie den Server durch Ausführen:

node server.js

Um dies zu testen, öffnen Sie Postman und erstellen Sie eine Post-Anfrage an http://localhost:3000. Setzen Sie keine Header und führen Sie die Anfrage aus. Sie erhalten die Meldung „Server requires application/json“ zurück.

Nun gehen Sie zurück und fügen den Content-Type Header mit einem Wert von application/json hinzu und führen die Anfrage erneut aus. Sie erhalten die Meldung „Sie haben JSON gesendet“ vom Server zurück.

Dieser app.post()-Methodenaufruf fügt die requireJsonContent()-Middleware-Funktion hinzu, um sicherzustellen, dass der eingehende Request-Payload einen Content-Type-Header-Wert hat, der auf application/json gesetzt ist. Wenn er die Prüfung nicht besteht, wird eine Fehlerantwort gesendet. Wenn dies der Fall ist, wird die Anfrage über die next()-Methode an die nächste Middleware in der Kette übergeben.

Express-Middleware von Drittanbietern

Sie haben bis jetzt ein paar eigene Middlewares gebaut, aber es gibt bereits viele Pakete, die die Dinge tun, die Sie normalerweise tun möchten. Tatsächlich haben Sie die einfache Routing-Middleware-Bibliothek verwendet, indem Sie die app.get() oder app.post() Middleware-Funktionen benutzt haben. Es gibt Tausende von Middleware-Bibliotheken für Dinge wie das Parsen eingehender Daten, Routing und Autorisierung.

Okta hat eine Express-Middleware für OIDC-Sicherheit, die ich Ihnen zeigen werde, um die Verwendung von Middleware-Bibliotheken von Drittanbietern zu demonstrieren.

Warum Okta für Express-Anwendungen

Unser Ziel bei Okta ist es, das Identitätsmanagement viel einfacher, sicherer und skalierbarer zu machen, als Sie es gewohnt sind. Okta ist ein Cloud-Service, der es Entwicklern ermöglicht, Benutzerkonten und Benutzerkontodaten zu erstellen, zu bearbeiten und sicher zu speichern und sie mit einer oder mehreren Anwendungen zu verbinden. Unsere API ermöglicht Ihnen:

  • Authentifizieren und autorisieren Sie Ihre Benutzer
  • Speichern Sie Daten über Ihre Benutzer
  • Passwortbasiertes und soziales Login durchführen
  • Sichern Sie Ihre Anwendung mit Multi-Faktor-Authentifizierung
  • Und vieles mehr! Schauen Sie sich unsere Produktdokumentation an

Okta’s OIDC Express Middleware

Um Okta’s OIDC Middleware für Express zu installieren, führen Sie aus:

npm install @okta/[email protected] --save

Dann erstellen Sie in der server.js-Datei eine Instanz der Middleware mit einigen Konfigurationsoptionen, damit Okta weiß, wie es sich mit Ihrer Okta-Anwendung verbinden soll.

const oidc = new ExpressOIDC({ issuer: 'https://{yourOktaDomain}/oauth2/default', client_id: '{yourClientId}', client_secret: '{yourClientSecret}', redirect_uri: 'http://localhost:3000/authorization-code/callback', scope: 'openid profile'});

Sie müssen Express auch mitteilen, dass es den OIDC-Middleware-Router anstelle des Standard-Routers verwenden soll.

app.use(oidc.router);

Dann verwenden Sie ihn wie jede andere Middleware:

app.get('/protected', oidc.ensureAuthenticated(), (req, res) => { res.send('Top Secret');});

Die oidc.ensureAuthenticated() Funktion ist eine Middleware in der Okta-Bibliothek. Sie führt eine Funktion aus, um zu sehen, ob der aktuelle Benutzer eingeloggt ist. Wenn ja, ruft sie next() auf, damit die app.get()-Funktion die Anfrage weiter bearbeiten kann. Wenn nicht, sendet es eine HTTP 401 (Unauthorized)-Antwort zurück.

Middleware-Reihenfolge ist wichtig

Wenn eine Anfrage von Express empfangen wird, wird jede Middleware, die zur Anfrage passt, in der Reihenfolge ausgeführt, in der sie initialisiert wurde, bis es eine abschließende Aktion gibt (wie das Senden einer Antwort).

Middleware Flow

Wenn also ein Fehler auftritt, werden alle Middlewares, die für die Fehlerbehandlung vorgesehen sind, in der Reihenfolge aufgerufen, bis eine von ihnen den next() Funktionsaufruf nicht mehr ausführt.

Fehlerbehandlung in Express-Middleware

Express hat einen eingebauten Standard-Fehlerhandler, der am Ende der Middleware-Pipeline eingefügt wird und alle unbehandelten Fehler behandelt, die in der Pipeline aufgetreten sind. Seine Signatur fügt einen Fehlerparameter zu den Standardparametern von request, response und next hinzu. Die grundlegende Signatur sieht wie folgt aus:

app.use((err, req, res, next) => { // middleware functionality here})

Um eine fehlerbehandelnde Middleware aufzurufen, übergeben Sie den Fehler einfach an next(), etwa so:

app.get('/my-other-thing', (req, res, next) => { next(new Error('I am passing you an error!'));});app.use((err, req, res, next) => { console.log(err); if(!res.headersSent){ res.status(500).send(err.message); }});

In diesem Fall wird die fehlerbehandelnde Middleware am Ende der Pipeline den Fehler behandeln. Vielleicht fällt Ihnen auch auf, dass ich die Eigenschaft res.headersSent überprüft habe. Damit wird nur überprüft, ob die Antwort die Header bereits an den Client gesendet hat. Wenn nicht, sendet sie einen 500 HTTP-Status und die Fehlermeldung an den Client. Sie können auch Middleware für die Fehlerbehandlung verketten. Dies ist üblich, um verschiedene Arten von Fehlern auf verschiedene Weise zu behandeln. Zum Beispiel:

app.get('/nonexistant', (req, res, next) => { let err = new Error('I couldn\'t find it.'); err.httpStatusCode = 404; next(err);});app.get('/problematic', (req, res, next) => { let err = new Error('I\'m sorry, you can\'t do that, Dave.'); err.httpStatusCode = 304; next(err);});// handles not found errorsapp.use((err, req, res, next) => { if (err.httpStatusCode === 404) { res.status(400).render('NotFound'); } next(err);});// handles unauthorized errorsapp.use((err, req, res, next) => { if(err.httpStatusCode === 304){ res.status(304).render('Unauthorized'); } next(err);})// catch allapp.use((err, req, res, next) => { console.log(err); if (!res.headersSent) { res.status(err.httpStatusCode || 500).render('UnknownError'); }});

In diesem Fall prüft die Middleware, ob ein 404 (not found) Fehler geworfen wurde. Wenn dies der Fall ist, rendert sie die Vorlagenseite „NotFound“ und übergibt den Fehler an das nächste Element in der Middleware. Die nächste Middleware prüft, ob ein 304-Fehler (nicht autorisiert) ausgegeben wurde. Wenn dies der Fall war, rendert sie die Seite „Unauthorized“ und übergibt den Fehler an die nächste Middleware in der Pipeline. Schließlich protokolliert der „catch all“-Fehlerhandler nur den Fehler, und wenn keine Antwort gesendet wurde, sendet er den httpStatusCode des Fehlers (oder einen HTTP 500-Status, wenn keiner angegeben wurde) und rendert die ‚UnknownError‘-Vorlage.

Mehr über Express-Middleware erfahren

Für detaillierte Anweisungen zum Einrichten der Okta OIDC-Middleware können Sie dem ExpressJS Quickstart folgen.

Es gibt auch eine Liste offiziell unterstützter Express-Middleware in diesem GitHub-Repos, die Sie ausprobieren und durchstöbern können, um mehr zu erfahren

Zu guter Letzt, wenn Sie mehr über die Verwendung von Okta erfahren möchten, gibt es ein Okta Node SDK, mit dem Sie weitere Benutzerverwaltungsfunktionen in Ihre Anwendung implementieren können.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.