- Lernen
- Informatik & Informationswissenschaften
- Programmierung
- JavaScript – DOM, Ereignisse und asynchroner Code
- Daten abrufen und Browser-APIs
Daten abrufen und Browser-APIs
fetch Daten zu laden und zu senden, die Ergebnisse auf einer Webseite anzuzeigen und kleine Datenmengen im Browser zu speichern. Du liest HTTP-Anfragen, HTTP-Antworten und JSON, behandelst Lade- und Fehlerzustände und formatierst Datumsangaben und Preise aus einer API passend für die jeweiligen Leser. Du erfährst, warum CORS-Fehler auftreten und warum geheime API-Schlüssel nicht in den Frontend-Code gehören. Anschließend nutzt du localStorage, sessionStorage und Browserfunktionen wie die Standortermittlung und die Zwischenablage.AnfängerEinheit 8 von 9 in Kurs: JavaScript – DOM, Ereignisse und asynchroner Code
Was du lernst
- Erkläre, wie eine HTTP-Anfrage und eine HTTP-Antwort funktionieren, lade JSON-Daten mit
fetchund sende Daten mit der passenden Methode sowie den passenden Headern und dem passenden Anfragekörper an eine API.- Lies HTTP-Anfragen und HTTP-AntwortenBeschreibe die Bestandteile einer HTTP-Anfrage und einer HTTP-Antwort, ordne jede HTTP-Methode der Aktion zu, die sie ausführt, und lies Statuscodes und Header in den Entwicklerwerkzeugen des Browsers.
- Lade JSON-Daten mit fetchRufe
fetchauf, prüfe den Status der Antwort, lies und parse den JSON-Antwortkörper und erstelle Anfrage-URLs mit korrekt kodierten Abfrageparametern. - Sende Daten mit POST, PUT, PATCH und DELETEÜbergib
fetchein Optionsobjekt, um JSON oder Formulardaten zu senden, und behandle die Antwort des Servers nach dem Erstellen, Aktualisieren oder Löschen eines Datensatzes.
- Lies HTTP-Anfragen und HTTP-Antworten
- Wandle API-Ergebnisse in Seiteninhalte um, formatiere Datumsangaben und Geldbeträge passend für die Leser und halte die Seite übersichtlich, während Daten laden, das Laden fehlschlägt oder Daten in anderer Reihenfolge eintreffen.
- Rendere API-Ergebnisse auf der SeiteErstelle Seitenelemente aus den Arrays und Objekten einer JSON-Antwort, füge API-Text sicher ein und behandle fehlende Felder, leere Ergebnisse und seitenweise gelieferte Ergebnisse.
- Formatiere Datumsangaben, Zahlen und Geldbeträge aus API-DatenWandle die Datumszeichenketten, Zeitstempel und Beträge, die APIs senden, mit den
Intl-Formatierern in lesbaren Text für Sprache, Region und Zeitzone der Leser um. - Zeige Ladezustände, Fehlerzustände und Leerzustände anVerfolge die Phasen einer Anfrage, zeige dem Nutzer für jede Phase eine klare Meldung und setze die Verarbeitung nach Fehlern mit Wiederholungsversuchen fort.
- Brich Anfragen ab, begrenze ihre Wartezeit und kombiniere sieBrich nicht mehr benötigte Anfragen ab, setze Zeitlimits, verhindere, dass veraltete Antworten neuere ersetzen, und führe mehrere Anfragen parallel oder nacheinander aus.
- Rendere API-Ergebnisse auf der Seite
- Erkläre die Same-Origin-Policy und CORS-Fehler und schütze API-Schlüssel und Nutzerdaten, wenn eine Seite mit anderen Servern kommuniziert.
- Verstehe die Same-Origin-Policy und CORSBestimme den Ursprung einer URL, erkläre, warum der Browser manche Antworten anderer Ursprünge blockiert, und erkenne, wie ein Server sie mit CORS-Headern und Preflight-Anfragen erlauben kann.
- Schütze API-Schlüssel und NutzerdatenErkläre, wozu API-Schlüssel und Token dienen, warum ein Geheimnis im Frontend-Code öffentlich ist und wie du Anmeldeinformationen sendest und verantwortungsvoll mit Nutzerdaten umgehst.
- Verstehe die Same-Origin-Policy und CORS
- Speichere, lies und aktualisiere kleine Datenmengen mit localStorage und sessionStorage und entscheide, was sich sinnvoll und sicher im Browser aufbewahren lässt.
- Speichere und lies Daten mit localStorage und sessionStorageNutze die Web-Storage-Methoden, erkläre, wie lange die jeweilige Speicherart erhalten bleibt und wer sie lesen kann, und wandle Zahlen, boolesche Werte und Objekte in gespeicherte Zeichenketten und zurück um.
- Nutze Browserspeicher in einer AnwendungStelle gespeicherte Zustände beim Laden einer Seite wieder her, speichere API-Antworten mit einer Ablaufzeit zwischen, halte Tabs synchron und entscheide, was nicht gespeichert werden sollte.
- Speichere und lies Daten mit localStorage und sessionStorage
- Erkenne und nutze Browser-APIs, die eine sichere Seite oder die Berechtigung des Nutzers benötigen, etwa für die Standortermittlung und die Zwischenablage, und biete Nutzern eine Rückfalllösung, wenn eine Funktion nicht verfügbar ist.
- Ermittle den Standort des Nutzers mit der Geolocation-APIPrüfe, ob eine Browser-API verfügbar ist, erkläre sichere Kontexte und Berechtigungsanfragen und nutze die Geolocation-API mit ihren Optionen, Fehlern und einer Rückfalllösung.
- Kopiere Text und nutze weitere Browser-APIsKopiere Text mit der Clipboard-API, reagiere auf Änderungen der Netzwerkverbindung, lies Parameter aus der Seitenadresse und nutze eine Methode, um beliebige neue Browser-APIs kennenzulernen.
- Ermittle den Standort des Nutzers mit der Geolocation-API
Daten abrufen und Browser-APIs einfach erklärt
1Stelle Anfragen und lies die Antworten
Eine Seite kann Daten von einem Server anfordern, ohne eine neue Seite zu laden. Die Anfrage gibt an, welche Ressource du möchtest und was du damit tun willst; die Antwort sagt dir, was passiert ist. Du verfolgst diesen Austausch im Netzwerk-Tab, wandelst eine JSON-Antwort in JavaScript-Daten um und sendest Änderungen an den Server zurück. Die wichtigen Prüfpunkte sind die Methode, der Status und die Struktur des Antwortkörpers: Dass eine Antwort eintrifft, bedeutet allein noch nicht, dass die Operation erfolgreich war.
1.1 Die Bestandteile eines HTTP-Austauschs
Eine Web-API lässt ein Programm Daten über benannte URLs lesen oder verändern, die Endpunkte heißen. Das JavaScript auf der Seite ist der Client. Es sendet eine HTTP-Anfrage; der Server antwortet mit einer HTTP-Antwort. Viele APIs senden JSON-Text, der Objekte, Arrays, Zeichenketten, Zahlen, boolesche Werte und null darstellen kann.
Eine Anfrage lesen
Betrachte GET https://api.example.com/books?author=Le%20Guin&limit=5. Die Methode ist GET, der Pfad /books wählt die Ressource aus und die Abfragezeichenkette enthält zwei Parameter. Der Wert von author ist „Le Guin“; %20 kodiert das Leerzeichen. Ein kaufmännisches Und trennt die Parameter. Die API-Dokumentation erklärt, welche Parameternamen sie akzeptiert und was die einzelnen Werte bedeuten.
Eine Anfrage kann außerdem Header enthalten, also Name-Wert-Paare, sowie einen Anfragekörper mit den übermittelten Daten. Accept: application/json fordert eine JSON-Antwort an. Content-Type: application/json beschreibt den gesendeten Körper. Authorization übermittelt Anmeldeinformationen. Bei Headernamen spielt die Groß- und Kleinschreibung keine Rolle, sodass content-type und Content-Type denselben Header bezeichnen.
Die Aktion wählen
| Methode | Beabsichtigte Aktion | Typische Verwendung |
|---|---|---|
GET | Lesen | Eine Liste von Notizen abrufen |
POST | Erstellen oder übermitteln | Eine Notiz hinzufügen |
PUT | Ersetzen | Die vollständige Ersatznotiz senden |
PATCH | Teilweise ändern | Eine Notiz als erledigt markieren |
DELETE | Entfernen | Eine Notiz löschen |
GET und HEAD sind sichere Methoden: Ihr vorgesehener Zweck ist das Lesen. Verwende GET daher nicht für eine Löschschaltfläche. GET, PUT und DELETE sind idempotent. Wird dieselbe Anfrage wiederholt, bleibt der Server im selben beabsichtigten Zustand wie nach einmaliger Ausführung. Das Löschen einer bereits gelöschten Notiz kann einen anderen Status zurückgeben, aber die Notiz bleibt gelöscht. Ein wiederholtes POST kann einen zweiten Datensatz erstellen.
Die Antwort deuten
Die Antwort enthält einen Statuscode, Header und normalerweise einen Antwortkörper. Die erste Ziffer ordnet das Ergebnis einer Gruppe zu:
- 2xx: Erfolg.
200 OKenthält üblicherweise Daten,201 Createdmeldet die Erstellung und204 No Contenthat keinen Antwortkörper. - 3xx: Weiterleitung. Standardmäßig folgt
fetchWeiterleitungen. - 4xx: ein Problem mit der Anfrage oder dem Zugriff.
400bedeutet fehlerhafte Anfrage;401bedeutet fehlende oder ungültige Anmeldeinformationen;403bedeutet, dass der Server die Anfrage versteht, aber verweigert;404bedeutet nicht gefunden;429bedeutet zu viele Anfragen. - 5xx: ein Serverproblem, etwa
500 Internal Server Erroroder503 Service Unavailable.
Ein Aufruf von /books/42 könnte 200, einen JSON-Inhaltstyp und {"id":42,"title":"The Dispossessed","year":1974} zurückgeben. Ein Aufruf von /books/9999 könnte 404 und {"error":"Book not found"} zurückgeben. Beide Aufrufe erhielten eine Antwort. Bei einem Verbindungsfehler gibt es keinen HTTP-Status, den JavaScript prüfen könnte.
Prüfen statt raten
Öffne die Entwicklerwerkzeuge, wähle Netzwerk und löse die Aktion aus. Untersuche Methode, vollständige URL, Status, Header, Körper und Zeitangaben. Vergleiche sie mit dem Endpunkt, den erforderlichen Parametern, Anmeldeinformationen, Ratenbegrenzungen und der Beispielantwort in der Dokumentation. Hat der Server die richtigen Daten zurückgegeben, untersuche den Seitencode; ist die Anfrage-URL falsch, korrigiere sie, bevor du die Renderfunktion änderst.
Wichtige Erkenntnisse
- Ein Endpunkt ist eine URL, die eine API für eine Operation bereitstellt.
- Die Methode wählt die Aktion; die URL wählt die Ressource und die Parameter.
- Anfrage- und Antwortheader beschreiben den Austausch.
- Ein
404ist eine Antwort, während ein Verbindungsfehler keinen HTTP-Status hat. - Untersuche den Netzwerk-Tab und vergleiche den Austausch mit der API-Dokumentation.
1.2 Lade JSON in zwei asynchronen Schritten
fetch(url) sendet eine GET-Anfrage und gibt ein Promise zurück. Dessen Ergebnis ist ein Response-Objekt, das verfügbar ist, sobald die Header eintreffen. Das Lesen des Antwortkörpers ist eine separate asynchrone Operation. response.json() liest diesen Körper und parst ihn in JavaScript-Daten.
Warten, prüfen, lesen
async function loadBook(id) {
const response = await fetch(`https://api.example.com/books/${id}`);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return await response.json();
}
Bei loadBook(42) erhält das erste await die Antwort. Die Bedingung weist ein nicht erfolgreiches HTTP-Ergebnis zurück. Das zweite await erhält das geparste Buch, etwa { id: 42, title: "The Dispossessed", year: 1974 }. Die Funktion selbst gibt ein Promise zurück, sodass auch der aufrufende Code auf das Ergebnis wartet.
response.ok ist bei Statuscodes von 200 bis einschließlich 299 wahr. Ein HTTP-Fehler wie 404 oder 500 führt nicht zur Ablehnung des fetch-Promises: Ein Server hat geantwortet. Ein Netzwerkfehler, ein durch CORS blockierter Austausch, eine ungültige Anfrage-URL oder ein Abbruch können es ablehnen. Wenn du bei !response.ok einen Fehler auslöst, kann ein umgebendes try/catch auch HTTP-Fehler behandeln.
Den Antwortkörper verstehen
Ohne await speichert const data = response.json() ein Promise und nicht das Buch. Der Zugriff auf data.title ergibt dann undefined. Lässt du await bei fetch weg, bleibt ebenso ein Promise dort, wo der Code ein Response-Objekt erwartet.
Wähle die zur Antwort passende Methode zum Lesen des Körpers: json() für JSON, text() für Text oder blob() für Binärdaten. Ein Körper kann nur einmal gelesen werden. Speichere das geparste Ergebnis und verwende es erneut. Um auch den rohen JSON-Text zu untersuchen, lies text() einmal und rufe dann JSON.parse(text) für diese gespeicherte Zeichenkette auf.
Ungültiges JSON führt zur Ablehnung mit einem SyntaxError. Eine HTML-Fehlerseite verursacht oft eine Meldung wie „Unexpected token '<'“. Auch ein leerer Körper, etwa bei einer 204-Antwort, lässt sich nicht als JSON parsen. Prüfe zuerst den Status und untersuche response.headers.get("Content-Type"), wenn das Format unklar ist. Ein Erfolgsstatus allein garantiert keinen JSON-Antwortkörper.
Eine Abfrage sicher erstellen
const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "rock & roll");
url.searchParams.set("limit", 10);
Die resultierende Adresse ist https://api.example.com/search?q=rock+%26+roll&limit=10. Das enthaltene kaufmännische Und wird als Teil des Werts kodiert. Hängst du Eingaben an "?q=" an, können &, # oder + ihre URL-Bedeutung erhalten und die Suche verändern. Eine relative URL wird nach den üblichen Regeln aufgelöst: Von https://notes.example.org/app/index.html aus lädt fetch("data/books.json") den Pfad /app/data/books.json auf diesem Ursprung.
Der zurückgegebenen Struktur folgen
Bei {"results":[{"title":"Dune"}],"total":1} ist der erste Titel data.results[0].title. Das äußere Objekt ist nicht das Ergebnisarray. Nutze die Antwortvorschau, um das Array zu finden, bevor du es durchläufst.
Eine gemeinsam verwendete Hilfsfunktion getJSON(url, options) kann für viele Aufrufe dieselbe Statusprüfung und dasselbe Lesen des Antwortkörpers übernehmen. Gib in einer .then()-Kette response.json() aus dem Antwort-Callback zurück, damit der nächste Callback geparste Daten erhält. Hänge am Ende .catch() an, um abgelehnte Schritte zu behandeln.
Wichtige Erkenntnisse
fetchundresponse.json()geben separate Promises zurück.- Prüfe
response.ok; ein HTTP-Fehler führt nicht zur Ablehnung vonfetch. - Lies einen Antwortkörper einmal und verwende den gespeicherten Wert erneut.
- Verwende
URL.searchParams, um Abfragewerte zu kodieren. - Lies verschachteltes JSON entsprechend seiner tatsächlichen Objekt- und Arraystruktur.
- Auch eine erfolgreiche Antwort kann einen leeren oder ungültigen JSON-Antwortkörper enthalten.
1.3 Sende eine Änderung und nutze das Ergebnis des Servers
Das zweite Argument von fetch konfiguriert eine Anfrage. Zu seinen Optionen gehören method, headers, body, signal und credentials. Ohne diese Optionen ist die Anfrage ein GET ohne Anfragekörper. Um JSON zu übermitteln, musst du sowohl den Text als auch seinen Inhaltstyp vorbereiten.
JSON senden
const response = await fetch("https://api.example.com/notes", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Shopping", text: "Rice, lentils" }),
});
JSON.stringify erzeugt {"title":"Shopping","text":"Rice, lentils"}. Der Header sagt dem Server, wie er diesen Text lesen soll. Wird das Objekt direkt als Anfragekörper übergeben, wird "[object Object]" gesendet. Wird die JSON-Zeichenkette ohne den Header gesendet, wird sie als text/plain;charset=UTF-8 gekennzeichnet. Viele JSON-Endpunkte lehnen dies mit 400 oder 415 Unsupported Media Type ab.
Eine erfolgreiche Erstellung gibt oft 201 mit dem gespeicherten Datensatz zurück, etwa {"id":17,"title":"Shopping","text":"Rice, lentils"}. Nutze dieses zurückgegebene Objekt, um die Seite zu aktualisieren: Die neue id des Servers wird für spätere Bearbeitungen und das Löschen benötigt.
Ersetzen oder aktualisieren
| Anfrage | Anfragekörper | Bedeutung |
|---|---|---|
PATCH /notes/17 | {"done":true} | Das übermittelte Feld ändern |
PUT /notes/17 | Die vollständige Ersatznotiz | Die Ressource ersetzen |
DELETE /notes/17 | Oft keiner | Die Ressource entfernen |
Fehlt in einem PUT-Anfragekörper ein Feld, kann dieses Feld je nach API entfernt oder zurückgesetzt werden. Verwende für die beabsichtigte Änderung die dokumentierte Methode. PUT und PATCH können einen aktualisierten Datensatz mit 200 oder 204 ohne Antwortkörper zurückgeben.
Behandle den Erfolg, ohne anzunehmen, dass immer JSON folgt:
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const saved = response.status === 204
? null
: await response.json();
Dieses Muster passt zu Endpunkten, die laut Dokumentation entweder JSON oder 204 zurückgeben. Ein Löschvorgang kann erfolgreich sein und dennoch einen Parserfehler erzeugen, wenn der Code auf dem leeren Antwortkörper blind json() aufruft.
Das Format des Anfragekörpers wählen
new FormData(formElement) sammelt benannte Formularfelder und ausgewählte Dateien. Übergib das Ergebnis direkt als body. Lass den Browser Content-Type: multipart/form-data und dessen Trennzeichen setzen; bei einem manuell angegebenen Header kann das Trennzeichen fehlen, das zur Trennung der Felder benötigt wird. Ein URLSearchParams-Anfragekörper verwendet application/x-www-form-urlencoded.
Anfragen mit GET und HEAD können über fetch keinen Anfragekörper übertragen. Setze Suchbegriffe und Filter in die Abfragezeichenkette der URL. Der Versuch, GET mit einem Anfragekörper zu verwenden, wird mit einem TypeError abgelehnt, bevor er als Suchanfrage dienen kann.
Nach Erfolg oder Fehler sicher abschließen
Deaktiviere die Absende-Schaltfläche, bevor du auf eine Anfrage zum Erstellen wartest, und aktiviere sie dann in finally wieder. Ein Doppelklick kann sonst zwei POST-Anfragen senden und zwei Datensätze erstellen. Wiederhole einen Erstellvorgang mit ungewissem Ausgang nicht automatisch: Eine verlorene Antwort sagt nicht aus, ob der Server die Änderung vorgenommen hat.
Bei einem Fehlerstatus kann der Antwortkörper eine nützliche Meldung wie {"error":"title is required"} enthalten. Lies ihn einmal, wenn er JSON enthält; fange einen fehlgeschlagenen Parsvorgang ab und behalte bei leeren oder HTML-Antworten eine allgemeine HTTP-Meldung bei. Lass den vom Nutzer eingegebenen Text verfügbar, damit er die Anfrage korrigieren kann.
Wichtige Erkenntnisse
- JSON-Anfragen benötigen
JSON.stringifyund einen passenden Inhaltstyp. - Verwende
PATCHfür eine teilweise Änderung undPUTfür einen vollständigen Ersatz. - Überspringe das Parsen von JSON bei
204 No Content. - Lass den Browser beim Senden von
FormDataden Multipart-Header setzen. - Nutze die Antwort des Servers einschließlich der vergebenen ID, um die Seite zu aktualisieren.
- Verhindere mehrfaches Absenden und stelle die Bedienelemente in
finallywieder her.
2Mache aus Antworten eine nützliche Seite
Das Abrufen von Daten ist nur der erste Teil einer Benutzeroberfläche. Leser brauchen nützliche Ergebnisse, lesbare Datumsangaben und Preise sowie eine klare Meldung, während sie warten. Du baust die Anzeige anhand der Antwortstruktur auf, verhinderst, dass fehlende Werte sie beschädigen, und machst Erfolge, leere Ergebnisse und Fehler sichtbar. Außerdem steuerst du überlappende Anfragen, damit eine langsame Antwort keine neueren Ergebnisse ersetzen kann, und entscheidest, wann unabhängige Anfragen gleichzeitig laufen können.
2.1 Erstelle die Anzeige aus Daten
Rendern wandelt ein Array oder Objekt in Elemente auf der Seite um. Speichere die abgerufenen Daten in einer Variablen und übergib sie einer Renderfunktion. Durch diese Trennung kannst du dieselben Daten sortieren oder filtern, ohne sie erneut vom Server anzufordern.
Ein Element pro Eintrag erstellen
function renderBooks(books, list) {
list.replaceChildren();
for (const book of books) {
const item = document.createElement("li");
item.textContent = `${book.title} (${book.year})`;
list.append(item);
}
}
Für [{"title":"Kindred","year":1979},{"title":"Beloved","year":1987}] entstehen zwei Listeneinträge: „Kindred (1979)“ und „Beloved (1987)“. Wird der Container zuerst geleert, ersetzt eine neue Suche die vorherige. Ohne diesen Schritt sammeln sich die alten und neuen Listen an.
Externen Text als Text behandeln
Verwende textContent für API-Titel, Beschreibungen und andere Texte. innerHTML parst eine Zeichenkette als Markup. Ein Titel mit <img src="x" onerror="alert(1)"> kann dann Code ausführen. Das ist Cross-Site-Scripting, kurz XSS. Text aus Formularen und Speicher muss ebenso behandelt werden, weil auch er aus einer nicht vertrauenswürdigen Quelle stammen kann.
Elementeigenschaften setzen einzelne Werte, etwa img.alt = book.title. Sie machen aber nicht automatisch jede Art von Wert sicher. Ein von der API gelieferter Link benötigt weiterhin eine Protokollprüfung:
try {
const target = new URL(book.url, location.href);
if (target.protocol === "https:" || target.protocol === "http:") {
link.href = target.href;
}
} catch {
link.removeAttribute("href");
}
Dieser Code wird für einen neu erstellten Link ohne vorhandenes href ausgeführt. Bei einer ungültigen oder nicht erlaubten URL bleibt er ohne Ziel. Eine javascript:-URL ist keine gewöhnliche Webnavigation. Gib für Bilder einen aussagekräftigen alt-Text an, den du nach Möglichkeit aus den Daten erstellst.
Standardwerte setzen, ohne echte Werte zu verlieren
Optionale Felder können fehlen oder null sein. book.author?.name ?? "Unknown author" versucht zuerst, den verschachtelten Namen zu lesen, und liefert andernfalls einen Standardwert. Die optionale Verkettung verhindert den Zugriff auf eine Eigenschaft eines fehlenden Autors.
Der Operator ist entscheidend: 0 || "n/a" ergibt "n/a", während 0 ?? "n/a" den Wert 0 beibehält. Der Operator || ersetzt alle Falsy-Werte, einschließlich null und einer leeren Zeichenkette; ?? ersetzt nur null und undefined. Bei einem Buch mit null verfügbaren Exemplaren muss null angezeigt werden.
Ein leeres Ergebnisarray ist eine erfolgreiche Suche ohne Treffer. Zeige „Keine Bücher entsprechen deiner Suche“ an, damit Leser dies von einer Seite unterscheiden können, die noch nicht geladen wurde.
Eine Suche ersetzen, eine weitere Seite anhängen
Viele APIs liefern Seiten mit page, limit, einer Gesamtzahl total oder einer next-URL. Bei 47 Ergebnissen und 10 pro Seite enthalten vier volle Seiten 40 Datensätze und die fünfte 7. Eine neue Suche leert die vorherige Liste; „Mehr laden“ hängt die nächste Seite an die aktuelle Liste an. Halte diese beiden Anzeigeaktionen getrennt.
Um eine lokale Kopie mit den neuesten Einträgen zuerst zu sortieren, verwende [...books].sort((a, b) => b.year - a.year) und rendere diese Kopie. Der Spread erstellt ein neues Array, sodass die gespeicherte Reihenfolge der Antwort verfügbar bleibt. Untersuche die Antwortstruktur, bevor du das Array auswählst: Eine API mit Paginierung legt es oft in data.results ab.
Wichtige Erkenntnisse
- Rendere aus gespeicherten Daten, damit lokales Sortieren keine neue Anfrage benötigt.
- Füge nicht vertrauenswürdigen Text mit
textContentein. - Validiere das Protokoll eines API-Links, bevor du sein Ziel zuweist.
- Verwende
?.für fehlende verschachtelte Daten und??, wenn null erhalten bleiben muss. - Ersetze Ergebnisse bei einer neuen Suche; hänge Ergebnisse für eine weitere Seite an.
2.2 Formatiere Datumsangaben und Beträge für Menschen
APIs geben Werte zurück, die Programme leicht austauschen können. Leser brauchen Datumsangaben, Uhrzeiten und Beträge, die für ihre Sprache und Region formatiert sind. Halte den gespeicherten Wert von dem Text für seine Anzeige getrennt.
Zuerst das Datumsformat lesen
"2026-03-05T14:30:00Z" bezeichnet einen Zeitpunkt: den 5. März 2026 um 14:30 Uhr UTC. Das Z bedeutet UTC. new Date(...) wandelt ihn in ein JavaScript-Date um. Eine Zeichenkette nur mit Datum wie "2026-03-05" wird als Mitternacht UTC interpretiert; ein Datum mit Uhrzeit ohne Versatz wie "2026-03-05T14:30" wird als Ortszeit interpretiert.
Ein Unix-Zeitstempel zählt ab dem 1. Januar 1970, UTC. APIs zählen oft Sekunden, aber Date erwartet Millisekunden. new Date(1772721000 * 1000) ergibt denselben Zeitpunkt am 5. März. Lässt du die Multiplikation weg, erhältst du ein Datum im Januar 1970. Lies die API-Dokumentation, um die Einheit zu bestimmen.
Gebietsschema und Zeitzone wählen
const date = new Date("2026-03-05T14:30:00Z");
const dateFormat = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short",
timeZone: "UTC",
});
dateFormat.format(date);
Die Ausgabe ist „Mar 5, 2026, 2:30 PM“. Mit en-GB ist sie „5 Mar 2026, 14:30“. Bei kurzen Datumsformaten entsteht „3/5/26“ in en-US, „05/03/2026“ in en-GB und „05.03.26“ in de-DE.
Das Gebietsschema steuert die Darstellung; die Zeitzone bestimmt die lokale Uhrzeit. Ohne timeZone verwendet der Formatierer die Zeitzone des Geräts. Dieser Zeitpunkt ist 9:30 Uhr in New York, 15:30 Uhr in Amsterdam und 23:30 Uhr in Tokio. Speichere und sende Zeitpunkte in UTC und wandle sie für die Anzeige um. Bei einem Kalenderdatum wie einem Geburtstag ist Vorsicht nötig: Wird Mitternacht UTC in einer Zeitzone westlich von UTC formatiert, kann der vorherige Tag erscheinen.
Währung und Gebietsschema getrennt halten
const priceFormat = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "EUR",
});
priceFormat.format(1234.5);
Das ergibt €1,234.50. Für denselben Betrag ergibt de-DE mit EUR 1.234,50 €; en-US mit USD ergibt $1,234.50; en-US mit JPY ergibt ¥1,235. JPY hat keine Währungsuntereinheit, daher hat seine übliche Anzeige keine Dezimalstellen. Die Wahl eines Gebietsschemas rechnet keine Währungen um. Verwende den Währungscode, der mit dem Betrag geliefert wird.
Manche Zahlungs-APIs senden ganzzahlige Währungsuntereinheiten. Bei USD bedeuten 1999 Cent 19.99 US-Dollar: Dividiere vor dem Formatieren durch 100. Wende diesen Divisor nicht auf JPY an. Das Addieren ganzzahliger Centbeträge ergibt 1999 + 2550 = 4549, was als $45.49 formatiert wird. So vermeidest du das bekannte Ergebnis der Dezimalrechnung 0.1 + 0.2, nämlich 0.30000000000000004.
Den Formatierer wiederverwenden
Erstelle für eine Liste einmal einen Formatierer. Verwende undefined als Gebietsschema, um die Standardeinstellung des Browsers zu wählen; navigator.language meldet das bevorzugte Sprachkürzel des Browsers. Andere Optionen formatieren 0.256 als „26%“ oder 12.5 mit der Einheit "kilometer-per-hour" als „12.5 km/h“. new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-1, "day") ergibt „yesterday“. Von Hand erstellte Datumsangaben wie „03/05/2026“ bleiben über Regionen hinweg mehrdeutig.
Wichtige Erkenntnisse
- Prüfe, ob Zeitstempel Sekunden oder Millisekunden verwenden.
- Eine ausdrücklich angegebene Zeitzone macht die Anzeige von Datum und Uhrzeit vorhersehbar.
- Das Gebietsschema steuert die Formatierung; die Währung stammt aus den Daten.
- Wandle dokumentierte Währungsuntereinheiten um, bevor du Geldbeträge formatierst.
- Verwende
Intl-Formatiererobjekte innerhalb einer Liste erneut. - Zeichenketten nur mit Datum werden als UTC geparst und können bei der Anzeige in einer anderen Zeitzone den Tag wechseln.
2.3 Zeige, was die Anfrage gerade tut
Eine Anfrage hat einen sichtbaren Lebenszyklus: inaktiv, lädt, dann Erfolg oder Fehler. Ein Erfolg kann Daten oder ein leeres Ergebnis enthalten. Die Seite sollte jeden Zustand erklären, damit Leser wissen, ob sie warten, eine Suche ändern oder es erneut versuchen sollen.
Vorbereitung und Aufräumen zusammenhalten
In diesem Handler prüft getJSON den HTTP-Status und parst den Antwortkörper, searchUrl erstellt die Anfrage-URL und renderResults aktualisiert die Liste. Die Schaltfläche und das Statuselement sind bereits auf der Seite vorhanden.
async function search(query) {
button.disabled = true;
status.textContent = "Loading…";
try {
const data = await getJSON(searchUrl(query));
renderResults(data.results);
status.textContent = data.results.length === 0 ? "No results." : "";
} catch (error) {
status.textContent = "Could not load results. Check your connection and try again.";
console.error(error);
} finally {
button.disabled = false;
}
}
Die Vorbereitung erfolgt vor dem Warten. Der Erfolgszweig rendert Ergebnisse und behandelt ein leeres Array. Der catch-Block ersetzt die Lademeldung, falls einer der erwarteten Schritte fehlschlägt. finally stellt die Schaltfläche nach beiden Wegen wieder her. Stünde das Aufräumen nur nach einem erfolgreichen await, bliebe das Bedienelement bei einem Fehler deaktiviert.
Die hilfreiche Ursache erklären
Ein HTTP-Fehler hat einen Status, an dem sich die Meldung orientieren kann. Teile bei 404 mit, dass der Eintrag nicht gefunden wurde. Bitte den Nutzer bei 401, sich anzumelden; erkläre bei 403, dass der Zugriff nicht erlaubt ist. Bitte ihn bei 429 zu warten. Erkläre bei 5xx, dass der Dienst ein Problem hat, und schlage einen späteren Versuch vor.
Ein TypeError von fetch kann einen Netzwerkfehler, eine blockierte CORS-Anfrage oder eine fehlerhafte URL bedeuten. JavaScript erhält keine zuverlässige detaillierte CORS-Diagnose; untersuche die Browserkonsole und den Netzwerk-Tab. Ein SyntaxError von json() weist auf ungültiges JSON hin. Abbrüche und Zeitlimits erzeugen Fehler wie AbortError und TimeoutError. Belasse technische Details in der Konsole und gib Lesern eine verständliche Handlungsmöglichkeit.
Änderungen bemerkbar machen
Verwende ein vorhandenes Statuselement wie <p role="status"></p>. Seine Änderungen werden Screenreadern angesagt, ohne den Fokus zu verschieben. aria-live="polite" dient einem ähnlichen Zweck; role="alert" eignet sich für dringende Fehler. Ein Ladesymbol allein vermittelt weder ein leeres Ergebnis noch eine fehlgeschlagene Anfrage.
Eine Schaltfläche „Erneut versuchen“ ruft die Ladefunktion erneut auf. Werden Anfragen mit 400 oder 404 unverändert wiederholt, tritt das Problem meist erneut auf. Vorübergehende Netzwerk- oder Dienstfehler können eine kurze, begrenzte Folge von Wiederholungsversuchen rechtfertigen. Exponentielles Backoff verlängert die Pause, zum Beispiel 1 Sekunde, 2 Sekunden, dann 4 Sekunden. Beachte einen Retry-After-Antwortheader; er kann eine Verzögerung in Sekunden oder ein HTTP-Datum angeben. Vermeide es, eine Erstellanfrage automatisch zu wiederholen, die möglicherweise bereits erfolgreich war.
Schlägt eine Aktualisierung fehl, sind frühere Daten aber weiterhin nützlich, lass sie mit einem Hinweis wie „Ergebnisse von 10:42 Uhr werden angezeigt. Aktualisierung fehlgeschlagen.“ sichtbar. Lass alte Ergebnisse nicht stillschweigend aktuell erscheinen.
Die schwierigen Abläufe testen
Verwende die Entwicklerwerkzeuge, um die Verbindung zu verlangsamen, offline zu gehen und einen fehlenden Pfad anzufordern. Prüfe, ob der Ladezustand sofort erscheint, eine fehlgeschlagene Anfrage ihn ersetzt, ein Erfolg ohne Ergebnis eine eigene Meldung hat und die Bedienelemente wieder benutzbar werden.
Wichtige Erkenntnisse
- Mache Laden, Erfolg ohne Ergebnis, Erfolg mit Daten und Fehler sichtbar.
- Setze gemeinsames Aufräumen in
finally. - Gib Lesern eine hilfreiche Handlungsmöglichkeit und belasse technische Details in der Konsole.
- Kündige Statusaktualisierungen über eine geeignete Live-Region an.
- Begrenze Wiederholungsversuche und beachte die vom Server verlangte Wartezeit.
2.4 Steuere überlappende und parallele Anfragen
Anfragen können länger laufen als die Aktion, die sie gestartet hat. Eine Suchantwort kann eintreffen, nachdem der Leser etwas anderes eingegeben hat, und ein langsamer Dienst kann eine Seite zu lange warten lassen. Steuere sowohl, welche Ergebnisse noch relevant sind, als auch, ob die einzelnen Anfragen erfolgreich waren.
Nicht mehr benötigte Arbeit abbrechen
Erstelle einen AbortController, übergib sein signal an fetch und rufe dann abort() auf, um die Anfrage abzubrechen. Der Abbruch kann sowohl das Warten auf Header als auch einen laufenden Lesevorgang des Antwortkörpers unterbrechen. Der Fehler bei der Ablehnung heißt normalerweise "AbortError".
const controller = new AbortController();
const response = await fetch(url, { signal: controller.signal });
// A cancel button can call controller.abort().
const data = await response.json();
Behandle Fehler bei beiden Aufrufen mit await. Wenn dein Code eine veraltete Suche absichtlich abbricht, kehre bei diesem erwarteten Abbruch ohne Meldung zurück, statt dem Nutzer mitzuteilen, dass der Dienst ausgefallen ist.
fetch hat keine eingebaute Option timeout. Verwende ein Signal wie AbortSignal.timeout(5000), um die Anfrage auf 5000 ms oder 5 Sekunden aktive Zeit zu begrenzen. Vor dem Eintreffen der Header führt der Timeout zur Ablehnung mit TimeoutError; danach kann er das Lesen des Antwortkörpers unterbrechen.
Verhindern, dass alte Suchen gewinnen
Eine Wettlaufsituation entsteht, wenn die Reihenfolge der Abschlüsse das Ergebnis verändert. Ein Leser tippt „ca“ und dann „cat“. Wenn „ca“ zuletzt zurückkommt und gerendert wird, passt die Anzeige nicht mehr zur Eingabe.
Ein Ansatz bricht vor jeder neuen Anfrage die vorherige ab. Ein anderer gibt jedem Aufruf eine Nummer und lässt nur den neuesten Aufruf die Seite aktualisieren:
let latest = 0;
async function search(query) {
const request = ++latest;
try {
const data = await getJSON(searchUrl(query));
if (request !== latest) return;
renderResults(data.results);
} catch (error) {
if (request !== latest) return;
showError(error);
}
}
Die Prüfung verhindert auch, dass ein alter Fehler einen neueren Erfolg ersetzt. Wende bei überlappenden Aufrufen dieselbe Relevanzprüfung auf Lademeldungen und das Zurücksetzen von Bedienelementen an.
Debouncing verringert die Anzahl der gestarteten Aufrufe. Lösche bei jedem Tastendruck den vorherigen Timer und starte einen neuen, zum Beispiel für 300 ms. Wird „paris“ schnell eingegeben, startet nach der Pause eine Anfrage. Debouncing spart Anfragen; es verhindert allein nicht, dass eine frühere, noch laufende Antwort verspätet eintrifft.
Entscheiden, was gleichzeitig laufen kann
Unabhängige Anfragen können gemeinsam starten:
const [user, posts] = await Promise.all([
getJSON(userUrl),
getJSON(postsUrl),
]);
Bei einer Dauer von 300 ms und 500 ms benötigen beide zusammen etwa 500 ms statt etwa 800 ms bei sequenzieller Ausführung. Promise.all wird abgelehnt, sobald eine Eingabe abgelehnt wird, bricht aber die anderen Operationen nicht ab.
Verwende Promise.allSettled, wenn Teilergebnisse nützlich sind. Es wartet auf jede Eingabe und meldet sie als { status: "fulfilled", value } oder { status: "rejected", reason }. Eine Übersicht kann dann erfolgreiche Bereiche neben einem fehlgeschlagenen anzeigen.
Abhängige Anfragen müssen nacheinander laufen. Wenn eine zweite URL eine ID aus der ersten Anfrage benötigt, warte auf diese ID, bevor du die zweite Anfrage erstellst.
Wichtige Erkenntnisse
- Übergib ein Abbruchsignal, bevor du
abort()aufrufst. - Fange Fehler sowohl beim Abrufen als auch beim Lesen des Antwortkörpers ab.
- Ignoriere veraltete Ergebnisse und veraltete Fehler.
- Debouncing reduziert Aufrufe; eine Relevanzprüfung verhindert veraltete Aktualisierungen.
- Verwende
Promise.allfür unabhängige Arbeiten, die gemeinsam erfolgreich sein müssen. - Verwende
Promise.allSettled, wenn Teilergebnisse nützlich sind. - Warte auf benötigte Daten, bevor du eine abhängige Anfrage startest.
3Lies Daten anderer Ursprünge und schütze Anmeldeinformationen
Eine Anfrage kann korrekt aussehen und im Browser trotzdem fehlschlagen, weil Seite und API zu unterschiedlichen Ursprüngen gehören. Diese Grenze wird gemeinsam durch Browserregeln und die Antwortheader des Servers gesteuert. Du bestimmst den Ursprung, deutest Fehler bei Preflight-Anfragen und CORS und unterscheidest die Berechtigung zum Lesen einer Antwort von der Berechtigung zum Ausführen einer Aktion. Bei Anmeldeinformationen ist eine eigene Entscheidung nötig: Alles, was an einen Browser geliefert wird, kann dessen Nutzer untersuchen.
3.1 Verstehe die Ursprungsgrenze des Browsers
Ein Ursprung kombiniert Schema, Host und Port einer URL. Ein Pfad gehört nicht dazu. https://shop.example.com/cart und /help auf derselben Website haben denselben Ursprung. Ein Wechsel zu http://shop.example.com, https://api.example.com oder https://shop.example.com:8443 ändert den Ursprung. HTTPS verwendet normalerweise Port 443.
Wer erlaubt das Lesen der Antwort?
Die Same-Origin-Policy begrenzt, welche Ressourcen ein Seitenskript lesen darf. Für einen Abruf von einem anderen Ursprung ermöglicht CORS dem API-Server, den Zugriff über Antwortheader zu gewähren. Eine Seite unter https://notes.example.org, die https://api.example.com/weather aufruft, benötigt die Erlaubnis der API, um die Antwort zu lesen.
Access-Control-Allow-Origin: https://notes.example.org gewährt diesem Ursprung Zugriff. Der Wert * erlaubt bei einer Anfrage ohne Anmeldeinformationen jeden Ursprung. Hat die Antwort keine passende Erlaubnis, verbirgt der Browser sie vor der Seite und fetch wird mit einem TypeError abgelehnt. Die Konsole liefert die CORS-Diagnose; JavaScript erhält den genauen Grund nicht.
Access-Control-Allow-Origin zur JavaScript-Anfrage hinzuzufügen, gewährt keine Erlaubnis. Es ist ein vom Server gesteuerter Antwortheader. Ein zusätzlicher benutzerdefinierter Anfrageheader kann stattdessen eine zusätzliche Preflight-Prüfung auslösen.
Warum OPTIONS vor POST erscheint
Manche Anfragen an andere Ursprünge benötigen eine Preflight-Anfrage. Der Browser sendet zuerst OPTIONS und führt die beabsichtigte Methode in Access-Control-Request-Method sowie nicht allgemein zugelassene Headernamen in Access-Control-Request-Headers auf. Der Server erlaubt sie mit Access-Control-Allow-Methods und Access-Control-Allow-Headers zusammen mit der Ursprungserlaubnis.
Ein einfaches GET, HEAD oder POST mit ausschließlich erlaubten Headern kann ohne Preflight auskommen. Zu den erlaubten Inhaltstypen gehören text/plain, application/x-www-form-urlencoded und multipart/form-data. Ein JSON-POST, ein Authorization-Header, PUT oder DELETE benötigt eine Preflight-Anfrage, sofern keine gültige frühere Erlaubnis zwischengespeichert ist. Wird die Preflight-Anfrage abgelehnt, wird die eigentliche Anfrage nicht gesendet.
Bei einer einfachen Anfrage kann die eigentliche Anfrage den Server erreichen, auch wenn der Browser später das Lesen ihrer Antwort blockiert. CORS ersetzt daher weder die Autorisierung noch die Eingabeprüfung auf dem Server.
Cookies bringen eine weitere Anforderung mit sich
Die Standardeinstellung credentials: "same-origin" sendet Cookies nur bei Anfragen an denselben Ursprung. credentials: "include" erlaubt geeignete Cookies auch bei Anfragen an andere Ursprünge. Die SameSite-Regeln für Cookies und Richtlinien für Drittanbieter-Cookies gelten weiterhin.
Damit JavaScript eine Antwort auf eine Anfrage mit Anmeldeinformationen lesen kann, muss der Server Access-Control-Allow-Credentials: true senden und den Ursprung der Seite ausdrücklich nennen. Access-Control-Allow-Origin: * funktioniert nicht mit Anmeldeinformationen. Diese Header zwingen einen Browser nicht dazu, ein anderweitig blockiertes Cookie zu senden.
Diagnostizieren, ohne das Problem zu verbergen
mode: "no-cors" ergibt eine opake Antwort mit Status 0 und ohne lesbaren Antwortkörper oder lesbare Header. Sie kann einer Seite kein JSON bereitstellen. Auch eine Anfrage, die in curl funktioniert, belegt keine CORS-Erlaubnis im Browser: Terminal-Clients setzen diese Browserregel nicht durch.
Untersuche die Konsole und den Netzwerk-Tab. Prüfe, ob die Preflight-Anfrage fehlgeschlagen ist oder der abschließenden Antwort die Erlaubnis fehlte. Korrigiere die Antwortkonfiguration der API oder leite den Aufruf über deinen eigenen Server. Stelle Seiten während der Entwicklung über einen lokalen Server bereit: Seiten mit file:// haben häufig opake Ursprünge. Auch ein lokaler Server benötigt CORS-Erlaubnis, wenn er einen anderen Ursprung aufruft.
Wichtige Erkenntnisse
- Ein Ursprung besteht aus Schema, Host und Port.
- Die Antwortheader des Servers gewähren die Leseberechtigung für andere Ursprünge.
- Eine Preflight-Anfrage prüft eine vorgesehene Methode und Header vor der eigentlichen Anfrage.
- CORS kann eine Antwort verbergen, nachdem eine einfache Anfrage bereits verarbeitet wurde.
- Antworten auf Anfragen mit Anmeldeinformationen benötigen einen ausdrücklich angegebenen Ursprung und einen Header für Anmeldeinformationen.
no-corserzeugt eine opake Antwort und kein lesbares JSON.
3.2 Halte geheime Schlüssel von der Seite fern
Ein API-Schlüssel identifiziert eine aufrufende Anwendung für Nutzungserfassung, Begrenzungen und Abrechnung. Ein Zugriffstoken gewährt Zugriff für einen Nutzer oder eine Sitzung. Wer nutzbare Anmeldeinformationen besitzt, kann möglicherweise mit den damit verbundenen Rechten handeln. Entscheide daher, wohin sie gehören, bevor du sie einer Anfrage hinzufügst.
Header schützen den Ablageort, aber nicht vor dem Nutzer
Anbieter können Authorization: Bearer <token>, X-API-Key: <key> oder einen Abfrageparameter wie api_key verlangen. Folge der API-Dokumentation. Ein Header verhindert, dass die Anmeldeinformation in einer vollständigen URL steht, die in Protokolle und Analysewerkzeuge gelangen kann. Ein Authorization- oder benutzerdefinierter Header bei einem Aufruf an einen anderen Ursprung löst normalerweise eine Preflight-Anfrage aus.
Ein Header ist in den Entwicklerwerkzeugen trotzdem sichtbar. Besucher können das heruntergeladene JavaScript und die von ihm gesendeten Anfragen untersuchen. Code zu minifizieren oder einen Wert hinter einer Variablen zu verbergen, macht ihn nicht geheim.
Den Schlüssel durch eine Wetteranfrage verfolgen
Angenommen, Browsercode ruft https://api.weather.example/v1/current?city=Lima&key=sk_live_4f9a2c auf. Jeder, der den Netzwerk-Tab öffnet, kann den Schlüssel kopieren und das Kontingent des Kontos verbrauchen. Es hilft nicht, diesen Wert in eine Umgebungsvariable zur Build-Zeit zu verschieben, wenn der Build ihn in das an die Seite gelieferte JavaScript einfügt.
Ein auf dem Server verwahrtes Geheimnis verändert den Weg:
- Die Seite ruft ihre eigene Website unter
/api/weather?city=Limaauf. - Der Server dieser Website fügt das Geheimnis hinzu und kontaktiert den Wetteranbieter.
- Der Server gibt die Wetterdaten an die Seite zurück, ohne das Geheimnis in die Antwort aufzunehmen.
Der Server muss weiterhin den Zugriff auf seinen Endpunkt und dessen Nutzung kontrollieren. Dass das Geheimnis für den externen Dienst auf einem Server liegt, macht eine unbegrenzte öffentliche Nutzung nicht harmlos.
Manche Anbieter vergeben Browserschlüssel, die öffentlich sein sollen. Wende die Domainbeschränkungen des Anbieters an, begrenze die erlaubten APIs und setze Nutzungskontingente. Verwende einen Schlüssel nur dann auf diese Weise, wenn sein dokumentierter Zweck es erlaubt; ein geheimer Serverschlüssel wird durch eine Domainbeschränkung nicht zu einem Browserschlüssel.
Auf eine Offenlegung reagieren
Halte Dateien mit Anmeldeinformationen aus der Versionsverwaltung heraus, indem du zum Beispiel .env in .gitignore aufführst. Das verhindert, dass die Datei committet wird; es verhindert nicht, dass ein Frontend-Build einen Wert veröffentlicht, den der Code einbettet. Wurde ein Schlüssel offengelegt, widerrufe ihn und erstelle einen Ersatz. Das Entfernen aus dem neuesten Commit lässt die frühere Historie und alle Kopien bestehen.
Den Austausch schützen und Verschwendung verringern
Verwende HTTPS für API-Aufrufe, damit Daten und Anmeldeinformationen bei der Übertragung verschlüsselt sind. Ein fetch-Aufruf einer HTTPS-Seite an eine HTTP-API wird als gemischter Inhalt blockiert. Die Verschlüsselung hindert die Person am Browser nicht daran, ihre eigene Anfrage zu sehen.
Eine Ratenbegrenzung beschränkt die Nutzung, zum Beispiel auf 60 Anfragen pro Minute. Aufrufe über dieser Grenze können 429 Too Many Requests zurückgeben. Speichere geeignete Ergebnisse zwischen, nutze Debouncing bei Sucheingaben und vermeide doppelte Aufrufe. Liefert der Server Retry-After, halte die angegebene Wartezeit ein, statt wiederholt sofort neue Versuche zu starten.
Validierung auf der Clientseite macht Formulare leichter nutzbar, aber jeder kann einen Endpunkt ohne dein Formular aufrufen. Der Server muss Anfragen selbst validieren und Aktionen selbst autorisieren. Behandle Zeichenketten beim Anzeigen seiner Antwort weiterhin als nicht vertrauenswürdigen Text und füge sie mit textContent ein.
Wichtige Erkenntnisse
- An den Browser gelieferter Code und dessen Anfragen legen alle darin enthaltenen Anmeldeinformationen offen.
- Ein geheimer Schlüssel gehört auf den Server, der den Anbieter aufruft.
- Beschränke öffentliche Browserschlüssel nach Domain, API und Kontingent.
- Widerrufe einen offengelegten Schlüssel; ihn aus dem aktuellen Code zu löschen, reicht nicht aus.
- HTTPS verschlüsselt die Übertragung; der Server muss Anfragen weiterhin validieren und autorisieren.
4Speichere kleine Zustandsmengen im Browser
Eine Einstellung oder ein Entwurf kann ein Neuladen ohne neue Serveranfrage überstehen. Browserspeicher macht das einfach, aber seine Werte sind Zeichenketten und ihre Lebensdauer hängt vom gewählten Speicher ab. Du speicherst kleine Teile eines Zustands und stellst sie wieder her, prüfst alte Daten vor der Verwendung und gibst zwischengespeicherten API-Ergebnissen eine Ablaufzeit. Außerdem entscheidest du, welche Daten anderswohin gehören, denn lokaler Speicher ist weder ein Tresor für Geheimnisse noch der einzige sichere Aufbewahrungsort für wichtige Arbeit.
4.1 Speichere Zeichenketten und stelle ihre Typen wieder her
Web Storage bietet zwei Schlüssel-Wert-Speicher: localStorage und sessionStorage. Sie verwenden dieselben Methoden. Die Hauptentscheidung betrifft daher die Lebensdauer der Daten und die Tabs, die sie teilen sollen.
Die Lebensdauer wählen
| Speicher | Lebensdauer | Gemeinsame Nutzung |
|---|---|---|
localStorage | Bleibt nach dem Schließen von Tab und Browser erhalten, bis die Daten entfernt oder gelöscht werden | Wird von Seiten desselben Ursprungs geteilt |
sessionStorage | Bleibt beim Neuladen während der Seitensitzung des Tabs erhalten | Getrennt für jeden Tab und Ursprung |
Das Schließen des Tabs beendet dessen Sitzungsspeicher. Eine neu geöffnete Seite mit einem öffnenden Fenster kann mit einer Kopie des Sitzungsspeichers des öffnenden Fensters beginnen, spätere Änderungen bleiben aber getrennt. Verwende Sitzungsspeicher nicht, um Informationen vor Skripten geheim zu halten.
Speicher ist an Ursprünge gebunden. https://app.example.com kann seinen Speicher nicht mit http://app.example.com teilen. Pfade desselben Ursprungs teilen ihn hingegen. Ein Präfix wie "notes-app:settings" hilft daher, die Schlüssel mehrerer Anwendungen auseinanderzuhalten.
Die Speichermethoden verwenden
localStorage.setItem("notes-app:theme", "dark");
const theme = localStorage.getItem("notes-app:theme");
localStorage.removeItem("notes-app:theme");
setItem erstellt oder überschreibt einen Eintrag. getItem gibt seinen Zeichenkettenwert oder null zurück, falls er fehlt. removeItem entfernt einen Eintrag. clear() entfernt alle Einträge aus diesem Speicherbereich des Ursprungs, was andere Funktionen derselben Website betreffen kann. Mit length und key(index) kannst du die Schlüssel untersuchen.
Verwende diese Methoden statt Eigenschaftszuweisungen wie localStorage.theme = "dark". Schlüssel wie "length" oder "key" können beim Zugriff als Eigenschaften mit eingebauten Mitgliedern kollidieren.
Werte gezielt umwandeln
Nach localStorage.setItem("count", 5) ergibt das Lesen "5". Das direkte Addieren von 1 erzeugt "51". Number(localStorage.getItem("count")) + 1 ergibt 6.
Ein gespeicherter boolescher Wert birgt eine ähnliche Falle. setItem("darkMode", false) speichert "false", und eine nicht leere Zeichenkette ist ein Truthy-Wert. Lies mit localStorage.getItem("darkMode") === "true", wenn dies die beiden erwarteten gespeicherten Zeichenketten sind.
Objekte und Arrays benötigen JSON:
localStorage.setItem("settings", JSON.stringify({
theme: "dark",
fontSize: 18,
}));
const settings = JSON.parse(localStorage.getItem("settings"))
?? { theme: "light", fontSize: 16 };
Ohne Serialisierung wird ein gewöhnliches Objekt zu "[object Object]". JSON.parse(null) ergibt null, sodass der Standardwert einen fehlenden Eintrag abdeckt. Eine fehlerhaft aufgebaute gespeicherte Zeichenkette benötigt weiterhin try/catch, und geparste Daten benötigen vor der Verwendung eine Strukturprüfung.
Die Arbeitslast klein halten
Aufrufe von Web Storage sind synchron: JavaScript wartet im Hauptthread auf jeden einzelnen. Verwende ihn für Einstellungen, einen Entwurf oder eine kurze Liste, nicht für große Sammlungen oder Dateien. IndexedDB bietet asynchronen Speicher für größere strukturierte Datenmengen.
Aktuelle Browser erlauben üblicherweise etwa 5 MiB in jedem Web-Storage-Bereich pro Ursprung. Ein voller Speicher kann dazu führen, dass setItem einen QuotaExceededError auslöst; auch kann der Zugriff aufgrund der Datenschutzeinstellungen des Browsers fehlschlagen. Behandle Fehler so, dass der Rest der Anwendung weiter funktioniert. Die Entwicklerwerkzeuge zeigen die Speicher im Tab „Application“ von Chrome und Edge oder im Tab „Storage“ von Firefox an. Das Löschen von Einträgen hilft dort, erste Besuche zu testen.
Wichtige Erkenntnisse
- Wähle
localStoragefür dauerhaft gemeinsame Einstellungen undsessionStoragefür den Zustand einer Tab-Sitzung. - Speicherbereiche sind nach Ursprung getrennt.
getItemgibt eine Zeichenkette odernullzurück.- Serialisiere Objekte mit JSON und stelle ihre Typen beim Lesen wieder her.
- Behandle Speicherfehler und halte synchrone Schreibvorgänge klein.
4.2 Stelle Zustände wieder her und lass zwischengespeicherte Daten ablaufen
Eine Anwendung lädt normalerweise beim Start und speichert bei Änderungen. Lies eine gespeicherte Liste, wenn die Seite geöffnet wird, rendere sie und speichere die aktualisierte Liste nach einer Bearbeitung. Der Browserspeicher ist eine praktische Kopie: Sie kann fehlen, beschädigt, veraltet oder gelöscht sein.
Vor dem Wiederherstellen validieren
Diese Ladefunktion erwartet ein gespeichertes Array aus Zeichenketten:
function loadTodos() {
try {
const value = JSON.parse(localStorage.getItem("todos"));
if (Array.isArray(value) && value.every(item => typeof item === "string")) {
return value;
}
} catch {
// Continue with an empty list if reading or parsing fails.
}
return [];
}
JSON.parse allein beweist nicht, dass der Wert ein Array ist oder dass seine Einträge zur Anwendung passen. Gültiges JSON wie 42 hat trotzdem die falsche Struktur. Ein mit einer Version versehener Wert wie { version: 2, todos: [...] } lässt neueren Code ältere Formate erkennen und sie entweder umwandeln oder einen Standardwert wiederherstellen.
Speichere die im Arbeitsspeicher gehaltene Liste nach jeder Änderung mit localStorage.setItem("todos", JSON.stringify(todos)). Fange auch Schreibfehler ab; eine Seite sollte weiter funktionieren können, selbst wenn sie den Zustand für das nächste Neuladen nicht sichern kann. Ein Formularentwurf, der nur zu einem Besuch gehört, kann sessionStorage verwenden.
Zwischengespeicherten Antworten einen Zeitstempel geben
localStorage.setItem("weather", JSON.stringify({
savedAt: Date.now(),
data,
}));
Prüfe bei einem späteren Laden zuerst die Struktur und den Zeitstempel des zwischengespeicherten Objekts. Sein Alter ist Date.now() - savedAt. Verwende es nur, solange dieses Alter kleiner als 10 * 60 * 1000 beziehungsweise 600.000 ms ist. Ein um 10:00:00 Uhr gespeicherter Wert ist um 10:07:30 Uhr 450.000 ms alt und kann verwendet werden. Um 10:12:00 Uhr ist er 720.000 ms alt; rufe daher einen frischen Wert ab. Bei genau 600.000 ms ist er nach dieser Regel abgelaufen.
Der Ablauf wird durch die Anwendungslogik bestimmt: localStorage löscht einen Eintrag nicht, wenn dein Aktualitätsintervall endet. Weise einen fehlenden oder ungültigen Zeitstempel zurück, statt ein unbekanntes Alter als aktuell zu behandeln. Du kannst veraltete Daten bei einer fehlgeschlagenen Aktualisierung sichtbar lassen, wenn sie als alt gekennzeichnet sind.
Andere Tabs synchronisieren
Wenn ein Tab localStorage ändert, erhalten andere Tabs desselben Ursprungs ein storage-Ereignis. Der schreibende Tab erhält sein eigenes Ereignis nicht. Das Ereignis stellt key, oldValue, newValue und url bereit.
window.addEventListener("storage", (event) => {
if (event.key === "theme") applyTheme(event.newValue);
});
Wende das Design auch im schreibenden Tab sofort an. Behandle null, wenn ein Schlüssel entfernt wird. Ein storage-Ereignis ersetzt nicht die Aktualisierung des Zustands deiner eigenen Seite.
Entscheiden, was anderswohin gehört
Speichere keine Passwörter, Zahlungskartennummern oder geheimen API-Schlüssel in Web Storage. Skripte des Ursprungs können ihn lesen, einschließlich Codes von Drittanbietern und eingeschleuster Skripte. Lösche relevante personenbezogene Daten beim Abmelden auf gemeinsam genutzten Geräten. Bewahre wichtige Arbeit auf einem Server auf oder biete einen Export an, denn Nutzer können Websitedaten löschen und private Sitzungen verwerfen sie.
Cookies unterscheiden sich: Passende Anfragen senden sie an den Server, und HttpOnly-Cookies können von Seitenskripten nicht gelesen werden. Sie sind klein, jeweils etwa 4 KB. Web Storage bleibt im Browser und ist für Skripte lesbar. IndexedDB eignet sich über eine asynchrone API für größere strukturierte Datenmengen.
Wichtige Erkenntnisse
- Validiere geparste Daten und greife auf einen Standardwert zurück, wenn sie fehlen oder beschädigt sind.
- Die Aktualität eines Cache-Eintrags ergibt sich aus einem Zeitstempel und einer von der Anwendung festgelegten Altersgrenze.
- Das
storage-Ereignis erreicht andere Tabs desselben Ursprungs, nicht den schreibenden Tab. - Halte Geheimnisse und unersetzliche Daten aus Web Storage heraus.
5Nutze Standort- und Zwischenablagefunktionen
Browser-APIs verbinden eine Seite mit Funktionen wie Standortermittlung und Zwischenablage. Ihre Verfügbarkeit hängt vom Browser, dem Sicherheitskontext der Seite und den Entscheidungen des Nutzers ab. Du erkennst eine Funktion vor ihrem Aufruf, fragst zu einem geeigneten Zeitpunkt nach Berechtigung und behandelst Erfolg oder Verweigerung. Zu einer vollständigen Interaktion gehören eine sichtbare Rückmeldung und ein anderer Weg, die Aufgabe abzuschließen, etwa die Eingabe einer Stadt oder das Kopieren markierten Textes mit der Tastatur.
5.1 Fordere einen Standort mit Rückfalllösung an
Die Geolocation-API ist eine Browser-API: eine vom Browser bereitgestellte Funktion, die von der JavaScript-Sprache getrennt ist. Wie andere Gerätefunktionen kann sie nicht verfügbar sein oder verweigert werden. Eine Wetterseite sollte weiterhin funktionieren, wenn ein Leser lieber eine Stadt eingibt.
Funktion und Kontext prüfen
Erkenne die Funktion selbst mit "geolocation" in navigator. Den Namen eines Browsers zu prüfen, ist weniger zuverlässig: Versionen und Einstellungen ändern, was verfügbar ist. Die Geolocation-API benötigt einen sicheren Kontext, normalerweise HTTPS; http://localhost erfüllt diese Voraussetzung ebenfalls für die Entwicklung. window.isSecureContext gibt an, ob die aktuelle Seite die Voraussetzung erfüllt. Einfaches HTTP über die Netzwerkadresse eines anderen Geräts ist nicht dasselbe wie localhost.
Erkläre, warum der Standort nützlich ist, und fordere ihn an, wenn der Leser auf „Meinen Standort verwenden“ klickt. Der Browser verwaltet die Berechtigungsanfrage. Eine Person kann sie erlauben oder blockieren, und eine frühere Verweigerung kann dazu führen, dass ein späterer Aufruf ohne erneute Nachfrage fehlschlägt. Der Nutzer kann diese Entscheidung in den Website-Einstellungen ändern.
Das Callback-Ergebnis lesen
navigator.geolocation.getCurrentPosition(success, error, options) verwendet Callbacks. Bei Erfolg enthält position.coords Breiten- und Längengrad in Dezimalgrad und accuracy in Metern. Die Genauigkeit ist ein geschätzter Unsicherheitsradius und keine zusätzliche Dezimalgenauigkeit. Bei einem Fehler bedeutet Code 1, dass die Berechtigung verweigert wurde, 2, dass die Position nicht verfügbar ist, und 3, dass die Wartezeit abgelaufen ist.
Die Optionen steuern den Versuch:
enableHighAccuracy: fordert höhere Genauigkeit an; Standardwertfalse.timeout: Wartezeitbegrenzung in Millisekunden; StandardwertInfinity.maximumAge: Höchstalter einer noch akzeptablen zwischengespeicherten Position; Standardwert0.
Verwende einen endlichen Timeout, damit die Seite das Warten beenden kann. watchPosition beobachtet Aktualisierungen über die Zeit und gibt eine ID zurück; übergib diese ID an clearWatch, wenn die Verfolgung enden soll.
Callbacks bei Verwendung von await einhüllen
function getPosition(options) {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
}
async function useLocation() {
if (!("geolocation" in navigator)) {
showCityInput();
return;
}
try {
const { coords } = await getPosition({ timeout: 10000 });
const url = new URL("https://api.example.com/weather");
url.searchParams.set("lat", coords.latitude.toFixed(2));
url.searchParams.set("lon", coords.longitude.toFixed(2));
await loadWeather(url);
} catch {
showCityInput();
}
}
Hier zeigt showCityInput das Bedienelement für die manuelle Eingabe an und loadWeather fordert Wetterdaten an und rendert sie. Verbinde useLocation mit der Standortschaltfläche. Der catch-Block deckt Fehler bei der Standortermittlung und der Wetteranfrage ab; in beiden Fällen bleibt die manuelle Eingabe verfügbar.
Nur nützliche Genauigkeit anfordern
Eine Position mit dem Breitengrad 52.3676, dem Längengrad 4.9041 und der Genauigkeit 35 hat einen geschätzten Radius von etwa 35 m. Das Runden auf 52.37 und 4.90 verringert die Genauigkeit, die an den Wetterdienst gesendet wird. Ein Breitengradintervall von 0,01° entspricht etwa 1,1 km; der Abstand zwischen Längengraden variiert mit dem Breitengrad. Diese regionale Genauigkeit eignet sich für eine Vorhersage, ohne die detailliertesten Koordinaten weiterzugeben.
Wo die Permissions-API verfügbar ist, meldet navigator.permissions.query({ name: "geolocation" }) den Wert "granted", "denied" oder "prompt". Erkenne und behandle auch diese API. Ihr Zustand hilft, die Oberfläche anzupassen, ersetzt aber nicht die Fehlerbehandlung für den Standortaufruf.
Wichtige Erkenntnisse
- Erkenne die Geolocation-API und verwende einen sicheren Kontext.
- Fordere den Standort nach einer eindeutigen Nutzeraktion mit angegebenem Zweck an.
- Breiten- und Längengrad verwenden Grad; die Genauigkeit verwendet Meter.
- Setze einen endlichen Timeout und biete bei einem Fehler die manuelle Eingabe an.
- Sende den Standort nur mit der Genauigkeit, die die Funktion benötigt.
- Der Berechtigungszustand garantiert keine nutzbare Position.
5.2 Kopiere Text und lerne eine weitere Browser-API kennen
Die Clipboard-API ermöglicht es einer Seite, Text zu kopieren, ohne den Leser zu bitten, ihn zuerst zu markieren. Wie die Geolocation-API benötigt sie eine Prüfung der Verfügbarkeit, eine passende Nutzeraktion, Fehlerbehandlung und eine Rückfalllösung.
Beim Klick kopieren
navigator.clipboard.writeText(text) gibt ein Promise zurück. Warte darauf, bevor du Erfolg meldest. Ein sicherer Kontext ist erforderlich, und eine verweigerte Operation kann mit NotAllowedError abgelehnt werden. Rufe sie direkt aus einem Klick-Handler auf: Firefox und Safari verlangen eine kürzlich erfolgte Nutzerinteraktion, während die Anforderungen von Chromium auch von der Berechtigung zum Schreiben in die Zwischenablage abhängen können.
copyButton.addEventListener("click", async () => {
try {
if (!navigator.clipboard?.writeText) {
throw new Error("Copy unavailable");
}
await navigator.clipboard.writeText(codeField.value);
copyButton.textContent = "Copied!";
} catch {
codeField.focus();
codeField.select();
copyButton.textContent = "Copy selected text: Ctrl+C / Cmd+C";
}
setTimeout(() => {
copyButton.textContent = "Copy";
}, 2000);
});
codeField ist ein Texteingabefeld oder ein mehrzeiliges Textfeld und copyButton ist die Schaltfläche der Seite. Schlägt das Kopieren fehl, bietet das Markieren des Textes einen Weg über die Tastatur: Strg+C auf vielen Systemen oder Cmd+C unter macOS. Nach 2000 ms wechselt die Beschriftung zurück zu „Copy“. Zeige „Copied!“ nie an, bevor das Promise erfüllt ist.
Zum Lesen dient navigator.clipboard.readText(); dafür gelten strengere Regeln, weil eine Zwischenablage private Inhalte aus anderen Anwendungen enthalten kann. Ein Browser kann eine Berechtigung verlangen oder eine Einfügeaufforderung anzeigen. Die Berechtigung zum Schreiben bedeutet keine Berechtigung zum Lesen.
Den Verbindungsstatus als Hinweis behandeln
navigator.onLine und die Ereignisse online und offline des Fensters helfen dabei, einen Verbindungshinweis anzuzeigen. Sie belegen nicht, ob deine API erreichbar ist. Ein Gerät kann eine Netzwerkverbindung haben, während der API-Server ausgefallen oder nicht erreichbar ist.
window.addEventListener("offline", () => {
status.textContent = "You are offline.";
});
Behandle Fehler bei fetch weiterhin, auch wenn navigator.onLine wahr ist. Verwende für die Statusmeldung eine geeignete Live-Region.
Eine teilbare Suche lesen
Die URL-API liest nicht nur API-URLs ein, sondern auch die Abfragezeichenkette der Seite. Auf https://books.example.org/search?q=dune gibt new URL(location.href).searchParams.get("q") den Wert "dune" zurück. Lies diesen Wert beim Laden, um eine Suche aus einem geteilten Link wiederherzustellen. Ein fehlender Parameter ergibt null; entscheide daher, was der Startbildschirm in diesem Fall anzeigen soll.
Eine Lernmethode verwenden
Lies für eine weitere Browser-API ihre Syntax, Regeln für sicheren Kontext und Berechtigungen, Kompatibilitätstabelle und den Baseline-Status auf MDN. Erkenne dann die Funktion, rufe sie bei Bedarf aus einer Nutzeraktion auf, behandle ihr Promise oder ihre Callbacks und biete eine Alternative, wenn sie fehlt oder verweigert wird.
Die Notifications-API zeigt nach einer Berechtigung Systembenachrichtigungen an; Web Share öffnet, wo unterstützt, ein Teilen-Menü des Geräts; Canvas zeichnet Grafiken; Medien-APIs steuern Audio und Video. Eine Karten-API von Drittanbietern kann eine Bibliothek und einen Anbieterschlüssel einbinden. Für ihre Anmeldeinformationen gelten dieselben Regeln für geheime Schlüssel wie für jeden anderen Dienst. Ein Test in einem Browser über localhost belegt nicht, dass ein anderes Gerät oder ein anderer Browser dieselbe Unterstützung und denselben Kontext hat.
Wichtige Erkenntnisse
- Warte auf
writeText, bevor du erfolgreiches Kopieren meldest. - Halte eine Rückfalllösung zum Kopieren per Tastatur bereit, wenn der Zugriff auf die Zwischenablage fehlschlägt.
- Für das Lesen der Zwischenablage gelten eigene, strengere Berechtigungsregeln.
- Der Online-Status belegt nicht die Erreichbarkeit einer API.
- Verwende URL-Parameter, um eine teilbare Suche wiederherzustellen.
- Prüfe für jede Browser-API die Kompatibilität, erkenne ihre Funktionen und behandle Verweigerungen.
Daten abrufen und Browser-APIs: Beispiele
1Erstelle und aktualisiere eine Leseliste
14 Schritte
- 1EinführungVerfolge einen Austausch
Eine Seite mit einer Leseliste soll Bücher laden und eine Notiz zu einem davon hinzufügen. Die Seite ist der Client; die API ist der Server. Jeder Austausch besteht aus einer Anfrage, die die Aktion beschreibt, und einer Antwort, die ihr Ergebnis beschreibt.
Wir lesen zuerst den Austausch, verbinden dann die beiden asynchronen Schritte beim Laden von JSON und übermitteln schließlich eine Änderung. Die Antwort des Servers bestimmt, was die Seite als Nächstes sicher anzeigen kann.
- 2BeispielLies die Ressource und die Filter
Die Seite fordert
GET https://api.example.com/books?author=Le%20Guin&limit=5an.GETfordert das Lesen an./booksbezeichnet die Ressource. Die Abfrage filtert nach Autor und begrenzt die Anzahl der Ergebnisse.%20steht für das Leerzeichen in „Le Guin“. Die API-Dokumentation legt diese Parameternamen fest; die Seite kann nicht einen anderen Namen wählen und erwarten, dass der Dienst ihn versteht. - 3KernaussageUnterscheide Status und Verbindung
Eine Antwort kann
200 OKmit einem Buch oder404 Not Foundmit einem Fehler melden. Beide belegen, dass ein Server geantwortet hat. Ein Verbindungsfehler liefert der Seite keinen HTTP-Status.Untersuche im Netzwerk-Tab die vollständige URL, Methode, Status, Header und Körper. Hat die API das erwartete JSON zurückgegeben, solltest du als Nächstes den Code untersuchen, der es liest oder anzeigt.
- 4HerausforderungWähle eine Methode für eine Änderung
Eine Notiz existiert bereits. Die Seite soll nur ihr Feld
doneändern und ihren Text erhalten. Welche HTTP-Methode passt zu dieser Operation und warum wäreGETfalsch?---reveal---
Verwende
PATCHfür die teilweise Aktualisierung.GETist eine sichere Lesemethode und darf nicht verwendet werden, um eine Datenänderung anzufordern.PUTwürde einen vollständigen Ersatz beschreiben und könnte Felder zurücksetzen, die im Anfragekörper fehlen. - 5ErklärungWarte auf die Antwort
Beginne mit
const response = await fetch(url). Das wartet, bis die Antwortheader eintreffen, und liefert einResponse-Objekt, nicht die geparsten Bücher.Die Antwort kann trotzdem einen HTTP-Fehler melden.
fetchwird bei einem404erfüllt, weil eine Antwort vorliegt. Prüferesponse.ok, bevor du die Operation als erfolgreich behandelst; der Wert ist für Statuscodes von200bis299wahr. - 6BeispielPrüfe und lies den Antwortkörper
Fahre innerhalb der async-Funktion fort:
if (!response.ok) { throw new Error(`HTTP error ${response.status}`); } const data = await response.json();Der zweite Wartevorgang liest und parst den Antwortkörper. Nun ist
dataein JavaScript-Wert. Ein äußerestry/catchkann sowohl den hier ausgelösten Statusfehler als auch Fehler beim Abrufen oder Parsen des Körpers behandeln. - 7BeispielFinde das verschachtelte Array
Angenommen, der Antwortkörper lautet
{"results":[{"title":"Dune"}],"total":1}.Der erste Schritt,
data.results, erreicht das Array.[0]erreicht dessen erstes Objekt..titleergibt"Dune". Eine Renderfunktion solltedata.resultserhalten, nicht das gesamte äußere Objekt. Die Antwortvorschau im Netzwerk-Tab ist nützlich, wenn sich die Struktur der API von der Erwartung der Seite unterscheidet. - 8HerausforderungDiagnostiziere den fehlenden Wert
Der Code verwendet
const data = response.json()und versucht danndata.results[0]. Der Antwortkörper ist tatsächlich gültiges JSON mit einemresults-Array. Was fehlt?---reveal---
Warte auf den Antwortkörper:
const data = await response.json(). Ohne dieses Warten istdataein Promise und hat keine Eigenschaftresults. Das ersteawait fetch(...)hat nicht auch auf das Parsen von JSON gewartet. - 9TippErhalte Satzzeichen in einer Suche
Erstelle Abfrageparameter über die URL-API:
const url = new URL("https://api.example.com/search"); url.searchParams.set("q", "rock & roll");Der Wert wird zu
rock+%26+roll. Das kaufmännische Und bleibt innerhalb des Suchbegriffs, statt die Abfrage zu teilen. Verwende nach dem Laden die geparste Antwort erneut: Eine zweite Methode zum Lesen des Körpers für dieselbe Antwort aufzurufen, schlägt fehl, weil der Körper bereits gelesen wurde. - 10BeispielBereite das Übermitteln von JSON vor
Um eine Notiz zu erstellen, serialisiere die Felder und gib ihr Format an:
const response = await fetch("https://api.example.com/notes", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: "Read", done: false }), });Die Methode fordert die Erstellung an, der Header gibt JSON an und der Anfragekörper enthält JSON-Text statt eines gewöhnlichen Objekts.
- 11VerbindungNutze den gespeicherten Datensatz
Der Server kann
201 Createdmit{"id":31,"title":"Read","done":false}zurückgeben. Füge diesen zurückgegebenen Datensatz zum Zustand der Seite hinzu. Seine ID ermöglicht es einer späteren Aktion,/notes/31aufzurufen.Übergib bei einem Formular mit Dateien
FormDatastatt JSON und lass den Browser das Multipart-Trennzeichen setzen. Eine Anfrage mitGETkann überfetchkeinen Anfragekörper übertragen; Suchfelder gehören in die Abfragezeichenkette. - 12HerausforderungBehandle einen Erfolg ohne Antwortkörper
Das Löschen einer gespeicherten Notiz gibt
204zurück. Der Code prüftresponse.ok, ruft dannresponse.json()auf und zeigt einen Fehler an. Ist das Löschen zwangsläufig fehlgeschlagen und was sollte sich ändern?---reveal---
Nein:
204meldet einen erfolgreichen Abschluss ohne Antwortkörper. Überspringe bei diesem Status das Parsen von JSON und aktualisiere die Seite nach der Statusprüfung. Der Parserfehler entstand durch den Versuch, leeren Inhalt zu lesen; er ist kein Beleg dafür, dass das Löschen fehlgeschlagen ist. - 13HerausforderungPrüfe einen vollständigen Ablauf zum Absenden
Eine Speichern-Schaltfläche kann zweimal angeklickt werden, während ein
POSTläuft. Eine erfolgreiche Antwort liefert eine neue ID. Nenne die beiden Verhaltensweisen der Seite, die ein versehentliches Duplikat verhindern und diese ID erhalten.---reveal---
Deaktiviere die Schaltfläche während der Anfrage und aktiviere sie in
finallywieder. Aktualisiere bei Erfolg anhand des zurückgegebenen Datensatzes einschließlich seiner ID. Die Schaltfläche verhindert wiederholte Klicks; das Serverergebnis liefert Werte, die in den übermittelten Daten nicht vorhanden waren. - 14ZusammenfassungPrüfe den Austausch an jeder Schnittstelle
Wenn eine Anfrage nicht wie erwartet funktioniert, untersuche die Operation der Reihe nach: die dokumentierte URL und Methode, den tatsächlichen Status, die gewählte Methode zum Lesen des Körpers und die Struktur der Daten, die an die Seite übergeben werden.
Prüfe bei einem Schreibvorgang auch den Inhaltstyp und den serialisierten Anfragekörper. Ein Erfolg ohne Inhalt benötigt keine Methode zum Lesen von JSON. Die Antwort auf eine Erstellung sollte den gespeicherten Datensatz liefern, der für die nächste Bearbeitung oder Löschung verwendet wird.
2Erstelle eine Suchseite, die aktuell bleibt
18 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungHalte die Anzeige mit der Suche abgestimmt
- 2BeispielErsetze die vorherige Liste
- 3KernaussageUnterscheide null von fehlenden Daten
- 4HerausforderungBehandle externen Text und Links
- 5BeispielLies die Zeiteinheit
- 6ErklärungWähle die Zeitzone für die Anzeige
- 7BeispielFormatiere dokumentierte Währungsuntereinheiten
- 8HerausforderungTrenne Gebietsschema und Wert
- 9ErklärungBeginne mit einem sichtbaren Wartezustand
- 10BeispielSchließe alle Wege ab
- 11HerausforderungErkenne eine erfolgreiche Suche ohne Treffer
- 12TippWarte vor einem erneuten Versuch bei einem vorübergehenden Fehler
- 13BeispielBeachte die Reihenfolge der Abschlüsse
- 14BeispielBrich eine veraltete Anfrage ab
- 15VerbindungVerringere Aufrufe während der Eingabe
- 16HerausforderungWähle Teilerfolg
- 17HerausforderungSchütze die gesamte aktuelle Interaktion
- 18ZusammenfassungTeste die Oberfläche mit unvollkommenen Antworten
3Diagnostiziere einen API-Aufruf an einen anderen Ursprung
10 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungTrenne zwei Berechtigungsfragen
- 2BeispielVergleiche die vollständigen Ursprünge
- 3ErklärungLies die Erlaubnis des Servers
- 4BeispielVerfolge zuerst die Preflight-Anfrage
- 5HerausforderungErkläre einen Erfolg nur im Terminal
- 6BeispielFinde den offengelegten Schlüssel
- 7VerbindungVerschiebe die Grenze des Geheimnisses
- 8HerausforderungUnterscheide Übertragung und Geheimhaltung
- 9HerausforderungPrüfe Cookies und lesbare Antworten
- 10ZusammenfassungDiagnostiziere die fehlgeschlagene Grenze
4Stelle eine gespeicherte Liste wieder her, ohne die Seite zu beschädigen
12 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungHalte eine praktische lokale Kopie bereit
- 2ErklärungWähle, was erhalten bleiben soll
- 3BeispielBewahre ein Array als Text auf
- 4HerausforderungStelle den beabsichtigten Typ wieder her
- 5TippBehandle einen Schreibvorgang ohne ausreichenden Platz
- 6BeispielPrüfe mehr als die JSON-Syntax
- 7BeispielFüge eine Cache-Zeit hinzu
- 8HerausforderungBerechne, welcher Cache-Eintrag aktuell ist
- 9VerbindungVerfolge ein Design über Tabs hinweg
- 10KernaussageSchütze wichtige Daten
- 11HerausforderungBehandle ein altes gespeichertes Format
- 12ZusammenfassungTeste einen ersten und einen erneuten Besuch
5Ergänze nützliche Standort- und Kopierschaltflächen
13 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungMache die Aktion verständlich
- 2ErklärungPrüfe Kontext und Verfügbarkeit
- 3TippFrage, wenn die Funktion nützlich ist
- 4BeispielSetze eine endliche Wartezeit
- 5BeispielLies nur eine nützliche Genauigkeit
- 6HerausforderungReagiere auf eine Verweigerung
- 7BeispielSchreibe beim Klick
- 8KernaussageWarte, bevor du das Kopieren als erfolgreich meldest
- 9HerausforderungBehandle ein Verbindungssignal als Hinweis
- 10VerbindungStelle eine Suche aus ihrem Link wieder her
- 11TippPrüfe eine weitere Browserfunktion
- 12HerausforderungPrüfe beide Schaltflächen zusammen
- 13ZusammenfassungTeste auch die Entscheidungen der Person
Daten abrufen und Browser-APIs: Übungsaufgaben
Set 1: Anfragen, Ergebnisse und Browserfunktionen10 Übungsfragen
Frage 1 von 10
403. Was teilt dieser Status der Seite mit?Antwort zeigenAntwort ausblenden
403 bedeutet, dass der Server die Anfrage verstanden hat und ihre Ausführung verweigert. Sie belegt weder, dass der Nutzer gültige Anmeldeinformationen angegeben hat, noch, dass eine Anmeldung die Verweigerung behebt. Bei einer fehlgeschlagenen Netzwerkverbindung gäbe es keinen HTTP-Status, den die Seite lesen könnte.Frage 2 von 10
200 mit {"items":[{"name":"Cedar"}]} zurück. Welche Zuweisung speichert nach const response = await fetch(url) den Namen des ersten Eintrags?Antwort zeigenAntwort ausblenden
json() den Antwortkörper liest und parst, und folge dann der äußeren Eigenschaft items zum ersten Objekt des Arrays. Ein Aufruf von json() ohne await hinterlässt ein Promise, das kein Array items hat. Das Lesen mit text() ergibt stattdessen eine Zeichenkette und nicht das für den Eigenschaftszugriff benötigte geparste Objekt.Frage 3 von 10
body beim Senden von { label: "Read" } erhalten?Antwort zeigenAntwort ausblenden
JSON.stringify wandelt das Objekt in gültigen JSON-Text für den Anfragekörper um. Die Umwandlung eines gewöhnlichen Objekts mit String oder toString() erzeugt "[object Object]", was der JSON-Endpunkt nicht parsen kann. Die Anfrage benötigt außerdem die dokumentierte Methode und einen Header für den JSON-Inhaltstyp.Frage 4 von 10
{ remaining: 0, owner: null } zurück. Wie kann die Seite die verbleibende Anzahl anzeigen und zugleich "Unknown" für den fehlenden Namen des Besitzers verwenden?Antwort zeigenAntwort ausblenden
name aus null zu lesen. Wird || für die Anzahl verwendet, ersetzt es null, weil null ein Falsy-Wert ist. Eine Rückfalllösung nach data.owner.name kann nicht helfen, wenn der Zugriff auf name bereits einen Fehler ausgelöst hat.Frage 5 von 10
amount: 3785, currency: "USD" als ganzzahlige Währungsuntereinheiten. Welche Zahl sollte an den Währungsformatierer übergeben werden?Antwort zeigenAntwort ausblenden
3785 direkt übergeben, wäre der angezeigte Betrag 100-mal zu groß. Intl.NumberFormat formatiert die übergebene Zahl; der Formatierer erkennt nicht selbst, dass ein API-Betrag in Cent angegeben ist.Frage 6 von 10
Antwort zeigenAntwort ausblenden
finally wird nach der geschützten Arbeit ausgeführt, unabhängig davon, ob sie erfolgreich ist oder einen Fehler auslöst. Dort gehört daher das Aufräumen hin, das auf beiden Wegen nötig ist. Ein Zurücksetzen nur in catch erfasst den Erfolg nicht; nach dem Rendern erfasst es den Fehler nicht. Ein Timer würde Bedienelemente zurücksetzen, ohne zu wissen, ob die Anfrage abgeschlossen ist.Frage 7 von 10
https://studio.example.org/edit. Was ist der Ursprung dieser Seite?Antwort zeigenAntwort ausblenden
/edit bezeichnet eine Ressource innerhalb des Ursprungs und ist nicht Teil des Ursprungs selbst. Ein Wechsel des Schemas zu HTTP würde selbst bei gleichem Host einen anderen Ursprung ergeben.Frage 8 von 10
Antwort zeigenAntwort ausblenden
Frage 9 von 10
localStorage.setItem("visits", 8) der Ausdruck localStorage.getItem("visits") + 1?Antwort zeigenAntwort ausblenden
"8" zurück, sodass + 1 eine Konkatenation ausführt und "81" ergibt. Der numerische Typ, der an setItem übergeben wurde, wird nicht wiederhergestellt. Wandle die gespeicherte Zeichenkette vor dem Addieren mit Number um, wenn die Zahl 9 das beabsichtigte Ergebnis ist.Frage 10 von 10
1 fehl. Welche Antwort an den Nutzer passt zu diesem Fehler?Antwort zeigenAntwort ausblenden
1 bedeutet, dass die Berechtigung verweigert wurde. Biete daher eine andere Möglichkeit zur Eingabe des Standorts an. Eine abgelaufene Wartezeit hat den Code 3 und erklärt diesen Fehler nicht. Wiederholte Anfragen können keine Berechtigung erzwingen; bei einer gespeicherten Verweigerung muss der Nutzer möglicherweise die Website-Einstellungen des Browsers ändern.Set 2: Verfolge Daten durch die Oberfläche10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
fetch-Aufruf gibt ein Response-Objekt mit dem Status 502 zurück. Der Code hat einen catch-Handler, prüft aber nie response.ok. Warum erscheint seine HTTP-Fehlermeldung möglicherweise nie?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
204 zurück. Was sollte die Seite tun, bevor sie versucht, JSON zu parsen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Promise.all ohne zusätzlichen Aufwand dauern?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
POST mit Content-Type: application/json. Es ist keine Preflight-Erlaubnis zwischengespeichert. Was muss der Browser prüfen, bevor er dieses POST sendet?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
http://localhost, aber ihre Standortfunktion fehlt, wenn sie auf einem Telefon unter einer HTTP-Netzwerkadresse geöffnet wird. Außerdem funktioniert die Anwendung nicht mehr, wenn Desktopnutzer den Standortzugriff verweigern. Welches Vorgehen behebt beide Probleme?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
navigator.clipboard.writeText(text). Wann sollte er „Copied!“ anzeigen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 3: Untersuche Fehler bei Anfragen, Speicher und Berechtigungen10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
PUT /profiles/29 als Ersetzen des Profils. Was bedeutet Idempotenz, wenn dieselbe vollständige Anfrage zweimal gesendet wird?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
new Date und zeigt Januar 1970 an. Welche Umwandlung sollte vor der Datumsformatierung erfolgen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
503 mit Retry-After: 12 zurückgegeben hat. Welches Verhalten vermittelt den Zustand und ermöglicht eine erneute Aktualisierung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
if (localStorage.getItem("compact")) wiederhergestellt. Der gespeicherte Text lautet "false", trotzdem wird der kompakte Modus eingeschaltet. Warum?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
-12.0464, den Längengrad -77.0428 und die Genauigkeit 80. Was beschreibt 80?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
https://recipes.example.org/search?q=green+tea liest new URL(location.href).searchParams.get("q"). Welchen Wert erhält sie?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 4: Entwirf zuverlässige API-Funktionen10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
429. Was bedeutet dieser Status?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
<b>-Text, einen javascript:-Link und remaining: 0. Die Seite muss den Titel wörtlich anzeigen, nur gewöhnliche Weblinks erlauben und die Anzahl erhalten. Welcher Renderplan erfüllt alle drei Anforderungen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Intl.DateTimeFormat und dem festen Gebietsschema en-US, aber ohne Option timeZone. Ihre angezeigten Uhrzeiten unterscheiden sich. Was erklärt den Unterschied?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
PATCH an einen anderen Ursprung verwendet credentials: "include". Die Preflight-Anfrage erlaubt es. Die abschließende Antwort hat einen Allow-Origin-Platzhalter und erlaubt Anmeldeinformationen, sodass JavaScript sie nicht lesen kann. Der Server nimmt außerdem an, dass CORS Nutzerprüfungen überflüssig macht. Welche Antwort- und Zugriffsregel behebt diesen Entwurf?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
.map auf. Bei manchen Nutzern liegt gültiges JSON {"version":1,"items":[]} aus älterem Code vor, bei anderen beschädigter Text. Welcher Wiederherstellungsplan behandelt beide Fälle?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
navigator.onLine === true, aber ein API-Aufruf wird abgelehnt. Was sollte die Seite über die Verbindung schließen und als Nächstes behandeln?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe
Schlüsselbegriffe
A
Abfrageparameter
?, den ein Endpunkt für Eingaben wie Suchbegriffe oder Seitenbegrenzungen verwendet.limit=12 fordert den Suchendpunkt auf, höchstens zwölf Ergebnisse zurückzugeben.Ablaufzeit
AbortController
AbortSignal
AbortSignal.timeout(4000) begrenzt eine Anfrage auf vier Sekunden aktive Zeit.Anfragekörper
Antwortkörper
API-Schlüssel
B
Baseline
Berechtigung
C
Cache
Clipboard-API
CORS
Cross-Site-Scripting
textContent für API-Titel verhindert die HTML-Interpretation, die Cross-Site-Scripting ermöglichen kann.D
Debouncing
E
Endpunkt
POST, um eine Notiz zu erstellen, und gibt die Kennung des neuen Datensatzes zurück.Exponentielles Backoff
F
Feature-Erkennung
"geolocation" in navigator entscheidet, ob die Seite den Gerätestandort anbieten kann.FormData
G
Gebietsschema
en-US schreibt ein Datum anders als en-GB, selbst wenn beide denselben Zeitpunkt formatieren.Gemischte Inhalte
Geolocation-API
H
Header
Content-Type-Header teilt dem empfangenden Programm mit, dass der zugehörige Körper JSON enthält.HTTP-Anfrage
/books an und enthält einen Abfrageparameter, der die Anzahl der Ergebnisse begrenzt.HTTP-Antwort
404 und erklärt in ihrem Körper, dass das angeforderte Buch nicht gefunden wurde.HttpOnly
I
Idempotenz
IndexedDB
Intl.NumberFormat
Intl.NumberFormat für jeden Preis erneut, statt für jeden Eintrag eine Währungszeichenkette neu zu erstellen.J
JSON
results, bevor sie Listeneinträge erstellt.L
Ladezustand
Leerzustand
Live-Region
role="status" sagt „Laden …“ und die spätere Meldung zum Suchergebnis an.localStorage
M
Multipart-Trennzeichen
N
Nullish-Coalescing-Operator
?? nur dann einen Ersatzwert, wenn sein linker Wert null oder undefined ist, und erhält gültige Nullen.Nutzeraktivierung
O
Opake Antwort
Optionale Verkettung
?. null oder undefined ist, und gibt undefined zurück.book.author?.name ein Buch behandeln, dessen Autorenobjekt in der Antwort fehlt.P
Paginierung
Parserfehler
204 verursacht einen JSON-Parserfehler, obwohl der Server die Operation erfolgreich abgeschlossen hat.Preflight-Anfrage
Promise.all
Promise.allSettled
R
Ratenbegrenzung
Rendern
Response-Objekt
200, aber die Seite wartet weiterhin auf json(), um die Buchdaten zu erhalten.Retry-After
Retry-After-Wert von 15 fordert die Seite auf, vor einer weiteren Anfrage fünfzehn Sekunden zu warten.Rückfalllösung
S
Same-Origin-Policy
Serialisierung
JSON.stringify erhält die Felder einer Notiz, wenn sie als JSON-Anfragekörper gesendet werden.sessionStorage
Sicherer Kontext
Speicherkontingent
Standortgenauigkeit
Statuscode
201 teilt der Seite mit, dass der Server als Antwort auf die Anfrage eine Ressource erstellt hat.storage-Ereignis
Synchrone Operation
T
textContent
textContent zeigt seine spitzen Klammern wörtlich an, statt ein HTML-Element zu erstellen.U
Unix-Zeitstempel
URL-Kodierung
Ursprung
UTC
Z dargestellt.W
Währungsuntereinheit
4680 einem Betrag von 46.80 US-Dollar.Web Storage
Web-API
Wettlaufsituation
Z
Zeitzone
Zugriffstoken
Über diese Einheit
Erstelle deine eigenen Lernmaterialien
Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.