From a26dd68636da30d4746911d2b700c0cebf63e074 Mon Sep 17 00:00:00 2001 From: Teena Steger Date: Tue, 5 May 2026 15:00:12 +0200 Subject: [PATCH 1/2] =?UTF-8?q?07:=20L=C3=B6sungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/07/labor/07_loesungen/uebung0102.go | 161 ++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 web/07/labor/07_loesungen/uebung0102.go diff --git a/web/07/labor/07_loesungen/uebung0102.go b/web/07/labor/07_loesungen/uebung0102.go new file mode 100644 index 0000000..d7f0c94 --- /dev/null +++ b/web/07/labor/07_loesungen/uebung0102.go @@ -0,0 +1,161 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "net/http" +) + +type Registrierung struct { + Vorname string `json:"vorname"` + Nachname string `json:"nachname"` + Email string `json:"email"` + Telefon string `json:"telefon"` + Sessions []string `json:"sessions"` + AGBAkzeptiert string `json:"agb"` + Newsletter string `json:"newsletter"` + Equipment string `json:"equipment"` + Format string `json:"format"` +} + +type workshopHandler int + +func parseCheckboxValue(value string) string { + if value == "" || (value != "ja") { + return "nein" + } + return "ja" +} + +func parseRadiobuttonValue(value string) (string, error) { + if value == "" || (value != "praesenz" && value != "online") { + return "", errors.New("Auswahl nicht erlaubt.") + } + return value, nil +} + +func (worksh workshopHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusNoContent) + return + } + + if r.Method != http.MethodPost { + http.Error(w, "Nur POST-Anfragen erlaubt", http.StatusMethodNotAllowed) + return + } + + var data Registrierung + + contentType := r.Header.Get("Content-Type") + + if contentType == "application/json" { + // JSON-Daten verarbeiten + decoder := json.NewDecoder(r.Body) + if err := decoder.Decode(&data); err != nil { + http.Error(w, "Ungültiges JSON", http.StatusBadRequest) + return + } + if data.Vorname == "" { + http.Error(w, "Pflichtfeld fehlt.", http.StatusBadRequest) + return + } + if data.Nachname == "" { + http.Error(w, "Pflichtfeld fehlt.", http.StatusBadRequest) + return + } + data.AGBAkzeptiert = parseCheckboxValue(data.AGBAkzeptiert) + if data.AGBAkzeptiert == "nein" { + http.Error(w, "AGB wurde nicht akzeptiert.", http.StatusBadRequest) + return + } + data.Newsletter = parseCheckboxValue(data.Newsletter) + data.Equipment = parseCheckboxValue(data.Equipment) + format, err := parseRadiobuttonValue(data.Format) + if err != nil { + http.Error(w, "Ungültige Auswahl des Formats.", http.StatusBadRequest) + return + } + data.Format = format + } else { + // Formulardaten parsen und Überprüfen, ob ungültige Formulardaten gesendet wurde + if err := r.ParseForm(); err != nil { + http.Error(w, "Fehler beim Parsen des Formulars", http.StatusBadRequest) + return + } + + // Überprüfen, ob leeres Formular gesendet wurde + if len(r.PostForm) == 0 { + http.Error(w, "Kein Formular gesendet", http.StatusBadRequest) + return + } + + // Formulardaten auslesen und ggf. überprüfen + vorname := r.PostForm.Get("vorname") + if vorname == "" { + http.Error(w, "Pflichtfeld fehlt.", http.StatusBadRequest) + return + } + nachname := r.PostForm.Get("nachname") + if nachname == "" { + http.Error(w, "Pflichtfeld fehlt.", http.StatusBadRequest) + return + } + email := r.PostForm.Get("email") + telefon := r.PostForm.Get("telefon") + sessions := r.PostForm["sessions"] + agb := parseCheckboxValue(r.PostForm.Get("agb")) + if agb == "nein" { + http.Error(w, "AGB wurde nicht akzeptiert.", http.StatusBadRequest) + return + } + newsletter := parseCheckboxValue(r.PostForm.Get("newsletter")) + equipment := parseCheckboxValue(r.PostForm.Get("equipment")) + format, err := parseRadiobuttonValue(r.PostForm.Get("format")) + if err != nil { + http.Error(w, "Ungültige Auswahl des Formats.", http.StatusBadRequest) + return + } + + data = Registrierung{ + Vorname: vorname, + Nachname: nachname, + Email: email, + Telefon: telefon, + Sessions: sessions, + AGBAkzeptiert: agb, + Newsletter: newsletter, + Equipment: equipment, + Format: format, + } + } + + // Ausgabe im Terminal + fmt.Fprintf(w, "Neue Registrierung erhalten:\n") + fmt.Fprintf(w, "Vorname: %s\n", data.Vorname) + fmt.Fprintf(w, "Nachname: %s\n", data.Nachname) + fmt.Fprintf(w, "E-Mail: %s\n", data.Email) + fmt.Fprintf(w, "Telefon: %s\n", data.Telefon) + fmt.Fprintf(w, "Bevorzugte Sessions: \n") + for _, session := range data.Sessions { + fmt.Fprintf(w, " - %s\n", session) + } + fmt.Fprintf(w, "AGB akzeptiert: %s\n", data.AGBAkzeptiert) + fmt.Fprintf(w, "Newsletter: %s\n", data.Newsletter) + fmt.Fprintf(w, "Equipment benötigt: %s\n", data.Equipment) + fmt.Fprintf(w, "Teilnahmeformat: %s", data.Format) + +} + +func main() { + var workshop workshopHandler + fmt.Println("Server läuft auf localhost:8080...") + log.Fatal(http.ListenAndServe(":8080", workshop)) +} From 7aaf855f4e791f742d0efc637e225a7d07df986f Mon Sep 17 00:00:00 2001 From: Teena Steger Date: Tue, 5 May 2026 15:00:43 +0200 Subject: [PATCH 2/2] 08: Demos und Labor --- web/08/demos/merkzettel/06_merkliste.html | 21 ++++ web/08/demos/merkzettel/06_session.go | 64 +++++++++++++ web/08/demos/merkzettel/go.mod | 5 + web/08/demos/merkzettel/go.sum | 2 + .../routingcookies/01_routing_servemux.go | 26 +++++ .../02_routing_defaultservemux.go | 25 +++++ .../routingcookies/03_routing_handlefunc.go | 20 ++++ .../04_marshal_unmarshal_json.go | 35 +++++++ web/08/demos/routingcookies/05_cookies.go | 48 ++++++++++ web/08/labor/08_aufgaben.md | 17 ++++ web/08/labor/anleitung_extlib.md | 26 +++++ web/08/labor/nickname.json | 96 +++++++++++++++++++ 12 files changed, 385 insertions(+) create mode 100644 web/08/demos/merkzettel/06_merkliste.html create mode 100644 web/08/demos/merkzettel/06_session.go create mode 100644 web/08/demos/merkzettel/go.mod create mode 100644 web/08/demos/merkzettel/go.sum create mode 100644 web/08/demos/routingcookies/01_routing_servemux.go create mode 100644 web/08/demos/routingcookies/02_routing_defaultservemux.go create mode 100644 web/08/demos/routingcookies/03_routing_handlefunc.go create mode 100644 web/08/demos/routingcookies/04_marshal_unmarshal_json.go create mode 100644 web/08/demos/routingcookies/05_cookies.go create mode 100644 web/08/labor/08_aufgaben.md create mode 100644 web/08/labor/anleitung_extlib.md create mode 100644 web/08/labor/nickname.json diff --git a/web/08/demos/merkzettel/06_merkliste.html b/web/08/demos/merkzettel/06_merkliste.html new file mode 100644 index 0000000..103ea45 --- /dev/null +++ b/web/08/demos/merkzettel/06_merkliste.html @@ -0,0 +1,21 @@ + + + + + + + Bücher-Merkliste + + + +

Eigenen Merkzettel anlegen

+
+ + + +
+
+ Gehe zu Merkzettel + + + \ No newline at end of file diff --git a/web/08/demos/merkzettel/06_session.go b/web/08/demos/merkzettel/06_session.go new file mode 100644 index 0000000..ef52b95 --- /dev/null +++ b/web/08/demos/merkzettel/06_session.go @@ -0,0 +1,64 @@ +package main + +import ( + "fmt" + "net/http" + "sync" + + "github.com/google/uuid" +) + +// Session-Speicher (Map von SessionID -> Merkzettel) +var ( + sessions = make(map[string][]string) + mu sync.Mutex +) + +// Hilfsfunktion: Session-ID aus Cookie holen oder neue erstellen +func getSessionID(w http.ResponseWriter, r *http.Request) string { + cookie, err := r.Cookie("session_id") + if err != nil { + // Neue Session-ID erzeugen (UUID) + newID := uuid.New().String() + http.SetCookie(w, &http.Cookie{ + Name: "session_id", + Value: newID, + }) + mu.Lock() + sessions[newID] = []string{} // leeren Merkzettel anlegen + mu.Unlock() + return newID + } + return cookie.Value +} + +// Buch zum Merkzettel hinzufügen +func addToMerkzettel(w http.ResponseWriter, r *http.Request) { + sessionID := getSessionID(w, r) + book := r.URL.Query().Get("book") + if book == "" { + fmt.Fprintln(w, "Bitte Buchtitel angeben: ?book=XYZ") + return + } + mu.Lock() + sessions[sessionID] = append(sessions[sessionID], book) + mu.Unlock() + fmt.Fprintf(w, "Buch '%s' zum Merkzettel hinzugefügt!\n", book) +} + +// Merkzettel anzeigen +func showMerkzettel(w http.ResponseWriter, r *http.Request) { + sessionID := getSessionID(w, r) + mu.Lock() + books := sessions[sessionID] + mu.Unlock() + fmt.Fprintf(w, "Dein Merkzettel: %v\n", books) +} + +func main() { + http.HandleFunc("/add", addToMerkzettel) + http.HandleFunc("/list", showMerkzettel) + + fmt.Println("Server läuft auf http://localhost:8080") + http.ListenAndServe(":8080", nil) +} diff --git a/web/08/demos/merkzettel/go.mod b/web/08/demos/merkzettel/go.mod new file mode 100644 index 0000000..f998da4 --- /dev/null +++ b/web/08/demos/merkzettel/go.mod @@ -0,0 +1,5 @@ +module demo/sessions + +go 1.24.5 + +require github.com/google/uuid v1.6.0 diff --git a/web/08/demos/merkzettel/go.sum b/web/08/demos/merkzettel/go.sum new file mode 100644 index 0000000..7790d7c --- /dev/null +++ b/web/08/demos/merkzettel/go.sum @@ -0,0 +1,2 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/web/08/demos/routingcookies/01_routing_servemux.go b/web/08/demos/routingcookies/01_routing_servemux.go new file mode 100644 index 0000000..cf9c41e --- /dev/null +++ b/web/08/demos/routingcookies/01_routing_servemux.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "net/http" +) + +type appleHandler int +type bananaHandler string + +func (a appleHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Apple 🍎") +} + +func (b bananaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Banana 🍌") +} + +func main() { + var a appleHandler + var b bananaHandler + mux := http.NewServeMux() + mux.Handle("/apple", a) + mux.Handle("/banana", b) + http.ListenAndServe("localhost:8080", mux) +} diff --git a/web/08/demos/routingcookies/02_routing_defaultservemux.go b/web/08/demos/routingcookies/02_routing_defaultservemux.go new file mode 100644 index 0000000..08cc906 --- /dev/null +++ b/web/08/demos/routingcookies/02_routing_defaultservemux.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "net/http" +) + +type appleDefaultHandler int +type bananaDefaultHandler string + +func (a appleDefaultHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Apple") +} + +func (b bananaDefaultHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Banana") +} + +func main() { + var a appleDefaultHandler + var b bananaDefaultHandler + http.Handle("/apple", a) + http.Handle("/banana", b) + http.ListenAndServe("localhost:8080", nil) +} diff --git a/web/08/demos/routingcookies/03_routing_handlefunc.go b/web/08/demos/routingcookies/03_routing_handlefunc.go new file mode 100644 index 0000000..4774176 --- /dev/null +++ b/web/08/demos/routingcookies/03_routing_handlefunc.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "net/http" +) + +func appleFunc(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "🍎 Apple func antwortet") +} + +func bananaFunc(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "🍌 Banana func antwortet") +} + +func main() { + http.HandleFunc("/apple", appleFunc) + http.HandleFunc("/banana", bananaFunc) + http.ListenAndServe("localhost:8080", nil) +} diff --git a/web/08/demos/routingcookies/04_marshal_unmarshal_json.go b/web/08/demos/routingcookies/04_marshal_unmarshal_json.go new file mode 100644 index 0000000..dcc2859 --- /dev/null +++ b/web/08/demos/routingcookies/04_marshal_unmarshal_json.go @@ -0,0 +1,35 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type User struct { + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Age int `json:"age"` +} + +func encode(w http.ResponseWriter, r *http.Request) { + mariam := User{ + Firstname: "Mariam", + Lastname: "Okonkwo", + Age: 25, + } + + json.NewEncoder(w).Encode(mariam) +} + +func decode(w http.ResponseWriter, r *http.Request) { + var user User + json.NewDecoder(r.Body).Decode(&user) + fmt.Fprintf(w, "%s %s is %d years old!", user.Firstname, user.Lastname, user.Age) +} + +func main() { + http.HandleFunc("/encode", encode) + http.HandleFunc("/decode", decode) + http.ListenAndServe("localhost:8080", nil) +} diff --git a/web/08/demos/routingcookies/05_cookies.go b/web/08/demos/routingcookies/05_cookies.go new file mode 100644 index 0000000..3ddb9dc --- /dev/null +++ b/web/08/demos/routingcookies/05_cookies.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "net/http" +) + +func start(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Setze Cookie") + fmt.Fprintln(w, "Zeige Cookie") + fmt.Fprintln(w, "Lösche Cookie") +} + +func setCookie(w http.ResponseWriter, r *http.Request) { + c := &http.Cookie{Name: "lang", Value: "de"} + http.SetCookie(w, c) + fmt.Fprintln(w, "

Cookie gesetzt. Überprüfe in Dev-Tools oder hier anzeigen oder hier löschen

") +} + +func getCookie(w http.ResponseWriter, r *http.Request) { + c, err := r.Cookie("lang") + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + fmt.Fprintln(w, "Cookie: lang = ", c.Value) +} + +func clearCookie(w http.ResponseWriter, r *http.Request) { + c, err := r.Cookie("lang") + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + c.MaxAge = -1 + http.SetCookie(w, c) + fmt.Fprintln(w, "Cookie gelöscht: lang") +} + +func main() { + http.HandleFunc("/", start) + http.HandleFunc("/set", setCookie) + http.HandleFunc("/get", getCookie) + http.HandleFunc("/clear", clearCookie) + + fmt.Println("Server läuft auf http://localhost:8080") + http.ListenAndServe(":8080", nil) +} diff --git a/web/08/labor/08_aufgaben.md b/web/08/labor/08_aufgaben.md new file mode 100644 index 0000000..ca5571d --- /dev/null +++ b/web/08/labor/08_aufgaben.md @@ -0,0 +1,17 @@ +# Übungsblatt 08 + +## 1. Go-Übung: Routing + +**Aufgabenstellung**: Erstellen Sie ein einfaches Webserver-Programm in Go, das zwei HTTP-Endpunkte `/login` und `/logout` bereitstellt. Beim Aufruf dieser Pfade soll jeweils eine passende Textnachricht im Browser erscheinen. Verwenden Sie dazu die Standardbibliothek `net/http` und definieren Sie eigene Funktionen zur Übergabe an `HandleFunc`. + + +## 2. Go-Übung: Cookies + +**Aufgabenstellung**: Erstellen Sie ein einfaches Webserver-Programm in Go, das drei HTTP-Endpunkte `/create-cookie`, `/show-cookie` und `/delete-cookie` bereitstellt. Beim Aufruf dieser Pfade soll entweder ein neuer Cookie mit dem Namen `keks` und einer generierten UUID als Wert erstellt, der Wert im Browser ausgegeben oder der Cookie ganz gelöscht werden. Außerdem soll jeweils eine passende Textnachricht im Browser erscheinen. + +## 3. Go-Übung: Sessions + +**Aufgabenstellung**: Erstellen Sie ein Webserver-Programm in Go, das die API-Spezifikation [nickname.json](nickname.json) erfüllt. + +#### Hinweis zu 2. und 3. +Um externe Bibliotheken (z.B. `github.com/google/uuid` zur Erstellung von UUIDs) verwenden zu können, müssen Sie zunächst ein Modul definieren und die Abhängigkeiten darüber verwalten. Eine kurze Anleitung dazu finden Sie hier: [Abhängigkeiten mit go.mod](anleitung_extlib.md) diff --git a/web/08/labor/anleitung_extlib.md b/web/08/labor/anleitung_extlib.md new file mode 100644 index 0000000..066e4ea --- /dev/null +++ b/web/08/labor/anleitung_extlib.md @@ -0,0 +1,26 @@ +# Abhängigkeitsverwaltung mit `go.mod` + +## Hintergrund + +Externe Pakete, die im Code importiert werden, werden über eine `go.mod`‑Datei verwaltet. Diese Datei definiert das Modul und listet alle Abhängigkeiten auf. Sie gehört fest zum Projekt und wird im Quellcode‑Repository mitgeführt. + +## Anweisungen + +*Annahme*: Go-Programm ist bereits vorhanden und die Imports im Code sind gesetzt. Außerdem befinden Sie sich in dem Ordner mit dem Go-Programm. + +1. **Modul initialisieren (falls noch nicht geschehen)** +```bash +go mod init example.com/myapp +``` +Der Modulpfad entspricht in der Praxis meist der Repository‑Adresse, z. B. `github.com/mymodule`. Für einfache Beispiele genügt ein Platzhalter wie z.B. `example.com/myapp` + +2. **Abhängigkeiten auflösen und aufräumen** +```bash +go mod tidy +``` +Dadurch werden alle im Code verwendeten Imports heruntergeladen und ins `go.mod` eingetragen. Nicht mehr benötigte Pakete werden entfernt. + +3. **Programm starten** +```bash +go run . +``` \ No newline at end of file diff --git a/web/08/labor/nickname.json b/web/08/labor/nickname.json new file mode 100644 index 0000000..534b87c --- /dev/null +++ b/web/08/labor/nickname.json @@ -0,0 +1,96 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Workshop API", + "version": "1.0.0" + }, + "servers": [ + { + "url": "localhost:8080" + } + ], + "paths": { + "/signup": { + "post": { + "summary": "Erstellt eine Session für eine:n Benutzer:in.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserData" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/html": { + "schema": { + "type": "string", + "example": "Daten konnten nicht verarbeitet werden." + } + } + } + } + } + } + }, + "/whoami": { + "get": { + "summary": "Liefert eine Nachricht an eine:n Benutzer:in zurück.", + "description": "Falls der/die Benutzer:in sich über einen signup-Request bereits registriert hat, erhält er/sie eine Nachricht unter Nennung des Spitznamens und der Information, ob er/sie als Admin registriert ist.", + "responses": { + "200": { + "description": "Nachricht an registrierte Benutzer:innen", + "content": { + "text/html": { + "schema": { + "type": "string" + }, + "example": "Hallo Karla!\nDu bist als Admin registriert." + } + } + } + } + } + } + }, + "components": { + "schemas": { + "UserData": { + "type": "object", + "required": [ + "username", + "nickname" + ], + "properties": { + "username": { + "type": "string", + "example": "fischauge12345" + }, + "nickname": { + "type": "string", + "example": "Karla" + }, + "admin": { + "type": "boolean", + "example": true + } + } + } + } + } +} \ No newline at end of file