Einfacher Webserver für SPA / PWA in 5 Minuten

So erstellen Sie einen einfachen Webserver mithilfe der Anweisungen für Standardknoten


Oft wird ein einfacher Webserver benötigt, um MPA / SPA / PWA-Anwendungen zu entwickeln. Einmal sagte ich bei einer großen Kundgebung auf die Frage: „Was haben Sie getan?“, Dass ich einen Webserver für das Hosten einer PWA-Anwendung aufstellte. Wir haben alle lange gelacht und ja, PWA ist übrigens kein Klebstoff. Wie SPA ist es kein Schönheitssalon. Dies sind alle Arten von Webanwendungen. Und SSR ist kein Land :-). Wenn Sie eine solche Anwendung einfach durch Öffnen der Startseite von index.html über einen Browser starten, funktioniert sie nicht ordnungsgemäß. Im besten Fall erhalten Sie eine Offline-Version. Ich liebe JavaScript und werde das Problem sozusagen mit den mir zur Verfügung stehenden Mitteln lösen.


Beginnen wir mit dem Plan:


  1. Wenn kein NodeJS vorhanden ist , laden Sie LTS herunter, installieren Sie, ändern Sie die Einstellungen nicht, und klicken Sie auf Weiter
  2. Erstellen Sie an unserem abgelegenen Ort, an dem alle Projekte gesammelt werden, den Ordner simple-web-server
  3. Führen Sie im Projektordner den Befehl npm init --yes // without --yes aus. Der Initialisierer stellt viele Fragen
  4. Fügen Sie in der Datei package.json im Abschnitt scripts die Eigenschaft und ihren Wert "main": "index.js" hinzu, damit wir unseren Server mit dem Befehl npm run schnell starten können
  5. Erstellen eines lib- Ordners Es wird empfohlen, den gesamten Code darin abzulegen, was keine Assemblierung und zusätzliche Schritte für den Betrieb erfordert
  6. Erstellen Sie eine index.js- Datei im Ordner lib . Dies ist unser zukünftiger Server.
  7. Erstellen Sie einen dist- Ordner - dies ist der Ordner, in dem öffentlich zugängliche Dateien gespeichert werden, einschließlich index.html, dh die statische Aufladung, die unser Server verteilt
  8. Öffnen Sie die Datei /index.js
  9. Schreiben Sie einen Code

Was wissen wir also darüber, was unser Server tun soll?


  1. Anfragen bearbeiten
  2. Dateien lesen
  3. Auf Anfrage mit Dateiinhalt antworten

Erstellen Sie zuerst unseren Server und importieren Sie ihn in die Datei idex.js


const {createServer} = require('http'); 

Diese Anweisung zerstört das Objekt des http- Moduls und weist dem Variablenbezeichner createServer einen Ausdruck, die Funktion createServer , zu.


Erstellen Sie einen neuen Server mit der folgenden Anweisung


  const server = createServer(); 

Wenn Sie zum ersten Mal zum Server-Host wechseln, sendet der Browser eine Anforderung zum Empfang des Dokuments. Um dieses Ereignis verarbeiten zu können, müssen wir daher auf solche Anfragen hören. Der von uns erstellte Server hat eine Listen- Methode, als Parameter übergeben wir die Portnummer 3000.


  const eventsEmitter = server.listen(3000); 

Der Ausdruck dieser Methode ist ein EventEmitter- Objekt, das in einer Variablen mit dem Bezeichner eventsEmitter gespeichert wird . Dieses Objekt ist beobachtbar. Wir abonnieren seine Ereignisse mithilfe des Methodenaufrufs on / addEventListener mit zwei erforderlichen Stringfunktionsparametern . Der erste Parameter gibt an, welche Ereignisse für uns von Interesse sind, der zweite Parameter ist eine Funktion, die dieses Ereignis verarbeitet.


  eventsEmitter.on('request', (req, res) => { debugger; }); 

Öffnen Sie den Link im Browser


Bild


Also haben wir uns auf die Anweisungen für den Debugger geeinigt. Wir sehen, dass wir als Parameter zwei Objekte vom Typ req , res erhalten . Diese Objekte sind Instanzen von Objekten vom Typ stream, daher ist req der Lesestream und res der Schreibestream.


Wir haben die Anfrage bearbeitet und können sagen, dass "die Sache im Hut ist". Jetzt müssen Sie nur noch die Datei lesen und zurückgeben. Zuerst müssen Sie verstehen, welche Art von Datei wir benötigen. Nachdem ich im Debugger alle Eigenschaften des req- Parameters untersucht hatte, sah ich, dass er die url- Eigenschaft hat. Aber es gibt nichts Vergleichbares wie index.html .


Schauen wir uns noch einmal unseren Browser an: Wir sehen, dass wir dies nicht explizit angegeben haben. Versuchen wir es noch einmal, aber wir geben index.html bereits explizit an.


Bild


Jetzt ist klar, dass der Dateiname in der Anfrage in der url- Eigenschaft enthalten ist und nichts weiter als dies. Wir müssen die Datei nicht aus der Anfrage lesen. Wir zerlegen es in geschweifte Klammern, geben den Namen der URL- Eigenschaft an und setzen über den Operator : einen beliebigen Namen unter Verwendung einer gültigen Variablenkennung, in meinem Fall requestUrl .


  eventsEmitter.addListener('request', ({url: requestUrl}, res) => { debugger }); 

Großartig, was kommt als nächstes? In Wahrheit mag ich die Tatsache, dass index.html immer explizit angegeben werden muss, nicht wirklich, also lasst uns dieses Problem sofort lösen. Ich entschied, dass der einfachste Weg dies zu tun ist, die standardmäßige extname- Funktion zu verwenden , die im Standardpaket enthalten ist
NodeJS des Pfadmoduls importieren wir mit der folgenden Anweisung.


  const {extname} = require('path'); 

Sie können es jetzt aufrufen, indem Sie den requestUrl-Bezeichnerausdruck als Parameter übergeben und den Ausdruck für die Zeichenfolge im ungefähren Format '.extension' . Wenn die Anforderung keine Datei explizit angibt, wird eine leere Zeichenfolge zurückgegeben. Nach diesem Prinzip fügen wir den Standardwert 'index.html' hinzu . Wir schreiben die folgende Anweisung


  const url = extname(requestUrl) === '' ? DEFAULT_FILE_NAME : requestUrl; 

Ich bin sicher, dass der Serverbenutzer diesen Namen überschreiben und mit der folgenden Anweisung auch eine Umgebungsvariable einrichten möchte


  const {env: {DEFAULT_FILE_NAME = '/index.html'}} = process;` 

In der globalen Variablen verarbeiten viele nützliche Informationen, ich begrabe nur einen Teil davon, insbesondere die env- Eigenschaft, die alle Eigenschaften der Benutzerumgebung enthält, und wir werden bereits nach DEFAULT_FILE_NAME suchen, wenn der Benutzer sie nicht angibt. Wir verwenden standardmäßig index.html.


WICHTIG: Wenn der Wert der Umgebungseigenschaft DEFAULT_FILE_NAME alles andere als undefiniert ist, funktioniert das Zuweisen eines Standardwerts nicht. Das ist es wert, in Erinnerung zu bleiben, aber jetzt tun wir alles auf ein Minimum :-)

Bild


Da wir nun einen relativen Link zur Datei haben, müssen wir den absoluten Pfad zur Datei im Dateisystem unseres Servers ermitteln. Wir haben beschlossen, dass alle öffentlichen Dateien im Ordner dist gespeichert werden. Um den absoluten Pfad zu der Datei zu ermitteln , verwenden wir eine andere Auflösungsfunktion aus dem Modul, das wir bereits kennen
Pfad einfach in der zuvor erstellten Anweisung in Zeile 5 angeben


  const {resolve, extname} = require('path'); 

Als nächstes schreiben wir in Zeile 10 eine Anweisung, die den absoluten Pfad zur filePath- Variablen empfängt und speichert . Ich habe auch im Voraus „gewartet“, dass der Name dieses Ordners aus Gründen der Flexibilität neu definiert werden kann. Daher erweitere ich die Anweisung in Zeile 6 und füge den Namen hinzu
Umgebungsvariable DIST_FOLDER !


Bild


Jetzt ist alles bereit, um die Datei zu lesen. Sie können eine Datei auf verschiedene Arten asynchron oder synchron lesen oder Streams verwenden . Ich werde Streams verwenden :-) Es ist schön und effektiver, aus der Sicht der verbrauchten Ressourcen. Erstellen Sie zunächst eine Testdatei im Ordner dist, damit Sie etwas zu lesen haben :-)


 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> TESTING 1,2,3... </body> </html> 

Jetzt benötigen wir eine Funktion, die einen Dateilesestream erstellt. Sie ist auch in der Standard-NodeJS-Distribution enthalten. Wir extrahieren sie aus dem fs- Modul mithilfe der folgenden Anweisung


  const {createReadStream} = require('fs'); 

und in Zeile 12 im Hauptteil des Anforderungsprozessors verwenden wir die folgende Anweisung


  createReadStream(filePath) 

Infolgedessen wird die Instanz des Read-Stream-Objekts mit diesem Objekt zurückgegeben
Wir können die gelesenen Streams auf den schreibenden Stream umschalten, wir können die Streams auch transformieren und viele andere nützliche Dinge. Der Parameter res ist also ein Lesestream, oder?


Lassen Sie uns versuchen, den Dateilesestream, den wir erstellt haben, sofort in den res write-Stream für zu ändern
In Zeile 12 setzen wir den Befehl durch Aufrufen der Pipe- Methode fort und übergeben als Parameter unseren Write-Stream res


  createReadStream(filePath).pipe(res); 

Bild


Ist das alles Nein. Und wer wird die Fehler behandeln? Was für Fehler? Versuchen wir, die CSS-Datei in die Datei index.html hochzuladen, aber wir werden sie nicht erstellen und sehen, was passiert :-)


 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="index1.css"> </head> <body> TESTING 1,2,3... </body> </html> 

Bild


Erwartet, aber der Server ist abgestürzt! Dies ist überhaupt nicht der Fall. Tatsache ist, dass Fehler standardmäßig nicht in Streams abgefangen werden und Sie dies selbst tun müssen :-) createReadStream gibt den Stream zurück, in dem der Fehler auftritt. Aus diesem Grund fügen wir einen Fehlerbehandler hinzu. Aufruf der Methode on . Angabe des Namens des Fehlerereignisses und des Handlers einer Funktion. Der gelesene Lesestream wird mit einem 404-Antwortcode beendet.


  createReadStream(filePath) .on('error', error => res.writeHead(404).end()) .pipe(res); 

überprüfen!


Bild


Eine andere Sache. Der Server ist übrigens noch nicht bereit und wenn wir versuchen, ihn in einem anderen Browser zu öffnen, funktioniert die Seite nicht richtig :-) Wer hat es erraten, bitte schreiben Sie in die Kommentare, was haben Sie vergessen zu tun? Tatsache ist, dass, wenn ein Server die Anfrage eines Servers mit einer Datei beantwortet, eine Erweiterung nicht ausreicht, damit der Browser den Dateityp und die anderen Browser verstehen kann: Weder Chrome noch ältere Versionen funktionieren mit heruntergeladenen Dateien, ohne den Antwortheader für den Inhaltstyp anzugeben. Bei der Verarbeitung der Datei muss unser Server unter anderem den MIME-Typ angeben . Dazu erstellen wir eine separate Variable mit allen gängigen Typen. Wir bieten auch die Möglichkeit, sie durch Übergabe als Umgebungsvariable zu erweitern


  const {env: {DEFAULT_FILE_NAME = '/index.html', DIST_FOLDER = 'dist', DEFAULT_MIME_TYPES = '{}'}} = process; const {text} = mimeTypes = { 'html': 'text/html', 'jpeg': 'image/jpeg', 'jpg': 'image/jpeg', 'png': 'image/png', 'js': 'text/javascript', 'css': 'text/css', 'text': 'plain/text', 'json': 'application/json', ...JSON.parse(DEFAULT_MIME_TYPES) }; 

Nun müssen Sie den MIME-Typ irgendwie angeben, bevor Sie den Lesestream auf den Schreibstream umschalten. Ich habe die Dateierweiterungsnamen als Schlüssel verwendet, daher erhalten wir die Dateierweiterung mit der bekannten Funktion extname


  const fileExtension = extname(url).split('.').pop(); 

und mit Hilfe des Pipe- Event-Handlers stellen wir den gewünschten MIME-Typ ein


 res.on('pipe', () => res.setHeader(contentType, mimeTypes[fileExtension] || text)); 

Überprüfen Sie


Bild


Das ist alles - der Server ist bereit. Es ist natürlich nicht perfekt, aber für den schnellen Einstieg ist es das Richtige. Wenn Sie an der Entwicklung dieser Idee interessiert sind, schreiben Sie bitte in die Kommentare :-)


Vollständiger Projektcode



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


All Articles