NodeJS

JavaScript Laufzeitumgebung basierend auf der
V8 JavaScript Engine

NodeJS Logo

NodeJS - Mindmap

NodeJS - Server

NodeJS Server

NodeJS - Beispiel

						
					

Im Browser öffnen: "http://localhost:8000/"

alternativ: "http://localhost:8000/trololol"

NodeJS - Dokumentation

NodeJS

Vorteile:

  • Asynchrone Performanz
  • Mächtige Programmiersprache und Programmierumgebung: JavaScript + Npm-Module
  • Plattformunabhängig

Nachteile:

  • Code kann Server zum Absturz bringen
  • Bindung an Programmiersprache JavaScript (yikes)

NodeJS http - ganz kurz

API für Client als auch Server Funktionalität: Docs

Server 'request' Event bei jeder Client Anfrage

						
					

'res.end' MUSS immer aufgerufen werden

NodeJS http - ganz kurz

Request-Listener '(req, res) => {}' einzigste Server-API

Zuordnung zu einer URL muss programmiert werden

Zuordnung zu einer HTTP-Methode muss programmiert werden

Low-Level Schnittstelle

Einschub: HTTP-Refresh

HTTP/1.1 - Anfrage/Request

GET /index.html HTTP/1.1
Host: example.com

HTTP/1.1 - Antwort/Response

HTTP/1.1 200 OK
Server: Apache/1.3.29 (Unix) PHP/4.3.4
Content-Length: 123456
Content-Type: text/html; charset=utf-8

<!DOCTYPE ...

NodeJS http.IncomingMessage

Dokumentation: http.IncomingMessage (req)

  • httpVersion (String)
  • method (String)
  • url (String)
  • headers (Object)
    • 'user-agent'
    • 'host'
  • Event 'data' (chunk, String)
  • Event 'end'

NodeJS http.OutgoingMessage

Dokumentation: http.OutgoingMessage (res)

  • setHeader(name, value)
  • getHeader(name)
  • removeHeader(name)
  • writeHead(statusCode[, statusMessage][, headers])
    • statusCode (number)
    • statusMEssage (String)
    • headers (Object|Array)

NodeJS http.OutgoingMessage

Dokumentation: http.OutgoingMessage (res)

  • write(chunk[, encoding][, callback])
    • chunk (String|Buffer)
    • encoding (String, "utf8")
    • callback (Function, chunk flushed)
  • end(chunk[, encoding][, callback])
    • chunk (String|Buffer)
    • encoding (String, "utf8")
    • callback (Function, "finish" event)

Praxis - NodeJS - Aufgabe 0

Node.js v18 installieren: Downloads

Zusatz

IDE installieren, z.B. VSCode oder WebStorm

Praxis - NodeJS - Aufgabe 1

Implementiere einen Echo-Server.
Bei einem GET-Request soll die URL (als plain text) zurückgegeben werden.
Bei einem POST-Request soll der Inhalt zurückgegeben werden.

Zusatz

Start des Servers (bereit Anfragen entgegenzunehmen) wird auf der Konsole ausgeben

Herunterfahren des Servers wird auf der Konsole ausgeben (Tip: Strg+C -> 'SIGINT', process.on)

NodeJS - Lösung - Aufgabe 1

Lösung

Npm-Module: Express

High-Level HTTP-Server API

						
					

'res.send' MUSS weiterhin immer aufgerufen werden

Express

Open-Source Npm-Module 'express' (expressjs.com)

Defakto-Standard im Node.JS Universum
für Http-Server

						
					

Express - HTML Antwort

						
					

Sinnvoller default: mit type('html') wird auch charset=utf-8 verwendet!

Express - JSON Antwort

						
					

Egal welcher "type()" der Inhalt einer Http-Response ist immer ein string!

Express - Anfrage-Inhalt

						
					

Praxis - NodeJS - Aufgabe 2

Erstellt einen Node.JS Server der per GET- und POST-Methode aufgerufen werden kann (z.B. via zwei Buttons in zwei Formularen)

Programmiert den Node.JS Server, sodass:

  • ein HTML-Dokument "Hello World from GET" mit Url bei einer get-Anfrage zurückgeliefert wird
  • ein HTML-Dokument "Hello World from POST" mit Body bei einer post-Anfrage zurückgeliefert wird

Zusatz

Gebt im HTML-Dokument die aktuelle Uhrzeit aus

Praxis - NodeJS - Aufgabe 3

Erstellt einen Node.JS-Server der eine Seite mit einer Überschrift und einem Formular mit einem Eingabefeld generiert.

  • Im Eingabefeld wird nach dem Namen gefragt:
    "Wie ist dein Name?"
  • Falls noch kein Name angegeben wurde lautet die Überschrift "Hallo Du Da".
  • Falls ein Name angegeben wurde lautet die Überschrift "Hallo <name>".

Praxis - NodeJS - Aufgabe 4

Programmiert einen kleinen Rechner

  • Formular für zwei Operanden als auch die Rechenoperation
  • Werden die Parameter an den NodeJS Server übergeben, soll die durchzuführende Berechnung samt Ergebnis ausgegeben werden. Danach folgt wieder das Eingabeformular.

Zusatz

Bei Eingabe nicht-nummerischer Werte: Passende Fehlermeldung an passender Stelle

HTML generieren

						
					

Das erzeugen von HTML-Code ist umständlich!

SSI - Rückblick

						<body>
	

Dynamisches HTML mit Server Side Includes

Datum/Uhrzeit auf dem Server-Rechner:
Uhr
Name dieser HTML-Datei:
Installierte Server-Software:
Ihr Web-Browser:
</body>

Quelle: SelfHTML

Vergleich: SSI vs. NodeJS

SSI

  • <h1>abc</h1>
    • HTML ist Grundsprache
  • <!-- #set var="x" value="1" -->
    • Für SSI-Befehl aus HTML "heraus springen"
  • Pfad entspricht Dateiname

NodeJS

  • response += "<h1>abc</h1>"
    • HTML explizit ausgeben
  • let x = 1;
    • JavaScript ist Grundsprache der Datei
  • Explizite Zuordnung zu einer URL

Grundidee: HTML-Seiten mit Einbettungen

  • Vorteile
    • HTML-Struktur leichter lesbar und änderbar
    • Leichter zu managen (IDE Syntaxhighlighting, etc.)
  • Bedingungen
    • Code hat nur kleinen Anteil
    • Komplexen Code auslagern!

PHP - Hypertext Preprocessor

  • Ursprünglich: Personal Homepage Tool
  • Frei Verfügbar
  • Große Auswahl an Bibliotheken
  • Für viele Betriebssysteme und Webserver verfügbar
  • Skriptsprache
  • (Für große und komplexe Systeme nur bedingt geeignet.)

PHP - Hypertext Preprocessor

HTML Formular

						<form action="action.php" method="post">
	

Ihr Name:

Ihr Alter:

</form>

PHP Ergebnisseite

					<p>
	Hallo <?php echo $_POST['name']; ?>.
	Sie sind <?php echo $_POST['alter']; ?> Jahre alt.
</p>
				
  • Pragma "<?php" und "?>" für PHP-Codeblöcke
  • Vorsicht: Kein gültiges HTML!
  • HTML via "echo" ausgegeben

ASP.NET - Active Server Pages

  • Weiterentwicklung von ASP
    (mit Einführung von ASP.NET 2002 eingestellt)
  • C# als Haupt-Programmiersprache,
    aber auch VB verfügbar
  • Bindung an .NET Framework von Microsoft,
    aber inzwischen Open-Source (seit 2020)
  • Mit C# auch für größere Systeme geeignet

ASP.NET - Active Server Pages

						<body>
	@{ if (IsPost) { 
		string companyname = Request["CompanyName"]; 
		string contactname = Request["ContactName"]; 
		

You entered:
Company Name: @companyname
Contact Name: @contactname

} else {
Company Name:

Contact Name:


} } </body>
  • Pragma "@{" und "}" für C#-Codeblöcke
  • Vorsicht: Kein gültiges HTML!
  • HTML ohne zusätzliche Syntax ausgegeben

JSP - Java Server Pages

  • JSP ist Bestandteil von Java EE (Enterprise Edition)
  • Große Auswahl an Bibliotheken und Infrastruktur
  • Java als Haupt-Programmiersprache
  • Application Server interpretierien JSP-Dateien
    on-the-fly: JSP zu Java zu Bytecode Übersetzung
  • Für größere Systeme geeignet

JSP - Java Server Pages

HTML Formular

						<form action="/action" method="post">
	

Ihr Name:

Ihr Alter:

</form>

JSP Ergebnisseite

					<p>
	Hallo <% out.println(request.getParameter("name")); %>.
	Sie sind <% out.println(request.getParameter("alter")); %> Jahre alt.
</p>
				
  • Pragma "<%" und "%>" für Java-Codeblöcke
  • Vorsicht: Kein gültiges HTML!
  • HTML via "out.println" ausgegeben

Express - Templates

  1. Template Engine registrieren: app.engine(ext, callback)
    • ext: Standard Dateiendung, String
    • callback: Render Funktion der Template-Engine, Funktion
  2. Template rendern:
    res.render(view [, locals] [, callback])
    • view: Pfad zur Template-Datei, String
    • locals: Template-Parameter, Objekt
    • callback: wird mit (err, html) gerufen. html muss selbst zurückgesendet werden (res.send)
  3. App-Einstellungen: app.set(name, value)
    • views: Pfad zum Ordner, String|Array
    • view engine: Standard Dateiendung, String

Express - Templates

Fluch und Segen Schild

Express - Templates

  • Segen
  • Fluch
    • Ist das OSS-Projekt noch aktiv? Security-Patches?
    • Schwer zu vergleichen (Syntax, Features, Tooling)
    • Projektwechsel schwieriger

Wir werden "eta" (eta.js.org) nutzen.

Express - Eta - Hello World

						
					

Im Browser öffnen: "http://localhost:8000/"

Express - Eta - Hello World

						
					
  • Pragma "<%" und "%>" für JavaScript-Codeblöcke
    Aber: Reservierte Variablen, z.B. "it"
  • Vorsicht: Kein gültiges HTML!

Express - Eta - Pragmas

  • "<%" und "%>", JavaScript-Codeblock
						<% if(it.somevalue === 1) { %>
	

Display this paragraph

<% } else { %>

Display this instead paragraph instead

<% } %>

Spezialvariable: "it"
enthält das gesamte Parameterobjekt

Nach wie vor gilt: Logik von Darstellung trennen(!!)

Express - Eta - Pragmas

  • "<%=" Expressions
    • Ergebnis des JavaScript-Ausdrucks wird ausgegeben (escaped!)
  • "<%~" Raw Expressions
    • Ergebnis des JavaScript-Ausdrucks wird ausgegeben (raw!)
  • "<%~ includeFile('./footer')" Partial
    • Inhalt der Datei wird ausgegeben (raw!)

Eta Syntax Dokumentation

Express - Statische Dateien

Bisher: Reine HTML/JSON/Plain-Text antworten auf HTTP-Anfragen.

Aber: Für eine vollständige Website fehlen noch (mindestens) CSS-Dateien und Bilder

Vorsicht: Node.JS+Express sind schnarch langsam statische Dateien auszuliefern. In Produktion auch Load Balancer/Caches/Web-Server nutzen.

Express - Statische Dateien

						
					

Im Browser öffnen: "http://localhost:8000/"

Praxis - NodeJS - Aufgabe 3

Erstellt einen Node.JS-Server der eine Seite mit einer Überschrfit und einem Formular mit einem Eingabefeld generiert.

  • Im Eingabefeld wird nach dem Namen gefragt: "Wie ist dein Name?"
  • Falls noch kein Name angegeben wurde lautet die Überschrift "Hallo Du Da".
  • Falls ein Name angegeben wurde lautet die Überschrift "Hallo <name>".

Praxis - NodeJS - Aufgabe 4

Programmiert einen kleinen Rechner

  • Formular für zwei Operanden als auch die Rechenoperation
  • Werden die Parameter an den NodeJS Server übergeben, soll die durchzuführende Berechnung samt Ergebnis ausgegeben werden. Danach folgt wieder das Eingabeformular.

Zusatz

Bei Eingabe nicht-nummerischer Werte: Passende Fehlermeldung an passender Stelle

Praxis - NodeJS - Aufgabe 5

Zählt die Anzahl der Seitenaufrufe mit einer Instanz-Variable und gebt die Anzahl auf der Seite aus.

Zusatz

Unterscheidet verschiedene Nutzer und gebt spezifische Anzahl an Seitenaufrufen aus (Tipp: HTTP-Header, User-Agent)

Redirects/Weiterleitungen

Super Mario Princess is in another castle Joke

HTTP - Redirect

HTTP-Status Codes 3xx, Server-Antwort beinhaltet Header "Location" mit neuer URI

  • 301 Moved Permanently
    • diese und alle zukünftigen Anfragen sollen an neue URI gehen

HTTP - Redirect

HTTP-Status Codes 3xx, Server-Antwort beinhaltet Header "Location" mit neuer URI

  • 302 Found
    • Browser soll sich neue URI anschauen, in Zukunft aber wieder Original-URI
  • 307 Temporary Redirect
    • siehe 302, plus HTTP-Methode muss gleich bleiben

Express - Redirect

  • res.redirect([status,] path)
    • status: HTTP-Status Code (Default: 302), Number
    • path: Neue URL, String
      • Absolute URL: "https://example.com"
      • Host-relative URL: "/login"
        "http://localhost:8000/" -> "http://localhost:8000/login"

Gültigkeitsbereiche

Wie lange sind Variablen gültig?

  1. Template
    • Leben nur innerhalb eines Rendervorgangs
  2. Request
    • Leben so lange Anfrage bearbeitet wird
  3. Session
    • Leben so lange bis Session beendet wird
  4. Server
    • Leben so lange wie der Server läuft

Template

Variablen nur als Teil eines Rendervorgangs gültig

						
    <% it.users.forEach(function(user){ %>
  • <%= user.name %>
  • <% }) %>
  • "it": Gültig über den gesamten Rendervorgang
  • "user": Gültig innerhalb der Schleife

Request

Variablen nur als Teil eines Requests gültig

						app.get("/result", (req, res) => {
	const result = superComplicatedCalculation(req);
	res.render("result.eta", { result, method: req.method });
});

Variablen gültigkeit basierend auf dem
Javascript-Blockscope, hier:

  • "req, res": Immer vorhanden, von Express
  • "result": Selbst erstellte Variable

Session

Variablen nur als Teil einer Session gültig

						app.get("/", (req, res) => {
	const session = getSession(req);
	console.log(session);
	// { id: "a1b2c3d4f5", name: "Max Müller" }
});

Sessions müssen selbst implementiert werden,
z.B. mit Cookies

Solange Session aktiv ist, können darin Variablen gespeichert werden

  • "id": ID der Session
  • "name": Variable Teil der Session

Server

Variablen nur als Teil der Server-Instanz gültig

						const app = require("express")();
const port = 8000;

app.get("/", (_req, res) => {
	res.send(`Server listens on ${port}`);
});

app.listen(port);

Typischerweise genutzt für z.B. Konfiguration, Cache, Startparameter

  • "port": Selbst angelegte Variable

Praxis - NodeJS - Aufgabe 6

Programmiert einen kleinen Rechner

  • Formular für zwei Operanden und Rechenoperation
  • Werden die Parameter an den NodeJS Server übergeben, soll die durchzuführende Berechnung samt Ergebnis ausgegeben werden. Danach folgt wieder das Eingabeformular.
  • Im Falle eines Fehlers leitet zu Fehlerseite

Easter-Egg

Falls einer der Operanden '42' leitet auf eine Easter-Egg-Seite weiter.

Praxis - NodeJS - Aufgabe 7

Programmiert einen kleinen Rechner

  • Formular für zwei Operanden und Rechenoperation
  • Werden die Parameter an den NodeJS Server übergeben, soll die durchzuführende Berechnung samt Ergebnis ausgegeben werden. Danach folgt wieder das Eingabeformular.
  • Zeigt auf der Ergebnisseite die letzten fünf Berechnungen und ihr Ergebnis an.

Model-1-Entwurfsmuster

Model-1-Entwurfsmuster

Ähnliche Problemstellungen

Aufgaben die sich in den Request-Handlern wiederholen:

  • Speichern eines Datensatzes
    • Falls alle Daten korrekt: Detailansicht
    • Falls Fehler vorhanden: Formular mit Fehlermeldung
  • Löschen eines Datensatzes
    • Falls erlaubt: Listenansicht
    • Falls nicht erlaubt: Detailansicht mit Fehlermeldung

Model-1-Entwurfsmuster

  • Wohin mit den Aktionen?
    • gleiche Aktion kann zu unterschiedlichen Folgeseiten führen
    • Bei Aktionen überwiegt der Programmcode für die Logik, dem für die Darstellung.
  • Trennung von Aktionen und Ergebnisdarstellung!
    • Aktionen in getrennte Module verlagern!
    • Je nach Ergebnis der Aktion wird die richtige Folgeseite angesprungen

Model-2-Entwurfsmuster

Model-2-Entwurfsmuster

Model-2-Entwurfsmuster

  1. Browser empfängt Formular
  2. Browser sendet Formular an Controller
  3. Controller verarbeitet Aktion
  4. Controller übergibt an View
  5. View generiert Antwort
  6. Browser empfängt Antwort

Redirect

  • Indirektion über den Client (Redirect)
    • res.redirect(...)
    • Browser erhält die Aufforderung, die Folgeseite zu laden
    • URL im Browser wechselt!
Redirect

Forward

  • Serverinterne weitergabe (Forward)
    • res.render(...)
    • Server antwortet mit Inhalt auf der Folgeseite
    • URL im Browser bleibt stehen!
Forward

Express: Middlewares

Funktionen die Zugriff auf das request-, response-Objekt und die next Funktion haben:
(req, res, next) => void

Mit einem Aufruf von "next" wird der nächste in der Kette aufgerufen

Express: Middlewares Beispiel

						
					

Praxis - NodeJS - Aufgabe 8

Programmiert einen kleinen Rechner

  • Formular für zwei Operanden und Rechenoperation
  • Werden die Parameter an den NodeJS Server übergeben, soll die durchzuführende Berechnung samt Ergebnis ausgegeben werden. Danach folgt wieder das Eingabeformular.
  • Nutzt das Model-2-Entwurfsmuster.
  • Implementiert das 42-Easter-Egg mit einer Middleware

Cookies

Problem + Lösung

Problem: HTTP ist ein zustandsloses Protokoll

Bedeutet: Jede Anfrage muss alle Informationen beinhalten um die Antwort generieren zu können

?? Wie dann Sessions im Web anbieten ??

Ohne Sessions keine Warenkorb, kein Einloggen,
kein "Zuletzt betrachtet"

Client muss Info mitschicken, aber Server den Wert vorgeben

!! Cookies !!

Cookies: Ablauf

  1. Browser frägt Website an
  2. Server Antwort mit HTML-Dokument und ein Cookie
  3. Browser frägt nächste Website auf der gleichen Domain and und schickt das Cookie mit

Cookies: Funktionsweise

Spezifiziert als RFC 2109:
"HTTP State Management Mechanism"

HTTP-Header: "Set-Cookie" (2)

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: theme=light
Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT
					

darauf folgende Anfrage (3)

GET /spec.html HTTP/1.1
Host: www.example.org
Cookie: theme=light; sessionToken=abc123
…
					

Cookies: Anatomy

Einfaches Key=Value Paar, z.B.:
sessionToken=abc123

Value darf alle druckbare ASCII-Zeichen:
"!" bis "~" ohne: "," und ";"

Zusätzliche Attribute:
Domain+Path, Expires/Max-Age, Secure und HttpOnly
werden mit ";" hinter den Value gehängt

Nur das Key=Value Paar wird an den Server zurückgeschickt!

Cookies: Attribute

  • Domain und Path
    • Gibt den Geltungsbereich des Cookies an, z.B.:
      Domain=sub.example.com; Path=/accounts;
  • Expires und Max-Age
    • Gibt die Geltungsdauer des Cookies an, z.B.:
      Expires=Tue, 15 Jan 2042 13:37:00 GMT;
      oder: Max-Age: 3600;
    • Ohne: Session-Cookie (Client definiert)
  • Secure und HttpOnly
    • Secure: nur verschlüsselt Übertragen (HTTPS!)
    • HttpOnly: nur via HTTP(S) übertragen

Node.js - Cookies

Setzt einen Cookie in der HTTP-Antwort

Node.js - Cookies

Lese Cookie aus einem HTTP-Request

Middleware um Header zu parsen und an req-Objekt anzuhängen: cookie-parser

		
	

Browser - Cookies

  • document.cookie
    • String aller Cookies, ";"-Separiert, z.B.:
      'enwikimwuser-sessionId=e53e40071b987899a83b; enwikiwmE-sessionTickLastTickTime=1654374542173; enwikiwmE-sessionTickTickCount=83'
    • Einzelnen Cookie auslesen: von Hand parsen 🤮 (Leerzeichen nicht vergessen! 🤮🤮🤮)
    • Einen Cookie hinzufügen: einfache Zuweisung 🥳
      						document.cookie = "theme=light; Secure;"
      document.cookie = "user=Me; Secure;"
      document.cookie // theme=light; user=Me
      						
      					

Praxis - NodeJS - Aufgabe 9

Erweitert den Taschenrechner um ein kleines Login mit Session-spezifischer Historie

  • Login-Seite: Formular mit einem Textinput für den Usernamen. In der HTTP-Antwort soll ein Cookie mit Session-Id=Username gesetzt werden.
  • Berechnungs-Seite: Wenn Client einen Cookie mit Session-Id mitschickt soll eine User-spezifische Berechnungs-Historie angezeigt werden.

WebSockets

Living Standard der WHATWG: WebSockets

  • Leitideen
    • Protokoll verschlanken: TCP als Basis
    • Auch Nachricht von Server an Client
  • Vorgehen
    • Client baut Verbindung zum Server auf
    • Bidirektionaler Nachrichtenaustausch
    • Client oder Server schließt die Verbindung
  • Verbindungsaufbau
    • Verbindungsaufbau via HTTP
    • Websocket-Protokoll in URL: ws(s)

WebSockets Protokoll Overhead

WebSocket Protokoll

Nur wenige Byte Protokoll Overhead: 2 - 10 Byte

Socket?!?

Wie kann man sich einen Socket vorstellen?

Portals Portals

Raus: socket.send, Rein: socket.onmessage

WebSockets: Clientside (JS)

Native Support in Browsern via "WebSocket"

		// Neues WebSocket Objekt anlegen
const socket = new WebSocket('ws://localhost:8000/');

socket.onopen = function () {
	// Verbindungsaufbau behandeln
};
socket.onmessage = function(event) {
	console.log(event.data); // Neue Nachricht behandeln
};
socket.onclose = function(event) {
// Verbindungsabbau behandeln
};

socket.send("Hello Server");

WebSockets: Serverside (NodeJS)

Packet "ws" (Api-Docs)

						
					

Praxis - NodeJS - Aufgabe 10

Programmiert einen Eilmeldungs-Verteiler

  • Redaktions-Seite: Formular mit einem Textinput, welches die Nachricht an den Server schickt
  • Eilmeldungs-Seite: Empfängt neue Eilmeldungen via WebSocket und zeigt sie an
  • Server sammelt alle Client-Sockets und schickt ihnen die Nachrichten der Redaktions-Seite

NodeJS - Lösung - Aufgabe 10

Lösung

Node.js - Persistenz

Daten länger Speichern als der Server läuft

  1. Template
  2. Request
  3. Session
  4. Server
  5. Persistent
    • Leben so lange der Speicher lebt (Platte, DB)
    • Speicherort must erstmal nicht auf dem gleichen Server sein

Node.js - JSON

JSON-Dateien können require'd werden

Beispiel JSON-Datei

						
					

require JSON-Datei

						
					

Node.js - JSON

  • Vorteile
    • Nativer JSON-Support in JavaScript
    • Persistenter Dateispeicher
    • Dateien lassen sich einfach Versionieren, Sichern und Editieren
  • Nachteile
    • Read-Only
    • Konfigurationsdatei muss neben dem Code verfügbar sein

Node.js - Dateisystem

Node.js-Prozess hat Zugriff auf das Dateisystem als der User der ihn startet

Sicherheitsvorkehrung: Server niemals(!!) als Root/Administrator starten

Dateisystem-API: fs

Mehrere Geschmacksrichtungen:
Callback-API, Promise-API und Synchrone-API

Node.js - Dateizugriff

Lesen (readFile) und Schreiben (writeFile)

Node.js - Dateizugriff

Lesen (readFile) und Schreiben (writeFile)

Node.js - Database

Ökosystem bietet für (fast) alle Datenbanken
eine Bibliothek:

Praxis - NodeJS - Aufgabe 11

Erweitert das Taschenrechner-Projekt, um eine JSON-basierte Konfiguration:

  • Port: Auf welchem Port der Server horcht
  • Historienlänge: Wie viele Einträge die Historie speichert

Zusatz

Persistiert die Historie in einer Datei