Web-basierte Systeme - Exam.pdf

Web-basierte Systeme - Exam
Web-basierte Systeme - Exam Aufgabe 1) Im Rahmen von Web-basierten Systemen spielen HTTP und HTTPS eine zentrale Rolle. HTTP, das auf Port 80 läuft, ist ein zustandsloses Protokoll, das verschiedene Arten von Anfragen wie GET, POST, PUT, und DELETE unterstützt. HTTPS, das auf Port 443 läuft und SSL/TLS für die Verschlüsselung nutzt, schützt Daten während der Übertragung vor Man-in-the-Middle-Angri...

© StudySmarter 2024, all rights reserved.

Web-basierte Systeme - Exam

Aufgabe 1)

Im Rahmen von Web-basierten Systemen spielen HTTP und HTTPS eine zentrale Rolle. HTTP, das auf Port 80 läuft, ist ein zustandsloses Protokoll, das verschiedene Arten von Anfragen wie GET, POST, PUT, und DELETE unterstützt. HTTPS, das auf Port 443 läuft und SSL/TLS für die Verschlüsselung nutzt, schützt Daten während der Übertragung vor Man-in-the-Middle-Angriffen. SSL (Secure Sockets Layer) und TLS (Transport Layer Security) sind die zugrunde liegenden Protokolle, die für die sichere Datenübertragung verantwortlich sind.

a)

  • Beschreibe, wie ein typischer HTTPS-Verbindungsaufbau abläuft, von der DNS-Abfrage bis zur verschlüsselten Datenübertragung. Gehe dabei besonders auf die Rolle von SSL/TLS und die verschiedenen Phasen (z.B. Handshake, Verschlüsselung) ein. Skizziere den Ablauf detailliert.

Lösung:

Um den Ablauf einer typischen HTTPS-Verbindung von der DNS-Abfrage bis zur verschlüsselten Datenübertragung zu beschreiben, können wir den Prozess in die folgenden Schritte unterteilen:

  • DNS-Abfrage: Der Browser des Nutzers sendet eine Anfrage an einen DNS-Server, um die IP-Adresse der Ziel-Website zu erhalten. Dies ist der erste Schritt, um eine Verbindung zum Server herzustellen. Der DNS-Server antwortet mit der entsprechenden IP-Adresse.
  • TCP-Verbindung: Sobald die IP-Adresse bekannt ist, beginnt der Browser eine TCP-Verbindung zum Webserver auf Port 443 (Standardport für HTTPS). Diese Verbindung dient als Grundlage für die spätere HTTPS-Kommunikation.
  • SSL/TLS Handshake: Bei der Initiierung der HTTPS-Verbindung durchläuft der Browser und der Server mehrere Schritte im sogenannten SSL/TLS Handshake:
    • Der Client (Browser) sendet eine „ClientHello“-Nachricht, die die unterstützten Verschlüsselungsmethoden und TLS-Versionen enthält.
    • Der Server antwortet mit einer „ServerHello“-Nachricht, die die gewählte Verschlüsselungsmethode und die TLS-Version enthält.
    • Der Server sendet zudem sein digitales Zertifikat, das die Authentizität des Servers bestätigt. Das Zertifikat wird von einer vertrauenswürdigen Zertifizierungsstelle (CA) ausgestellt.
    • Optionale Schritte: Der Server kann eine „ServerKeyExchange“-Nachricht senden, falls zusätzliche geheime Schlüsselinformationen benötigt werden. Der Server kann auch eine „ServerHelloDone“-Nachricht senden, um den Server-Teil des Handshakes abzuschließen.
    • Der Client überprüft das Zertifikat des Servers. Wenn es vertrauenswürdig ist, erstellt der Client einen pre-master secret (vorläufigen geheimen Schlüssel) und verschlüsselt ihn mit dem öffentlichen Schlüssel des Servers. Dieser verschlüsselte Schlüssel wird dann an den Server gesendet.
    • Sowohl der Client als auch der Server verwenden den pre-master secret in Kombination mit anderen Informationen, um den session key (Session-Schlüssel) zu generieren, der für die Ver- und Entschlüsselung der übertragenen Daten verwendet wird.
    • Abschließend senden sowohl der Client als auch der Server eine „Finished“-Nachricht, die durch den session key verschlüsselt ist, um zu bestätigen, dass der Handshake abgeschlossen ist und die Verbindung sicher ist.
  • Verschlüsselte Datenübertragung: Nachdem der Handshake abgeschlossen ist, beginnt die eigentliche Datenübertragung. Alle Daten, die zwischen dem Client und dem Server gesendet werden, sind nun durch den session key verschlüsselt. Dies schützt die Daten vor Man-in-the-Middle-Angriffen und stellt sicher, dass die Kommunikation vertraulich bleibt.

Zusammenfassung:

  • Die Verbindung startet mit einer DNS-Abfrage, gefolgt von einer TCP-Verbindung.
  • Ein SSL/TLS Handshake erfolgt, um die Authentifizierung und die Generierung eines gemeinsamen session keys durchzuführen.
  • Nach Abschluss des Handshakes werden alle Daten zwischen dem Client und dem Server verschlüsselt übertragen.

b)

  • Angenommen, Du hast die Wahl zwischen der Implementierung einer Datenübertragung über HTTP und HTTPS. Welche konkreten Sicherheitsrisiken bestehen bei der Nutzung von einfachem HTTP im Vergleich zu HTTPS? Nenne und erläutere mindestens drei spezifische Risiken und wie HTTPS diese Risiken durch seine technischen Mechanismen mitigiert.

Lösung:

Beim Vergleich von HTTP und HTTPS zur Datenübertragung gibt es mehrere konkrete Sicherheitsrisiken, die bei der Nutzung von einfachem HTTP bestehen und durch HTTPS gemindert werden können. Hier sind mindestens drei spezifische Risiken:

  • Man-in-the-Middle-Angriffe (MitM): Bei HTTP können Angreifer den Datenverkehr zwischen dem Client und dem Server abfangen und manipulieren, ohne dass der Benutzer oder der Server dies bemerkt. Dies kann geschehen, indem der Angreifer den Netzverkehr umleitet oder einfach auf dem gleichen Netzwerk wie der Benutzer agiert. HTTPS verhindert MitM-Angriffe durch die Verschlüsselung des Datenverkehrs mit TLS/SSL, wodurch der Inhalt der Kommunikation geschützt wird und der Angreifer den Datenverkehr nicht ohne weiteres abfangen oder manipulieren kann.
  • Abhören (Eavesdropping): Bei der Nutzung von HTTP werden alle Daten im Klartext übertragen. Das bedeutet, dass jede Information, die zwischen Client und Server ausgetauscht wird, von jedem, der den Netzverkehr beobachtet, leicht gelesen werden kann. Dies schließt sensible Informationen wie Passwörter, Kreditkarteninformationen und persönliche Daten ein. HTTPS verwendet SSL/TLS, um die Daten während der Übertragung zu verschlüsseln, sodass ein Lauscher nur verschlüsselten, unleserlichen Datenverkehr sehen kann.
  • Datenintegrität: Bei einer HTTP-Verbindung ist es möglich, dass der übertragene Dateninhalt während der Übertragung geändert wird, ohne dass der Empfänger dies bemerkt. Ein Angreifer könnte den Inhalt von Webseiten verändern oder schädlichen Code einschleusen. HTTPS stellt sicher, dass die Datenintegrität erhalten bleibt, indem es Mechanismen wie digitale Signaturen und kryptografische Hashes verwendet, die sicherstellen, dass die Daten weder verändert noch verfälscht wurden.

Zusammengefasst mitigiert HTTPS diese Risiken durch folgende technische Mechanismen:

  • Verschlüsselung des gesamten Datenverkehrs durch SSL/TLS, um Abhören und MitM-Angriffe zu verhindern.
  • Authentifizierung des Servers mittels digitaler Zertifikate, um sicherzustellen, dass die Verbindung zu einem vertrauenswürdigen Server hergestellt wird.
  • Gewährleistung der Datenintegrität durch die Verwendung von kryptografischen Hashes und digitalen Signaturen, um sicherzustellen, dass die Daten während der Übertragung nicht verändert wurden.

Aufgabe 2)

Kontext: Du entwickelst eine einfache Webanwendung, bei der der Benutzer mit verschiedenen Elementen auf der Webseite interagieren kann. Diese Interaktionen lösen bestimmte Ereignisse aus, die durch JavaScript Event-Listener und Callbacks behandelt werden. Die Anwendung enthält einen Knopf (Button), ein Eingabefeld (Textbox) und ein Bild (Image). Du musst sicherstellen, dass die Interaktionen korrekt verarbeitet werden und entsprechende Funktionen als Reaktion auf die Ereignisse ausgelöst werden.

a)

Schreibe den JavaScript-Code, um einen Event-Listener für den Knopf hinzuzufügen, der eine Nachricht in der Konsole ausgibt, wenn der Knopf geklickt wird. Verwende dazu die Methode addEventListener.

Lösung:

JavaScript-Code, um einen Event-Listener für den Knopf hinzuzufügen:

  • Verwende die Methode addEventListener, um eine Funktion zu definieren, die ausgelöst wird, wenn der Knopf geklickt wird.
  // Den Knopf-Element selektieren const button = document.querySelector('button'); // Event-Listener hinzufügen, der eine Nachricht in der Konsole ausgibt button.addEventListener('click', function() { console.log('Der Knopf wurde geklickt!'); });  

b)

Erweitere den Code aus der vorherigen Teilaufgabe, um einen Event-Listener für das Eingabefeld hinzuzufügen. Sobald der Benutzer eine Taste drückt, soll die aktuelle Eingabe in der Konsole ausgegeben werden.

Lösung:

Erweitere den JavaScript-Code, um einen Event-Listener für das Eingabefeld hinzuzufügen:

  • Verwende die Methode addEventListener, um eine Funktion zu definieren, die ausgelöst wird, wenn der Benutzer eine Taste im Eingabefeld drückt.
  // Den Knopf-Element selektieren const button = document.querySelector('button'); // Event-Listener hinzufügen, der eine Nachricht in der Konsole ausgibt button.addEventListener('click', function() { console.log('Der Knopf wurde geklickt!'); }); // Das Eingabefeld-Element selektieren const textbox = document.querySelector('input'); // Event-Listener hinzufügen, der die aktuelle Eingabe in der Konsole ausgibt textbox.addEventListener('keydown', function(event) { console.log('Aktuelle Eingabe: ' + event.target.value); });  

c)

Füge einen Event-Listener für das Bild hinzu, sodass, wenn die Maus über das Bild fährt, der Hintergrund der Webseite seine Farbe ändert. Verwende dazu das mouseover Ereignis.

Lösung:

Erweitere den JavaScript-Code, um einen Event-Listener für das Bild hinzuzufügen:

  • Verwende die Methode addEventListener, um eine Funktion zu definieren, die ausgelöst wird, wenn die Maus über das Bild fährt.
  // Den Knopf-Element selektieren const button = document.querySelector('button'); // Event-Listener hinzufügen, der eine Nachricht in der Konsole ausgibt button.addEventListener('click', function() { console.log('Der Knopf wurde geklickt!'); }); // Das Eingabefeld-Element selektieren const textbox = document.querySelector('input'); // Event-Listener hinzufügen, der die aktuelle Eingabe in der Konsole ausgibt textbox.addEventListener('keydown', function(event) { console.log('Aktuelle Eingabe: ' + event.target.value); }); // Das Bild-Element selektieren const image = document.querySelector('img'); // Event-Listener hinzufügen, der den Hintergrund der Webseite ändert image.addEventListener('mouseover', function() { document.body.style.backgroundColor = 'lightblue'; });  

d)

Erkläre, wie Callbacks in der ereignisgesteuerten Programmierung verwendet werden. Verwende dabei Beispiele aus den obigen Teilaufgaben und beschreibe, warum sie wichtig sind, um die Interaktionen korrekt zu handhaben.

Lösung:

Erklärung von Callbacks in der Ereignis-gesteuerten Programmierung:

  • Was sind Callbacks? Callbask sind Funktionen, die als Argumente an andere Funktionen übergeben werden und zu einem späteren Zeitpunkt, meist in Reaktion auf ein Ereignis, ausgeführt werden.
  • Verwendung von Callbacks in den obigen Beispielen: In den oben genannten Teilaufgaben haben wir mehrere Callbacks verwendet, um auf Benutzerinteraktionen zu reagieren:
    • button.addEventListener('click', function() { console.log('Der Knopf wurde geklickt!'); }); Hier haben wir eine anonyme Funktion als Callback für das 'click'-Ereignis des Buttons definiert. Diese Callback-Funktion wird aufgerufen, wenn der Benutzer auf den Knopf klickt, und sie gibt eine Nachricht in der Konsole aus.
    • textbox.addEventListener('keydown', function(event) { console.log('Aktuelle Eingabe: ' + event.target.value); }); Diese Callback-Funktion wird ausgelöst, wenn der Benutzer eine Taste im Eingabefeld drückt. Sie gibt die aktuelle Eingabe in der Konsole aus.
    • image.addEventListener('mouseover', function() { document.body.style.backgroundColor = 'lightblue'; }); Hier haben wir eine Callback-Funktion für das 'mouseover'-Ereignis des Bildes. Sobald die Maus über das Bild fährt, ändert diese Funktion die Hintergrundfarbe der Webseite.
  • Warum sind Callbacks wichtig? Callbacks sind in der ereignisgesteuerten Programmierung entscheidend, weil sie es ermöglichen, dass Funktionen nur dann ausgeführt werden, wenn bestimmte Events eintreten. Das ist besonders wichtig in Webanwendungen, wo verschiedene Benutzerinteraktionen wie Klicks, Tastendrücke oder Mausbewegungen auftreten können. Mithilfe von Callbacks können Entwickler sicherstellen, dass die Anwendung dynamisch und auf die Benutzeraktionen reagiert. Ohne Callbacks müsste man ständig den Zustand der Anwendung überprüfen, was ressourcenintensiv und ineffizient wäre.

Aufgabe 3)

Du sollst eine einfache beispielhafte Webanwendung in Angular und eine in React entwickeln. Beide Anwendungen sollen eine Liste von Nutzern anzeigen und es ermöglichen, neue Nutzer hinzuzufügen. Während Du diese Anwendungen erstellst, achte auf die Wiederverwendbarkeit der Komponenten, das Zustandsmanagement, sowie die Kommunikation zwischen den Komponenten.

a)

Teilaufgabe a: Erstelle die Hauptkomponente UserList in Angular. Diese Komponente soll eine Liste von Nutzern anzeigen, die in einem Service gespeichert sind. Implementiere den Service UserService, der eine Methode getUsers() zum Abrufen der Nutzer und eine Methode addUser(user) zum Hinzufügen eines neuen Nutzers bereitstellt. Verwende RxJS zur Asynchronität. Stelle sicher, dass die neuen Nutzer zur Liste hinzugefügt und angezeigt werden.

Lösung:

Teilaufgabe a: Erstellen der UserList Komponente in Angular

  • Erstelle die Hauptkomponente UserList und implementiere sie in Angular.
  • Erstelle den UserService, um die Nutzerdaten zu verwalten.
  • Implementiere die Methoden getUsers() und addUser(user) im UserService unter Verwendung von RxJS.
  • Teile den Zustand zum Abrufen und Hinzufügen von Nutzern zwischen der Komponente und dem Service.
Implementationscode:

1. UserService:

 @Injectable({ providedIn: 'root' }) export class UserService { private users: BehaviorSubject<User[]> = new BehaviorSubject<User[]>([]); constructor() {} getUsers(): Observable<User[]> { return this.users.asObservable(); } addUser(user: User): void { const currentUsers = this.users.getValue(); this.users.next([...currentUsers, user]); } }  

2. UserList Komponente:

 @Component({ selector: 'app-user-list', templateUrl: './user-list.component.html', styleUrls: ['./user-list.component.css'] }) export class UserListComponent implements OnInit { users: User[] = []; newUser: User = { id: 0, name: '' }; constructor(private userService: UserService) {} ngOnInit(): void { this.userService.getUsers().subscribe((users) => { this.users = users; }); } addUser(): void { if (this.newUser.name) { this.userService.addUser(this.newUser); this.newUser = { id: 0, name: '' }; } } }  

3. HTML Template für UserList Komponente:

 <div> <h2>Liste der Nutzer</h2> <ul> <li *ngFor="let user of users"> {{ user.name }} </li> </ul> <div> <input [(ngModel)]="newUser.name" placeholder="Nutzername" /> <button (click)="addUser()">Nutzer hinzufügen</button> </div> </div>  
  • Der UserService verwaltet die Liste der Nutzer und stellt Methoden zum Abrufen und Hinzufügen von Nutzern bereit.
  • Die UserList Komponente holt die Nutzerliste vom UserService und rendert sie im Template.
  • Die UserList Komponente enthält ein Formular zum Hinzufügen neuer Nutzer und greift dafür auf den UserService zu.

b)

Teilaufgabe b: Entwickle die gleiche Benutzerliste- und Hinzufüge-Funktionalität in React. Erstelle eine Hauptkomponente UserList, die die Liste der Nutzer anzeigt. Schreibe eine separate UserService Klasse, die die Nutzer im Zustand der Anwendung verwaltet und Methoden getUsers() und addUser(user) bereitstellt. Verwende useState und useEffect für das Zustandsmanagement und asynchrone Datenabfragen.

Lösung:

Teilaufgabe b: Entwickle die Benutzerliste- und Hinzufüge-Funktionalität in React

  • Erstelle eine Hauptkomponente UserList und implementiere sie in React.
  • Schreibe eine separate UserService Klasse, um die Nutzerdaten zu verwalten.
  • Implementiere die Methoden getUsers() und addUser(user) in der UserService Klasse.
  • Verwende useState und useEffect für das Zustandsmanagement und asynchrone Datenabfragen.
Implementationscode:

1. UserService:

 class UserService { constructor() { this.users = []; this.subscribers = []; } getUsers() { return new Promise((resolve) => { resolve(this.users); }); } addUser(user) { this.users.push(user); this.notifySubscribers(); } subscribe(callback) { this.subscribers.push(callback); } notifySubscribers() { this.subscribers.forEach((callback) => callback(this.users)); } } export const userService = new UserService();  

2. UserList Komponente:

 import React, { useState, useEffect } from 'react'; import { userService } from './UserService'; function UserList() { const [users, setUsers] = useState([]); const [newUser, setNewUser] = useState({ id: 0, name: '' }); useEffect(() => { userService.getUsers().then((users) => setUsers(users)); userService.subscribe((users) => setUsers(users)); }, []); const addUser = () => { if (newUser.name) { userService.addUser(newUser); setNewUser({ id: 0, name: '' }); } }; return ( <div> <h2>Liste der Nutzer</h2> <ul> {users.map((user, index) => (<li key={index}>{user.name}</li>))} </ul> <div> <input value={newUser.name} onChange={(e) => setNewUser({ ...newUser, name: e.target.value })} placeholder="Nutzername" /> <button onClick={addUser}>Nutzer hinzufügen</button> </div> </div> ); } export default UserList;  
  • Die UserService Klasse verwaltet die Liste der Nutzer und stellt Methoden zum Abrufen und Hinzufügen von Nutzern bereit.
  • Die UserList Komponente holt die Nutzerliste vom UserService und rendert sie im JSX.
  • Die UserList Komponente enthält ein Formular zum Hinzufügen neuer Nutzer und greift dafür auf den UserService zu.
  • useEffect wird verwendet, um den Zustand beim ersten Laden der Komponente zu setzen und sich für Änderungen an der Nutzerliste zu registrieren.
  • useState wird verwendet, um den Zustand der Nutzer und des neuen Nutzers zu verwalten.

c)

Teilaufgabe c: Erkläre die Unterschiede und Gemeinsamkeiten in der Handhabung des Zustandsmanagements zwischen Angular (mit NgRx) und React (mit Redux). Gehe dabei auch auf die zugrunde liegenden Prinzipien ein und veranschauliche deine Erklärungen durch Codebeispiele in Pseudocode.

Lösung:

Teilaufgabe c: Unterschiede und Gemeinsamkeiten im Zustandsmanagement zwischen Angular (mit NgRx) und React (mit Redux)

  • Angular verwendet NgRx für das Zustandsmanagement, während React oft Redux verwendet.

Gemeinsamkeiten:

  • Beide basieren auf dem Redux-Prinzip, wobei der Anwendungsszustand in einem einzigen Store gehalten wird.
  • Es wird eine unidirektionale Datenflussarchitektur verwendet, bei der Aktionen den Zustand des Stores verändern.
  • Implementierung eines Stores, Reducers, Actions und Selectors.
  • State ist immutabel, was bedeutet, dass der Zustand nicht direkt verändert wird, sondern immer eine neue Kopie zurückgegeben wird.

Unterschiede:

  • NgRx ist speziell für Angular entwickelt und integriert sich nahtlos mit Angular-Diensten und RxJS.
  • Redux ist framework-agnostisch und kann mit verschiedenen UI-Bibliotheken verwendet werden, ist jedoch weit verbreitet in React-Projekten.
  • NgRx nutzt stark RxJS-Observables, während Redux hauptsächlich auf plain JavaScript basiert.
  • In NgRx erfolgt die Interaktion mit dem Store oft über Angular-Dienste, während in React mit Redux der Store direkt im JSX-Code verwendet wird.

Pseudocode Beispiele:

1. Angular mit NgRx:

 # State-Management Elemente # Actions export const ADD_USER = '[User] Add'; export class AddUser implements Action { readonly type = ADD_USER; constructor(public payload: User) {} } # Reducer function userReducer(state = initialState, action: UserActions): State { switch (action.type) { case ADD_USER: return { ...state, users: [ ...state.users, action.payload ] }; default: return state; } } # Selector export const selectUsers: MemoizedSelector<State, User[]> = createSelector( selectUserState, (state: State) => state.users ); # Service class UserDataService { constructor(private store: Store<State>) {} addUser(user: User) { this.store.dispatch(new AddUser(user)); } getUsers(): Observable<User[]> { return this.store.select(selectUsers); } }  

2. React mit Redux:

 # State-Management Elemente # Actions const ADD_USER = 'ADD_USER'; function addUser(user) { return { type: ADD_USER, payload: user }; } # Reducer function userReducer(state = initialState, action) { switch (action.type) { case ADD_USER: return { ...state, users: [ ...state.users, action.payload ] }; default: return state; } } # Selector const selectUsers = (state) => state.users; # Service (Optional in Redux, oft direkt in JSX verwendet) class UserDataService { constructor(store) { this.store = store; } addUser(user) { this.store.dispatch(addUser(user)); } getUsers() { return this.store.getState().users; } } # Verwendung in React-Komponente function UserList() { const users = useSelector(selectUsers); const dispatch = useDispatch(); const [newUser, setNewUser] = useState({ id: 0, name: '' }); const addUserToStore = () => { if (newUser.name) { dispatch(addUser(newUser)); setNewUser({ id: 0, name: '' }); } }; return ( <div> <h2>Liste der Nutzer</h2> <ul> {users.map((user, index) => (<li key={index}>{user.name}</li>))} </ul> <div> <input value={newUser.name} onChange={(e) => setNewUser({ ...newUser, name: e.target.value })} placeholder="Nutzername" /> <button onClick={addUserToStore}>Nutzer hinzufügen</button> </div> </div> ); }  

Zusammenfassung:

  • Beide Ansätze (NgRx für Angular und Redux für React) teilen die Kernprinzipien von Redux, wie den unidirektionalen Datenfluss und einen zentralisierten Application State.
  • NgRx nutzt die Leistungsfähigkeit von RxJS-Observables, was es zu einer leistungsfähigen Lösung für Angular macht.
  • Redux ist flexibler, da es mit verschiedenen JS-Frameworks und -Bibliotheken verwendet werden kann, wird jedoch am häufigsten mit React-Apps kombiniert.
  • Zusammenfassend haben beide ähnliche Konzepte aber unterschiedliche Implementierungsdetails, die dem jeweiligen Framework angepasst sind.

d)

Teilaufgabe d: Diskutiere die Vor- und Nachteile der Verwendung von Lifecycle-Methoden in Angular und React. Verwende spezifische Beispiele, wie ngOnInit und ngOnDestroy in Angular sowie componentDidMount und componentWillUnmount in React. Wie tragen diese Methoden zur Kontrolle des Renderings und der Datenflüsse bei? In welchen Fällen sind diese Methoden besonders nützlich?

Lösung:

Teilaufgabe d: Diskussion der Vor- und Nachteile der Verwendung von Lifecycle-Methoden in Angular und React

  • Angular und React haben beide integrierte Lifecycle-Methoden, die eine präzise Kontrolle über das Verhalten von Komponenten während ihres Lebenszyklus ermöglichen.

Angular Lifecycle-Methoden:

  • ngOnInit: Wird aufgerufen, nachdem die Komponente initialisiert wurde. Ideal für Initialisierungslogik wie Datenabfragen.
  • ngOnDestroy: Wird kurz vor dem Zerstören der Komponente aufgerufen. Geeignet, um Ressourcen freizugeben, z.B. Subscriptions zu beenden.

React Lifecycle-Methoden:

  • componentDidMount: Wird aufgerufen, nachdem die Komponente in den DOM eingefügt wurde. Ideal für Initialisierungslogik wie Datenabfragen.
  • componentWillUnmount: Wird aufgerufen, kurz bevor die Komponente aus dem DOM entfernt wird. Geeignet, um Ressourcen freizugeben, z.B. Subscriptions zu beenden.

Beispiele und Nutzen:

1. Angular Beispiel:

 @Component({ selector: 'app-example', template: ` <div> Beispiel-Komponente </div> ` }) export class ExampleComponent implements OnInit, OnDestroy { private subscription: Subscription; constructor(private dataService: DataService) {} ngOnInit() { this.subscription = this.dataService.getData().subscribe(data => { console.log(data); }); } ngOnDestroy() { if (this.subscription) { this.subscription.unsubscribe(); } } }  

2. React Beispiel:

 class ExampleComponent extends React.Component { componentDidMount() { this.subscription = dataService.getData().subscribe(data => { console.log(data); }); } componentWillUnmount() { if (this.subscription) { this.subscription.unsubscribe(); } } render() { return <div> Beispiel-Komponente </div> } }  

Vor- und Nachteile

Angular (ngOnInit und ngOnDestroy):

  • Vorteile:
    • Elegante Trennung von Initialisierungs- und Freigabelogik.
    • Integration mit Angulars Dependency Injection und Services.
    • Leicht zu testen und konsistent in der Struktur.
  • Nachteile:
    • Kann zu verbogenem Code führen, wenn zu viele Initialisierungsaufgaben in ngOnInit durchgeführt werden.
    • Komplexere Lebenszyklen können zu schlechter Lesbarkeit führen.

React (componentDidMount und componentWillUnmount):

  • Vorteile:
    • Klar definierte Methoden für Initialisierung und Bereinigung.
    • Flexibilität durch Kombination mit React Hooks (z.B. useEffect).
    • Einfachere Handhabung von Side-Effects durch Custom Hooks.
  • Nachteile:
    • Kann zu komplizierten Component Lifecycles führen, wenn nicht gut strukturiert.
    • Erfordert zusätzliche Hooks oder integrierte Lifecycle-Methoden, um komplexe Side-Effects zu verwalten.

Anwendungsbeispiele:

  • Initialisierung: Ideal für die Platzierung von Datenabfragen, Event-Listener-Setups oder API-Aufrufen nach dem ersten Rendern.
  • Bereinigung: Perfekt für das Abbestellen von Observables, das Entfernen von Event-Listenern oder das Aufräumen von Ressourcen.

Zusammenfassung:

  • Lifecycle-Methoden in Angular (ngOnInit, ngOnDestroy) und React (componentDidMount, componentWillUnmount) bieten eine robuste Möglichkeit zur Kontrolle des Komponentenverhaltens.
  • Beide Frameworks/in Both frameworks können Dateninitialisierungen und -bereinigungen effizient implementiert werden, allerdings mit unterschiedlichen Ansätzen und Vor- und Nachteilen.
  • Die Wahl der Methodik hängt oft von den spezifischen Anwendungsfällen und der bevorzugten Teamstruktur ab.

Aufgabe 4)

Du sollst einen RESTful Webservice implementieren, der eine einfache Benutzerverwaltung (CRUD-Operationen) ermöglicht. Der Webservice soll in Spring Boot entwickelt werden und JSON als Datenformat verwenden. Folgende Aspekte sind zu berücksichtigen:

  • Verwendet werden die HTTP-Methoden: GET, POST, PUT, DELETE
  • Die Ressourcen (hier: Benutzer) sollen durch eine URI identifiziert werden
  • Die Kommunikation soll zustandslos erfolgen
  • Die Fehlerbehandlung soll durch HTTP-Statuscodes erfolgen

a)

Schreibe den Code für den Controller in Spring Boot, der die CRUD-Operationen für die Benutzerverwaltung (Benutzer erstellen, lesen, aktualisieren, löschen) implementiert. Stelle sicher, dass du die richtigen HTTP-Methoden verwendest, die Benutzer-Ressourcen durch eine URI identifizierst und JSON als Datenformat nutzt.

Lösung:

Um einen RESTful Webservice in Spring Boot zu implementieren, der die CRUD-Operationen für die Benutzerverwaltung ermöglicht, wirst Du einen Controller erstellen wollen. Dieser Controller wird die entsprechenden Endpunkte und HTTP-Methoden enthalten. Unten ist der Code für einen solchen Controller aufgeführt.

Stelle sicher, dass Du die notwendigen Abhängigkeiten in Deiner pom.xml oder build.gradle Datei hast, um Spring Boot und Spring Web nutzen zu können:

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 

Hier ist der Code für den Controller:

 @RestController @RequestMapping("/api/benutzer") public class BenutzerController { private final BenutzerService benutzerService; @Autowired public BenutzerController(BenutzerService benutzerService) { this.benutzerService = benutzerService; } @PostMapping public ResponseEntity<Benutzer> createBenutzer(@RequestBody Benutzer benutzer) { Benutzer createdBenutzer = benutzerService.createBenutzer(benutzer); return new ResponseEntity<>(createdBenutzer, HttpStatus.CREATED); } @GetMapping public ResponseEntity<List<Benutzer>> getAllBenutzer() { List<Benutzer> benutzerList = benutzerService.getAllBenutzer(); return new ResponseEntity<>(benutzerList, HttpStatus.OK); } @GetMapping("/{id}") public ResponseEntity<Benutzer> getBenutzerById(@PathVariable Long id) { Optional<Benutzer> benutzer = benutzerService.getBenutzerById(id); return benutzer.map(value -> new ResponseEntity<>(value, HttpStatus.OK)) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @PutMapping("/{id}") public ResponseEntity<Benutzer> updateBenutzer(@PathVariable Long id, @RequestBody Benutzer benutzerDetails) { Optional<Benutzer> updatedBenutzer = benutzerService.updateBenutzer(id, benutzerDetails); return updatedBenutzer.map(value -> new ResponseEntity<>(value, HttpStatus.OK)) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteBenutzer(@PathVariable Long id) { boolean deleted = benutzerService.deleteBenutzer(id); if (deleted) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } } 
  • POST URI: /api/benutzer Beschreibung: Erstelle einen neuen Benutzer. Beispielkörpertyp: JSON HTTP-Statuscode bei Erfolg: 201 Created
  • GET (alle Benutzer) URI: /api/benutzer Beschreibung: Liste alle Benutzer auf. HTTP-Statuscode bei Erfolg: 200 OK
  • GET (einzelne Benutzer) URI: /api/benutzer/{id} Beschreibung: Hole einen bestimmten Benutzer per ID. HTTP-Statuscode bei Erfolg: 200 OK HTTP-Statuscode bei Nichtauffinden: 404 Not Found
  • PUT URI: /api/benutzer/{id} Beschreibung: Aktualisiere einen bestimmten Benutzer per ID. Beispielkörpertyp: JSON HTTP-Statuscode bei Erfolg: 200 OK HTTP-Statuscode bei Nichtauffinden: 404 Not Found
  • DELETE URI: /api/benutzer/{id} Beschreibung: Lösche einen bestimmten Benutzer per ID. HTTP-Statuscode bei Erfolg: 204 No Content HTTP-Statuscode bei Nichtauffinden: 404 Not Found

b)

Beschreibe die Fehlerbehandlung in Deinem RESTful Webservice. Wie gehst Du mit verschiedenen Fehlerfällen (z.B. Benutzer nicht gefunden, ungültige Eingabedaten) um, und welche HTTP-Statuscodes verwendest Du in diesen Fällen?

Lösung:

Die Fehlerbehandlung ist ein wichtiger Aspekt bei der Implementierung eines RESTful Webservices. In Spring Boot kann die Fehlerbehandlung auf verschiedene Weisen erfolgen. Hier sind einige Schritte und Beispiele zur Handhabung verschiedener Fehlerfälle und den entsprechenden HTTP-Statuscodes:

  • Benutzer nicht gefunden: Falls ein Benutzer nicht gefunden wird, verwenden wir den Statuscode 404 Not Found. Dieser Fall tritt auf, wenn eine Anfrage an eine bestimmte Benutzer-ID gesendet wird, die nicht existiert.
  • Ungültige Eingabedaten: Bei ungültigen Eingabedaten verwenden wir den Statuscode 400 Bad Request. Diese Situation tritt auf, wenn die Nutzerdaten, die an den Webservice gesendet werden, nicht den erwarteten Anforderungen entsprechen (z.B. fehlende Felder, falsches Datenformat usw.).
  • Interner Serverfehler: Für unerwartete Fehler, die nicht spezifisch behandelt werden können, verwenden wir den Statuscode 500 Internal Server Error. Diese Fehler können durch verschiedene Ursachen ausgelöst werden, einschließlich Datenbankfehler, NullPointerExceptions usw.

Im Folgenden ein Beispiel, wie Du diese Fehler in Deinem REST-Controller handhaben kannst:

 @RestController @RequestMapping("/api/benutzer") public class BenutzerController { // Service und Konstruktor wie zuvor @Autowired private final BenutzerService benutzerService; @Autowired public BenutzerController(BenutzerService benutzerService) { this.benutzerService = benutzerService; } // CRUD-Operationen wie zuvor // GET- und POST-Handler-Methoden omitted for brevity @GetMapping("/{id}") public ResponseEntity<Benutzer> getBenutzerById(@PathVariable Long id) { Optional<Benutzer> benutzer = benutzerService.getBenutzerById(id); return benutzer.map(value -> new ResponseEntity<>(value, HttpStatus.OK)) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @PutMapping("/{id}") public ResponseEntity<Benutzer> updateBenutzer(@PathVariable Long id, @RequestBody Benutzer benutzerDetails) { if (!validateBenutzer(benutzerDetails)) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } Optional<Benutzer> updatedBenutzer = benutzerService.updateBenutzer(id, benutzerDetails); return updatedBenutzer.map(value -> new ResponseEntity<>(value, HttpStatus.OK)) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteBenutzer(@PathVariable Long id) { boolean deleted = benutzerService.deleteBenutzer(id); if (deleted) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } // Helper-Methoden // Eine Beispiel-Validierungsmethode private boolean validateBenutzer(Benutzer benutzer) { // Führe Validierungslogik durch (z.B. alle Felder sind nicht null) // Hier nur als Platzhalter, erweiterbar return benutzer.getName() != null && !benutzer.getName().isEmpty(); } } 
  • ResponseEntity wird verwendet, um die Antwort und den zugehörigen HTTP-Statuscode festzulegen.
  • Im GET-Anfrage-Handler für Benutzer nach ID, wird überprüft, ob der Benutzer vorhanden ist. Wenn nein, wird HttpStatus.NOT_FOUND zurückgegeben.
  • Der PUT-Anfrage-Handler überprüft die Gültigkeit der Eingabedaten. Bei ungültigen Eingabedaten wird HttpStatus.BAD_REQUEST zurückgegeben. Wenn der Benutzer nicht gefunden wird, wird HttpStatus.NOT_FOUND zurückgegeben.
  • Der DELETE-Anfrage-Handler zeigt HttpStatus.NO_CONTENT bei erfolgreichem Löschen und HttpStatus.NOT_FOUND an, wenn der Benutzer nicht gefunden wird.

Mit dieser Struktur kannst Du sicherstellen, dass Dein RESTful Webservice eine angemessene Fehlerbehandlung bietet und nützliche HTTP-Statuscodes zurückgibt.

Sign Up

Melde dich kostenlos an, um Zugriff auf das vollständige Dokument zu erhalten

Mit unserer kostenlosen Lernplattform erhältst du Zugang zu Millionen von Dokumenten, Karteikarten und Unterlagen.

Kostenloses Konto erstellen

Du hast bereits ein Konto? Anmelden