Merge remote-tracking branch 'upstream/main'

main
Oliver Stolle 2026-04-28 16:01:32 +02:00
commit 6bab3f2abe
20 changed files with 887 additions and 0 deletions

View File

@ -0,0 +1,8 @@
## 1. Lösung
1.1. Antonette
1.2 10
1.3 sunt aut facere repellat provident occaecati excepturi optio reprehenderit
1.4 5
1.5 Ja
1.6 `curl 'https://jsonplaceholder.typicode.com/posts?userId=2`

View File

@ -0,0 +1,13 @@
## 2. Lösung
1. 68eb4a0b-6c49-44e9-aec8-5af947d22141
```bash
curl https://hp-api.onrender.com/api/characters/staff
```
2.
```bash
https://hp-api.onrender.com/api/character/68eb4a0b-6c49-44e9-aec8-5af947d22141
```
3. blond

View File

@ -0,0 +1,36 @@
## 3. Lösung
#### Staudenpflanzen
```json
[
{
"_id": "68fa239f7037b603e8a5af57",
"name": "Lavendel",
"botanischer_name": "Lavandula angustifolia",
"standort": "Sonnig, trocken",
"besonderheiten": "Duftend, bienenfreundlich, winterhart"
},
{
"_id": "68fa23d77037b603e8a5af59",
"name": "Funkie",
"botanischer_name": "Hosta",
"standort": "Halbschatten bis Schatten",
"besonderheiten": "Dekoratives Laub, schneckenanfällig, robust"
},
{
"_id": "68fa23de7037b603e8a5af5a",
"name": "Sonnenhut",
"botanischer_name": "Echinacea purpurea",
"standort": "Sonnig",
"besonderheiten": "Heilpflanze, zieht Schmetterlinge, schneckenanfällig"
},
{
"_id": "68fa23e67037b603e8a5af5b",
"name": "Tränendes Herz",
"botanischer_name": "Lamprocapnos spectabilis",
"standort": "Halbschatten",
"besonderheiten": "Herzförmige Blüten, romantisch, giftig"
}
]
```

View File

@ -0,0 +1,60 @@
## 4. Lösung
1.
```json
{
"name": "Alex",
"alter": 30,
"wohnort": "Berlin"
}
```
2.
```json
{
"produktName": "Kopfhörer",
"preis": 89.99,
"verfuegbarkeit": true,
"farben": ["schwarz", "weiß"]
}
3.
```json
{
"benutzername": "maria1999",
"passwort": "abc123",
"rolle": "Admin",
"adresse": {
"strasse": "Bauernring 15",
"stadt": "Weilbach",
"plz": "12345"
}
}
```
4.
```json
[
{
"titel": "Einkaufen gehen",
"erledigt": false
},
{
"titel": "Rechnung bezahlen",
"erledigt": true
}
]
```
5.
```json
{
"titel": "Was ist VRNnextbike?",
"autor": "VRN Redaktion",
"inhalt": "Nutzer:innen können Fahrräder per App ausleihen und zurückgeben. Die Registrierung ist kostenlos, lediglich eine Zahlungsmittel-Verifizierung ist nötig.",
"kommentar": {
"autor": "Tilla Tränenreich",
"text": "Toller Beitrag! Ich werde mich morgen selbst registrieren und es ausprobieren!"
}
}
```

View File

@ -0,0 +1,112 @@
{
"openapi": "3.0.0",
"info": {
"title": "Simple Greeting API",
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:8080"
}
],
"paths": {
"/hello": {
"get": {
"summary": "Returns a greeting",
"parameters": [
{
"name": "name",
"in": "query",
"required": true,
"description": "Name of the person to greet",
"schema": {
"type": "string"
},
"example": "Deepak"
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"text/plain": {
"schema": {
"type": "string"
},
"example": "Hello, Deepak!"
}
}
}
}
},
"post": {
"summary": "Accepts a plain text name and returns a plain text greeting",
"requestBody": {
"required": true,
"content": {
"text/plain": {
"schema": {
"type": "string"
},
"example": "Deepika"
}
}
},
"responses": {
"200": {
"description": "Plain text greeting response",
"content": {
"text/plain": {
"schema": {
"type": "string"
},
"example": "Hello, Deepika!"
}
}
}
}
}
},
"/hello-json": {
"post": {
"summary": "Accepts a json with name and age and returns a plain text greeting",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"example": "Florian"
},
"age": {
"type": "integer",
"example": 23
}
}
}
}
}
},
"responses": {
"200": {
"description": "Plain text greeting response",
"content": {
"text/plain": {
"schema": {
"type": "string"
},
"example": "Hello, 23-year-old Florian!"
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,48 @@
package main
import (
"encoding/json"
"os"
)
type Book struct {
ID int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
Read bool `json:"read"`
}
type Library struct {
Books []Book `json:"books"`
}
func (lib *Library) Add(title, author string) {
id := len(lib.Books) + 1
lib.Books = append(lib.Books, Book{ID: id, Title: title, Author: author, Read: false})
}
func (lib *Library) MarkRead(id int) {
for i := range lib.Books {
if lib.Books[i].ID == id {
lib.Books[i].Read = true
}
}
}
func (lib *Library) Save(filename string) error {
data, err := json.MarshalIndent(lib, "", " ")
if err != nil {
return err
}
return os.WriteFile(filename, data, 0644)
}
func LoadLibrary(filename string) (*Library, error) {
data, err := os.ReadFile(filename)
if err != nil {
return &Library{}, nil
}
var lib Library
err = json.Unmarshal(data, &lib)
return &lib, err
}

View File

@ -0,0 +1,16 @@
{
"books": [
{
"id": 1,
"title": "Der Steppenwolf",
"author": "Hermann Hesse",
"read": false
},
{
"id": 2,
"title": "Clean Code",
"author": "Robert C. Martin",
"read": true
}
]
}

View File

@ -0,0 +1,50 @@
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: add <title> <author> | list | read <id>")
return
}
lib, _ := LoadLibrary("books.json")
switch os.Args[1] {
case "add":
if len(os.Args) < 4 {
fmt.Println("Usage: add <title> <author>")
return
}
lib.Add(os.Args[2], os.Args[3])
lib.Save("books.json")
fmt.Println("Buch hinzugefügt.")
case "list":
for _, b := range lib.Books {
status := " "
if b.Read {
status = "✓"
}
fmt.Printf("[%s] %d: \"%s\" by %s\n", status, b.ID, b.Title, b.Author)
}
case "read":
if len(os.Args) < 3 {
fmt.Println("Geben Sie eine Buch-ID ein.")
return
}
id, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Ungültige ID-Eingabe.")
return
}
lib.MarkRead(id)
lib.Save("books.json")
fmt.Println("Buch als gelesen markiert.")
}
}

View File

@ -0,0 +1,119 @@
{
"openapi": "3.0.0",
"info": {
"title": "Workshop API",
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:8081"
}
],
"paths": {
"/registrierung": {
"post": {
"summary": "Sends all information needed for registering at a workshop",
"requestBody": {
"required": false,
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/RegistrierungForm"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegistrierungForm"
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/html": {
"schema": {
"type": "string"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"text/plain": {
"schema": {
"type": "string",
"example": "Kein Formular gesendet"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"RegistrierungForm": {
"type": "object",
"required": [
"vorname",
"nachname",
"agb",
"format"
],
"properties": {
"vorname": {
"type": "string",
"example": "Zoya"
},
"nachname": {
"type": "string",
"example": "Akhtar"
},
"email": {
"type": "string",
"example": "z.akhtar@test.de"
},
"telefon": {
"type": "string",
"example": "Zoya"
},
"sessions": {
"type": "array",
"items": {
"type": "string",
"example": "vormittag"
},
"example": [
"vormittag",
"nachmittag"
]
},
"agb": {
"type": "string",
"enum": ["ja"],
"example": "ja"
},
"newsletter": {
"type": "string",
"enum": ["ja"],
"example": "ja"
},
"equipment": {
"type": "string",
"enum": ["ja"],
"example": "ja"
},
"format": {
"type": "string",
"enum": ["online","praesenz"],
"example": "online"
}
}
}
}
}
}

View File

@ -0,0 +1,26 @@
package main
import "fmt"
type Person struct {
Name string
Age int
}
// Wert-Receiver: arbeitet auf einer Kopie
func (p Person) Greet() {
fmt.Println("Hallo,", p.Name)
}
// Zeiger-Receiver: kann das Original ändern
func (p *Person) Birthday() {
p.Age++
}
func main() {
a := Person{Name: "Karl", Age: 30}
a.Greet() // Ausgabe: Hallo, Karl
a.Birthday() // Alter wird erhöht
fmt.Println(a.Age) // Ausgabe: 31
}

View File

@ -0,0 +1,18 @@
package main
import (
"fmt"
"net/http"
)
type helloHandler int
func (hello helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World %d!", hello)
}
func main() {
var world helloHandler
world = 42
http.ListenAndServe("localhost:8080", world)
}

View File

@ -0,0 +1,19 @@
package main
import (
"net/http"
)
type respExampleHandler int
func (hello respExampleHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("my-http-header", "Ein beliebiger Text")
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusFound)
w.Write([]byte("<h1>Dies ist eine HTML-Überschrift<h1>"))
}
func main() {
var r respExampleHandler
http.ListenAndServe("localhost:8080", r)
}

View File

@ -0,0 +1,36 @@
package main
import (
"fmt"
"net/http"
)
type reqExampleHandler int
func (hello reqExampleHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Only GET requests are allowed", http.StatusMethodNotAllowed)
return
}
fmt.Println("Method:", r.Method)
fmt.Println("URL:", r.URL.String())
fmt.Println("Host:", r.Host)
fmt.Println("Accept-Header", r.Header.Get("Accept"))
queryParams := r.URL.Query()
fmt.Println("Query Parameters:")
for key, values := range queryParams {
for _, value := range values {
fmt.Printf(" %s = %s\n", key, value)
}
}
w.Write([]byte("<h1>GET: http.Request-Beispiel<h1>"))
}
func main() {
var r reqExampleHandler
http.ListenAndServe("localhost:8080", r)
}

View File

@ -0,0 +1,47 @@
package main
import (
"fmt"
"net/http"
)
type formExampleHandler int
func (formHandler formExampleHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST requests are allowed", http.StatusMethodNotAllowed)
return
}
// 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 POST-Formular gesendet", http.StatusBadRequest)
return
}
// Zugriff auf PostForm-Daten
// string mit Wert des Feldes "name"
name := r.PostForm.Get("name")
// []string mit allen ausgewählten Werten der Checkbox "hobby"
hobbies := r.PostForm["hobby"]
// Ausgabe der Werte
fmt.Fprintf(w, "Name: %s\n", name)
fmt.Fprintf(w, "Hobbies:\n")
for _, h := range hobbies {
fmt.Fprintf(w, "- %s\n", h)
}
}
func main() {
var r formExampleHandler
http.ListenAndServe("localhost:8080", r)
}

View File

@ -0,0 +1,28 @@
package main
import (
"encoding/json"
"net/http"
)
type jsonEncodeHandler int
type User struct {
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Age int `json:"age"`
}
func (jsonHandler jsonEncodeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mariam := User{
Firstname: "Mariam",
Lastname: "Okonkwo",
Age: 25,
}
json.NewEncoder(w).Encode(mariam)
}
func main() {
var enc jsonEncodeHandler
http.ListenAndServe("localhost:8080", enc)
}

View File

@ -0,0 +1,26 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type jsonDecodeHandler int
type User struct {
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Age int `json:"age"`
}
func (jsonHandler jsonDecodeHandler) ServeHTTP(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() {
var dec jsonDecodeHandler
http.ListenAndServe("localhost:8080", dec)
}

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Einfaches Formular für Backend</title>
</head>
<body>
<form method="POST" action="http://localhost:8080">
<label for="nameLabel">Name</label>
<input name="name" id="nameLabel" />
<p>Hobbies:
<label><input type="checkbox" name="hobby" value="lesen"> Lesen</label>
<label><input type="checkbox" name="hobby" value="kochen"> Kochen</label>
<label><input type="checkbox" name="hobby" value="wandern"> Wandern</label>
</p>
<button type="submit">Absenden</button>
</form>
</body>
</html>

View File

@ -0,0 +1,18 @@
# Übungsblatt 07
## 1. Go-Übung: Formulardaten empfangen
**Aufgabenstellung**: Erstellen Sie ein Webserver-Programm in Go, das einen Webserver auf der Adresse `localhost:8080` startet und einen Endpunkt bereitstellt.
- Der Endpunkt nimmt die Formulardaten der Workshop-Anmeldung aus Übungsblatt 04 entgegen und gibt sie als Response zurück.
- Nutzen Sie dazu gerne die zuvor von Ihnen in Übungsblatt 06 definierte SWAGGER-Schnittstelle (passen Sie sie ggf. an) oder diese [API-Definition](workshop-api.json).
- Es sollen nur `POST`-Request erlaubt sein. Falls eine andere Methode verwendet wurde, soll der Status-Code 405 (Method not allowed) zurückgegeben werden.
- Testen Sie Ihren Endpunkt mit Ihrem HTML-Formular oder mit diesem [Formular](workshop-anmeldung.html).
## 2. Go-Übung: JSON-Daten empfangen
**Aufgabenstellung**: Erweitern Sie den Endpunkt aus Aufgabe 1 so, dass auch JSON-Daten empfangen und ausgegeben werden können.
- Nutzen Sie dazu gerne die zuvor von Ihnen in Übungsblatt 06 definierte SWAGGER-Schnittstelle (passen Sie sie ggf. an) oder diese [API-Definition](workshop-api.json).
- Falls die JSON-Daten fehlerhaft sind, soll der Status-Code 400 (Bad Request) zurückgegeben werden.
- Testen Sie Ihren Endpunkt mit `curl`-Befehlen oder wahlweise mit Postman oder SWAGGER.

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Workshop-Anmeldung</title>
</head>
<body>
<form action="http://localhost:8080/registrierung" method="post">
<fieldset>
<legend>Persönliche Angaben:</legend>
<label for="vorname">Vorname:</label><br>
<input type="text" name="vorname" id="vorname" placeholder="Vorname" required><br>
<label for="nachname">Nachname:</label><br>
<input type="text" name="nachname" id="nachname" placeholder="Nachname" required><br>
<label for="email">E-Mail:</label><br>
<input type="email" name="email" id="email" autocomplete="email"><br>
<label for="telefon">Handynummer:</label><br>
<input type="tel" name="telefon" id="telefon" pattern="^01[5-7][0-9]{7,10}$"><br><br>
</fieldset>
<fieldset>
<legend>Kursauswahl:</legend>
<label for="kurs">Kurs:</label><br>
<input type="text" name="kurs" id="kurs" value="Webentwicklung Basics" disabled><br><br>
<label for="sessions">Bevorzugte Session:</label><br>
<select id="sessions" name="sessions" multiple size="4">
<option value="vormittag">Vormittag</option>
<option value="nachmittag">Nachmittag</option>
<option value="abendsession">Abend</option>
<option value="wochenende">Wochenende</option>
</select><br>
</fieldset>
<fieldset>
<legend>Zusätzliche Optionen:</legend>
<input type="checkbox" id="agb" name="agb" value="ja" required>
<label for="agb">Ich akzeptiere die Teilnahmebedingungen.</label><br>
<input type="checkbox" id="newsletter" name="newsletter" value="ja">
<label for="newsletter">Newsletter abonnieren.</label><br>
<input type="checkbox" id="equipment" name="equipment" value="ja">
<label for="equipment">Ich benötige spezielles Equipment.</label><br>
</fieldset>
<fieldset>
<legend>Teilnahmeformat:</legend>
Format wählen:<br>
<input type="radio" id="online" name="format" value="online" required>
<label for="online">Online</label><br>
<input type="radio" id="praesenz" name="format" value="praesenz">
<label for="praesenz">Präsenz</label><br>
</fieldset>
<br>
<input type="submit" value="Jetzt anmelden">
</form>
</body>
</html>

View File

@ -0,0 +1,119 @@
{
"openapi": "3.0.0",
"info": {
"title": "Workshop API",
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:8080"
}
],
"paths": {
"/registrierung": {
"post": {
"summary": "Sends all information needed for registering at a workshop",
"requestBody": {
"required": false,
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/RegistrierungForm"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegistrierungForm"
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/html": {
"schema": {
"type": "string"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"text/plain": {
"schema": {
"type": "string",
"example": "Kein Formular gesendet"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"RegistrierungForm": {
"type": "object",
"required": [
"vorname",
"nachname",
"agb",
"format"
],
"properties": {
"vorname": {
"type": "string",
"example": "Zoya"
},
"nachname": {
"type": "string",
"example": "Akhtar"
},
"email": {
"type": "string",
"example": "z.akhtar@test.de"
},
"telefon": {
"type": "string",
"example": "Zoya"
},
"sessions": {
"type": "array",
"items": {
"type": "string",
"example": "vormittag"
},
"example": [
"vormittag",
"nachmittag"
]
},
"agb": {
"type": "string",
"enum": ["ja"],
"example": "ja"
},
"newsletter": {
"type": "string",
"enum": ["ja"],
"example": "ja"
},
"equipment": {
"type": "string",
"enum": ["ja"],
"example": "ja"
},
"format": {
"type": "string",
"enum": ["online","praesenz"],
"example": "online"
}
}
}
}
}
}