Zum Inhalt springen

Aufgabe 09-2a - Client trifft Server

Zu Zen-Modus wechseln
  • Du wählst begründet zwischen dem Datenzugriff in der Server-Komponente und fetch im Client.
  • Du baust ein Formular mit Prüfung im Browser, Schutz gegen Doppelklick und Fehleranzeige aus dem Fehlerformat des Vertrags.
  • Du machst die drei Zustände einer Mutation (unterwegs, gelungen, gescheitert) in der Oberfläche sichtbar.
  • Du aktualisierst die Anzeige nach einer Mutation, ohne die Seite neu zu laden.

Die Buffet-API aus Aufgabe 09-1a bekommt ein Gesicht: eine Weboberfläche, die über deine eigene Schnittstelle liest und schreibt. Client und Server leben im selben Projekt, und alles aus den React- und Async-Wochen kommt hier zusammen (siehe Kapitel Next.js: API und Datenfluss). Die ersten zwei Punkte der Stufe Wesentlich_überwiegend passen nach Einheit 9.2 (Daten lesen), alles Weitere nach Einheit 9.3 (Daten verändern).

Ausgangspunkt ist dein Projekt aus Aufgabe 09-1a, mindestens im Stand Wesentlich_vollständig. Fehlt dir dieser Stand, bekommst du von deiner Lehrkraft ein Startprojekt.

Projektordner anlegen siehe https://qiz.at/projektordner

  • Lies vorher kurz https://qiz.at/assignmentsabgabe durch.
  • Nur vollständige Abgaben (siehe https://qiz.at/rules) können positiv bewertet werden.
  • Abgabe- und Testdetails siehe https://qiz.at/assignmentsabgabe
  • Die Übung liegt als Unterordner uebung-09-2a in deinem Jahres-Repository SJ26_27_4AHITM_SEW_<Benutzername>_exercises. Kopier dafür den Ordner uebung-09-1a ohne node_modules/ und .next/ und arbeite allein in der Kopie weiter; uebung-09-1a bleibt, wie es abgegeben wurde.
  • Die Rolle wird weiter über den Header x-role nachgestellt (buffet oder student).
  • Schriftliche Antworten stehen in der README.md des Projekts. npm run build muss fehlerfrei durchlaufen, npm run lint ohne Fehler; node_modules/ und .next/ kommen nicht ins Repository. Alle Artikel und Bestellungen sind erfunden.
  • Die Seite /buffet zeigt das Tagesangebot mit Name, Preis in Euro und verfügbarer Menge. Entscheide: Liest die Server-Komponente direkt über dein Modul lib/store.ts, oder holt sie die Daten per fetch über die eigene API? Begründe die Wahl in zwei Sätzen in der README.
  • Ausverkaufte Artikel werden angezeigt, aber sichtbar deaktiviert. Das ergibt sich aus der Menge; es gibt dafür keinen eigenen State.
  • Das Bestellformular ist eine Client-Komponente: Positionen zusammenklicken, höchstens 5 Positionen und je Artikel höchstens die verfügbare Menge. Der Knopf “Bestellen” ist gesperrt, solange die Bestellung ungültig ist.
  • Absenden per fetch an POST /api/orders nach dem Muster aus Einheit 9.3: In-Flight-Flag gegen den Doppelklick, Ladezustand am Knopf, bei einer Ablehnung die Meldung aus dem Fehlerformat (error.message), bei einem Netzwerkfehler eine eigene Meldung.
  • Bei Erfolg erscheint die Abholnummer groß, das Formular wird geleert, und das Tagesangebot zeigt die neuen Mengen, ohne dass die Seite neu geladen wird.
  • Der Client kennt die Grenzen, aber der Server prüft noch einmal. Beantworte in der README: Warum wird doppelt geprüft, und welche der beiden Prüfungen wäre verzichtbar?

Tipp: Legst du über die API einen Artikel an und die Seite zeigt ihn nicht, dann lies in Einheit 9.2 den Abschnitt Zwei Kopien desselben Moduls. Preise kommen als Cent; die Umrechnung in Euro ist Sache der Anzeige. Prüf am Ende mit npm run build, dass /buffet nicht mit ○ erscheint.

Vollständig, wenn:

  • npm run build und npm run lint fehlerfrei durchlaufen.
  • /buffet das Tagesangebot aktuell zeigt, auch nach npm run build und npm start, und die Wahl des Datenwegs begründet ist.
  • ausverkaufte Artikel sichtbar deaktiviert sind und sich nicht bestellen lassen.
  • das Formular alle drei Zustände zeigt: gesperrter Knopf mit Ladetext, Abholnummer bei Erfolg, Meldung aus error.message bei einer Ablehnung.
  • nach einer Bestellung die Mengen ohne Neuladen stimmen und die Frage zur doppelten Prüfung richtig beantwortet ist.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Die Seite /buffet/admin zeigt die Bestellliste mit Abholnummer, Positionen und Status. Jede Bestellung hat die Knöpfe “fertig” und “abgeholt”, die PATCH /api/orders/[id] aufrufen.
  • Nach jeder Aktion aktualisiert sich die Liste. Wähl dafür router.refresh() oder ein Update im State und begründe die Wahl in einem Satz in der README.
  • Fehler sind sichtbar: Antwortet die API mit 409, etwa weil “abgeholt” vor “fertig” geklickt wurde, erscheint die Meldung direkt bei der betroffenen Bestellung. Kein alert, kein stiller Fehlschlag.
  • Der Header x-role wird bei jedem fetch mitgeschickt. Kapsle das in einer kleinen Hilfsfunktion in lib/api-client.ts, damit es in Aufgabe 12a an genau einer Stelle ersetzt werden kann.

Tipp: Jede Bestellung braucht ihren eigenen Fehler- und Ladezustand; am einfachsten ist eine eigene Client-Komponente pro Zeile. Die Hilfsfunktion nimmt Pfad und Optionen wie fetch und ergänzt nur den Header.

Vollständig, wenn:

  • /buffet/admin alle Bestellungen aktuell zeigt und beide Knöpfe den Status über die API ändern.
  • die Liste nach jeder Aktion ohne Neuladen stimmt und die Wahl des Verfahrens begründet ist.
  • eine 409-Antwort als Meldung bei der betroffenen Bestellung erscheint und die anderen Zeilen unberührt bleiben.
  • kein fetch den Header x-role selbst setzt; alle gehen über die Hilfsfunktion.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Störungstest: Stopp den Server, während die Seite /buffet offen ist, und schick eine Bestellung ab. Halt in disruption.md fest, was die Oberfläche zeigt und welcher Zustand zurückbleibt.
  • Härte das Formular: Der Netzwerkfehler erscheint als verständliche Meldung mit einem Knopf “Erneut versuchen”. Nach dem Neustart des Servers schickt dieser Knopf dieselbe Bestellung ab, ohne dass die Seite neu geladen wird. Kein Formularinhalt geht verloren.
  • Wiederhol den Störungstest und halt das neue Verhalten in disruption.md fest.

Tipp: Wirft fetch, gibt es gar keine Antwort; das ist ein anderer Fall als eine Antwort mit 4xx oder 5xx (Einheit 5.2). Nach dem Neustart ist der In-Memory-Speicher wieder im Startzustand; das gehört in dein Protokoll.

Vollständig, wenn:

  • disruption.md das Verhalten vor und nach der Härtung beschreibt.
  • der Netzwerkfehler mit eigener Meldung und dem Knopf “Erneut versuchen” erscheint.
  • der erneute Versuch nach dem Neustart ohne Neuladen gelingt und die gewählten Positionen erhalten bleiben.

Wie unter Allgemein, dazu disruption.md.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Prüf die heikle Frage der Doppelbestellung: Der Server hat die Bestellung angenommen, aber die Antwort hat den Client nie erreicht. Was passiert bei “Erneut versuchen”? Stell den Fall nach und halt das Verhalten deiner API in disruption.md fest.
  • Behebe das Problem: Der Client vergibt für jede Bestellung eine eigene Kennung und schickt sie mit; der Server legt zu einer Kennung höchstens eine Bestellung an und antwortet bei der Wiederholung mit der schon angelegten. Trag die Änderung in api.md samt Changelog ein.
  • Erklär in drei Sätzen, warum ein wiederholtes POST heikel ist, ein wiederholtes PATCH auf denselben Zielstatus aber nicht.
  • Führ den ganzen Bestellablauf samt Störungstest als Folge von Screenshots oder als kurzes Video vor.

Tipp: Den Fall “angenommen, aber Antwort verloren” stellst du nach, indem der Route Handler vor der Antwort einige Sekunden wartet und du den Server in dieser Zeit stoppst; mit einem Speicher im Arbeitsspeicher ist die Bestellung dann allerdings auch weg. Überleg, wie du die Wiederholung trotzdem zeigen kannst. Eine eindeutige Kennung liefert im Browser crypto.randomUUID(). Stichwort: Idempotenz (Einheit 5.1).

Vollständig, wenn:

  • der Fall der Doppelbestellung nachgestellt und in disruption.md beschrieben ist.
  • zwei Anfragen mit derselben Kennung genau eine Bestellung ergeben und api.md die Änderung samt Changelog enthält.
  • die Erklärung zu POST und PATCH den Begriff Idempotenz richtig verwendet.
  • die Screenshots oder das Video den Ablauf samt Störungstest zeigen.

Wie unter Allgemein, dazu die Screenshots im Ordner screenshots/ oder das Video.

  1. Nach welchen Kriterien entscheidest du zwischen dem Datenzugriff in der Server-Komponente und fetch im Client?
  2. Warum ersetzt die Prüfung im Browser die Prüfung am Server nicht?
  3. Welche drei Zustände jeder Mutation muss die Oberfläche sichtbar machen?
  4. Was bewirkt router.refresh() nach einer gelungenen Mutation?
  5. Warum ist ein wiederholtes POST heikel, ein wiederholtes PATCH auf denselben Zielstatus aber nicht?

Durchschnittlich bis Wesentlich_vollständig: ca. 100 min

  • Oliver Maurhart (MAO)
  • Michael Gabriel (GAM)

CC-BY-SA

https://qiz.at/rules