REST-Webservices mit Node.js, Teil 1: Connect als Fundament

Know-how  –  1 Kommentare

Kenntnisse in den Frameworks Connect und dem darauf aufbauenden Express gehören für Node.js-Entwickler zu den Grundlagen. Sie erweitern das grundlegende Request-Response-Modell von Node.js um häufig benötigte Funktionen wie Middleware und Routing. Was liegt also näher, als in einem zweiteiligen Tutorial mit ihnen REST-Webservices in Node.js zu entwickeln?

Anwendungen enthalten in der Regel außer ihren Kernfunktionen generischen Infrastrukturcode, der sich um sekundäre, anwendungsweite funktionale Belange wie Authentifizierung oder Internationalisierung kümmert. Da solcher Code per Definition nicht domänenspezifisch ist, lässt er sich im Idealfall als Komponente von der Stange in unterschiedliche Anwendungen einfügen. Voraussetzung hierfür sind jedoch anwendungsübergreifende einheitliche Schnittstellen zur Integration von Infrastrukturcode.

Die Definition und Implementierung dieser Schnittstellen ist Aufgabe der sogenannten Middleware. Das Framework Connect stellt in dieser Hinsicht den Industriestandard für Node.js dar und dient daher als häufig verwendete Basis für zahlreiche andere Projekte.

Middleware und Connect

Die grundlegende Idee von Middleware im Allgemeinen und Connect im Speziellen ist, die direkte Verbindung zwischen eingehenden Anfragen und der verarbeitenden Webanwendung zu trennen, um Infrastrukturcode zwischen beiden Punkten einfügen zu können. Dazu dienen der Middleware verschiedene Module, die unterschiedliche Aufgaben wahrnehmen, beispielsweise Authentifizierung oder Internationalisierung. Diese schaltet die Middleware in Reihe, sodass jede eingehende Anfrage zunächst die gesamte Kette von Modulen durchlaufen muss, bevor sie sich schließlich von der eigentlichen Webanwendung verarbeiten lässt.

Sofern alle beteiligten Module die gleichen Schnittstellen bedienen, erlaubt dieses Prinzip das für die Webanwendung transparente Einfügen beliebiger Module in beliebiger Reihenfolge innerhalb der Middleware (s. Abb.).

Jede eingehende Anfrage durchläuft zunächst die einzelnen Module der Middleware, bevor sie die eigentliche Webanwendung erreicht.

Filter und Provider

Connect kennt zwei Arten von Modulen: Filter und Provider. Filter dienen dazu, eingehende Anfragen auszuwerten oder zu verändern. Anschließend werden diese entweder an das nächste Modul der Middleware oder, im Falle des letzten Moduls, an die eigentliche Webanwendung weitergeleitet.

Provider hingegen fungieren als Stellvertreter der Webanwendung und beantworten Anfragen daher selbstständig, ohne diese im Anschluss an das nächste Modul oder die Webanwendung weiterzuleiten: Stattdessen wird die weitere Verarbeitung abgebrochen. Während Filter potenziell für jede Anfrage zuständig sein können, gilt das für Provider nicht: Sonst wäre es für eine Anfrage unmöglich, jemals die eigentliche Webanwendung zu erreichen, da ein Provider sie zuvor stets verarbeiten und anschließend abbrechen würde.

Die Basis eines jeden Moduls in Connect stellt eine Funktion dar, die die folgende Signatur aufweist:

function (req, res, next)

Die beiden Parameter req und res entsprechen dabei den von der createServer-Funktion des http-Moduls bekannten Parametern. Der Parameter next hingegen ist spezifisch für Connect und enthält eine Funktion, die die Verarbeitung an das nächste Modul weiterleitet. Das einfachstmögliche wäre daher jenes, das nichts anderes macht, als die Verarbeitung an das nachfolgende Modul weiterzuleiten. Dazu ruft es die next-Funktion auf. Die Parameter req und res werden dabei nicht von Hand übergeben, das erfolgt automatisch im Hintergrund. Wird das Modul in einer eigenen Datei definiert, ist es zudem zu exportieren:

var foo = function() {
return function(req, res, next) {
next();
};
};
module.exports = foo;

Das Modul erfüllt zugegebenermaßen keinen Zweck, enthält aber das erforderliche Grundgerüst. Je nachdem, ob der Entwickler zusätzlichen Code vor oder nach dem Aufruf der next-Funktion einfügt, erfolgt die Ausführung während der Anfrage oder während der Antwort.

Beim Zugriff auf req sollte das also vor dem Aufruf von next geschehen; bei dem auf res hingegen danach, da erst zu diesem Zeitpunkt die von der Webanwendung generierte Antwort zur Verfügung steht:

var foo = function() {
return function(req, res, next) {
// Zugriff auf req ...
next();
// Zugriff auf res ...
};
};

Soll der Entwickler anstelle eines Filters einen Provider implementieren, darf er next nicht aufrufen. Stattdessen greift er in der Regel schreibend auf res zu und ruft anschließend die Funktion res.end auf, um die weitere Verarbeitung abzubrechen.