Zum Inhalt

Block 04 M295

Aufgabe zur Erstellung/Nutzung von Javascript Modulen

Bildschirmfoto 2026-02-24 um 14.48.15.png

Bildschirmfoto 2026-02-24 um 14.51.52.png

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.exports bereitgestellt.

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

Bildschirmfoto 2026-02-24 um 15.55.40.png Bildschirmfoto 2026-02-24 um 16.24.43.png

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 den images-Ordner mit dem Node-Modul fs (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 Befehl res.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/bilder abzurufen.

  • 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.0 im 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

Bildschirmfoto 2026-02-24 um 17.14.15.png

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

  1. Abhängigkeiten installieren
npm install
  1. Server starten
npm start
  1. 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:

{
"category": "sport",
"question": "Wie lautet ...?",
"options": ["A","B","C","D"],
"answer": "A"
}

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.json geschrieben.