Block 04 M295¶
Aufgabe zur Erstellung/Nutzung von Javascript Modulen¶
server.js:
// server.js
const http = require('http');
const url = require('url');
const math = require('./modules/mathModule'); // Importiert dein eigenes Modul
const PORT = 4010;
const server = http.createServer((req, res) => {
// Erstellt ein modernes URL-Objekt aus der Anfrage
// Wir nutzen den Host-Header, damit die URL für Node.js vollständig ist
const myUrl = new URL(req.url, `http://${req.headers.host}`);
// Prüfen, ob die aufgerufene Route /average ist
if (myUrl.pathname === '/average') {
// Extrahiert den String nach ?numbers= (z.B. "4,5,6")
const queryData = myUrl.searchParams.get('numbers');
if (queryData) {
// Wandelt den String "4,5,6" in ein Array [4, 5, 6] um
const numbersArray = queryData.split(',').map(Number);
// Nutzt die Funktion aus deinem mathModule
const result = math.calculateAverage(numbersArray);
// Antwort an den Browser senden
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`Average: ${result}`);
} else {
// Fehler, falls keine Zahlen übergeben wurden
res.writeHead(400);
res.end('Fehler: Bitte Zahlen angeben, z.B. ?numbers=4,5,6');
}
} else {
// Fehler, falls eine falsche URL aufgerufen wurde
res.writeHead(404);
res.end('Seite nicht gefunden. Nutze /average');
}
});
// Server starten und auf allen Netzwerk-Schnittstellen lauschen (0.0.0.0)
server.listen(PORT, '0.0.0.0', () => {
console.log(`Server läuft! Erreichbar unter:`);
console.log(`- Lokal: http://localhost:${PORT}/average?numbers=4,5,6`);
});
mathModules.js:
// mathModule.js
function calculateAverage(numbers) {
// Falls das Array leer ist, geben wir 0 zurück, um Fehler zu vermeiden
if (numbers.length === 0) return 0;
// Summiere alle Zahlen im Array auf
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
// Teile die Summe durch die Anzahl der Elemente
return sum / numbers.length;
}
// Exportiere die Funktion, damit server.js sie mit require() laden kann
module.exports = {
calculateAverage
};
Doku:
📂 Projektstruktur¶
Das Projekt ist in ein Hauptmodul (Server) und ein Hilfsmodul (Logik) unterteilt:
-
4.1/ (Root)
-
server.js(Einstiegspunkt) -
modules/
mathModule.js(Logik-Modul)
🛠️ Umsetzung¶
1. Das Modul (mathModule.js)¶
Dieses Modul enthält die Logik zur Berechnung des Durchschnitts. Es wurde darauf geachtet, dass auch leere Arrays abgefangen werden.
-
Funktion:
calculateAverage(numbers) -
Export: Über
module.exportsbereitgestellt.
JavaScript
function calculateAverage(numbers) {
if (numbers.length === 0) return 0;
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
return sum / numbers.length;
}
module.exports = { calculateAverage };
2. Der Server (server.js)¶
Der Server nutzt das native http-Modul von Node.js (ohne externe Libraries wie Express).
-
Port: 4010
-
Besonderheiten: - Verarbeitet URL-Parameter mittels
URL-Objekt. -
Konvertiert den Query-String (
1,10,20) in ein numerisches Array. -
Lauscht auf
0.0.0.0, um Anfragen aus dem lokalen Netzwerk (z. B. vom Raspberry Pi) zu erlauben.
JavaScript
const http = require('http');
const math = require('./modules/mathModule');
const server = http.createServer((req, res) => {
const myUrl = new URL(req.url, `http://${req.headers.host}`);
if (myUrl.pathname === '/average') {
const queryData = myUrl.searchParams.get('numbers');
if (queryData) {
const numbersArray = queryData.split(',').map(Number);
const result = math.calculateAverage(numbersArray);
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`Average: ${result}`);
} else {
res.writeHead(400);
res.end('Fehler: Bitte Zahlen angeben.');
}
}
});
server.listen(4010, '0.0.0.0');
🚀 Resultat & Test¶
Der Server wurde erfolgreich gestartet und ist über die IP-Adresse des Hosts erreichbar.
-
Beispiel-Aufruf:
http://10.27.160.12:4010/average?numbers=1,10,20 -
Output:
Average: 10.333333333333334
4.8 Bilder vom Server zum Frontend senden¶
1. Projektübersicht¶
1. Projektübersicht¶
Dieses Projekt implementiert eine leichtgewichtige Bilder-API mit Node.js und dem Framework Express. Der Server läuft auf einem Raspberry Pi und stellt Bilddateien (Lichtpulte) im lokalen Netzwerk zur Verfügung. Das System demonstriert die dynamische Datenbeschaffung über eine REST-Schnittstelle und das clientseitige Rendering einer Galerie.
2. Architektur & Dateistruktur¶
Die Anwendung ist extrem schlank gehalten und besteht aus nur zwei Hauptdateien:
-
server.js(Backend): Steuert die API-Routen, liest das lokale Dateisystem aus und liefert die Daten an den Browser. -
index.html(Frontend): Ruft die Daten asynchron vom Server ab und generiert daraus dynamisch die visuelle Bilder-Galerie. -
images/(Daten): Der physische Speicherort für die.jpeg-Dateien auf dem Raspberry Pi.
3. Backend: Die API-Endpunkte¶
Der Express-Server stellt drei zentrale Funktionen zur Verfügung:
-
Die Übersichts-Route (
/api/bilder): Dieser Endpunkt durchsucht denimages-Ordner mit dem Node-Modulfs(File System). Er filtert die Dateien nach Bildformaten und gibt ein strukturiertes JSON-Array mit allen Dateinamen zurück. -
Die Detail-Route per ID (
/api/bilder/:id): Hier wird das Konzept des "ID-basierten Routings" angewendet. Anstatt komplexe Dateinamen zu nutzen, wird jedes Bild über eine einfache Nummer (z. B./api/bilder/1) adressiert. Der Server berechnet den passenden Array-Index und nutzt den Befehlres.sendFile(), um direkt die rohe Bilddatei an den Browser zu senden. Dadurch wird das Bild ohne HTML-Wrapper in maximaler Grösse im Browser dargestellt. -
Statisches Hosting (
/images): Macht den Bildordner als statisches Verzeichnis für das Frontend zugänglich, damit die Galerie-Vorschaubilder geladen werden können.
4. Frontend: Die Client-Logik¶
Das Frontend (index.html) ist als Single-Page-Element aufgebaut:
-
Sobald die Seite lädt, wird die Fetch-API genutzt, um das JSON-Array vom Endpunkt
/api/bilderabzurufen. -
Eine JavaScript-Schleife (
forEach) iteriert durch die Liste und generiert für jedes Bild automatisch ein HTML-<img>-Tag. -
Jedes dieser Vorschaubilder wird mit einem Link (
<a>) umschlossen, der auf den dynamischen Detail-Endpunkt (z. B./api/bilder/1) verweist.
5. Netzwerkkonfiguration & Betrieb¶
-
Der Node.js-Server lauscht auf dem dedizierten Port 4010.
-
Durch die Bindung an die IP-Adresse
0.0.0.0im Code (app.listen(4010, '0.0.0.0')) wird sichergestellt, dass die API nicht nur lokal auf dem Raspberry Pi, sondern im gesamten lokalen Netzwerk erreichbar ist. -
Der Aufruf erfolgt über die IP des Raspis, beispielsweise:
http://10.27.160.12:4010.
4.14 Erstellung GET und POST Handler¶
Code server.js:
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const PORT = 3000;
const FILE_PATH = path.join(__dirname, 'quiz.json');
// Middleware, um JSON-Daten im Body zu verarbeiten
app.use(express.json());
// Statische Dateien (UI) ausliefern
app.use(express.static(path.join(__dirname, 'public')));
// Hilfsfunktion: Daten aus der Datei lesen
const readQuizData = () => {
try {
const data = fs.readFileSync(FILE_PATH, 'utf8');
return JSON.parse(data);
} catch (err) {
console.error("Fehler beim Lesen der Datei:", err);
// Struktur zurückgeben, die wir erwarten
return { quiz: {} };
}
};
// Hilfsfunktion: Daten in die Datei schreiben
const writeQuizData = (data) => {
fs.writeFileSync(FILE_PATH, JSON.stringify(data, null, 2));
};
// --- GET: Alle Quizfragen anzeigen ---
app.get('/quiz', (req, res) => {
const data = readQuizData();
res.status(200).json(data.quiz || {});
});
// --- POST: Eine neue Quizfrage hinzufügen ---
app.post('/quiz', (req, res) => {
const { category, question, options, answer } = req.body;
if (!category || !question || !options || !answer) {
return res.status(400).json({
success: false,
message: 'Fehlende Felder: category, question, options oder answer'
});
}
const data = readQuizData();
if (!data.quiz) data.quiz = {};
if (!data.quiz[category]) data.quiz[category] = {};
// neue Schlüsselnummer ermitteln (q1, q2, ...)
const keys = Object.keys(data.quiz[category]);
const lastNum = keys.reduce((max, k) => {
const m = parseInt(k.replace(/^q/, ''), 10);
return isNaN(m) ? max : Math.max(max, m);
}, 0);
const newKey = 'q' + (lastNum + 1);
data.quiz[category][newKey] = { question, options, answer };
try {
writeQuizData(data);
res.status(201).json({
success: true,
message: 'Frage erfolgreich hinzugefügt',
category,
key: newKey,
entry: data.quiz[category][newKey]
});
} catch (err) {
res.status(500).json({ success: false, message: 'Datei konnte nicht gespeichert werden.', error: err.message });
}
});
app.listen(PORT, () => {
console.log(`🚀 Server läuft auf http://localhost:${PORT}`);
console.log(`Öffne http://localhost:${PORT}/ in deinem Browser, um die UI zu sehen.`);
});
Struktur¶
-
server.js– Hauptserver mit GET/POST-Endpunkten und statischer UI-Auslieferung -
quiz.json– Speicherung aller Fragen in einer verschachtelten Struktur (quiz.<kategorie>.qN) -
public/index.html– einfache Benutzeroberfläche zur Anzeige und Eingabe von Fragen -
package.json– Abhängigkeiten und Startskript
Nutzung¶
- Abhängigkeiten installieren
- Server starten
- Browser öffnen unter
http://localhost:3000/um die UI zu verwenden. Die Anwendung liefert außerdem JSON unter derselben Adresse.
API¶
GET /quiz¶
Liefert alle Fragen im Format:
{
"sport": {
"q1": { "question": "...", "options": [...], "answer": "..." },
...
},
"maths": { ... }
}
POST /quiz¶
Ein neuer Eintrag kann als JSON-Body gesendet werden:
Die Kategorie wird bei Bedarf angelegt, die Frage erhält automatisch den nächsten Schlüssel (q1, q2 …). Response:
{ "success": true, "message": "Frage erfolgreich hinzugefügt", "category": "sport", "key": "q3", "entry": { ... } }
Bei fehlenden Feldern wird ein 400-Fehler mit passender Nachricht zurückgegeben.
UI¶
Die Seite zeigt alle verfügbaren Fragen nach Kategorien geordnet. Ein Formular erlaubt das Hinzufügen neuer Fragen ohne Tools wie Postman.
Hinweis: Änderungen werden direkt in
quiz.jsongeschrieben.




