Python-Kurs / HTTP-APIs aufrufen und JSON-Antworten prüfen
Eine HTTP-API nimmt Anfragen entgegen und liefert einen Status sowie Antwortdaten. Prüfen Sie in einem Python-Client getrennt, ob die Anfrage funktioniert hat, wie lange einzelne Netzwerkoperationen warten dürfen, wie groß die Antwort ist und ob die eingelesenen JSON-Daten zur Anwendung passen.
Protokoll, Adresse und Datenformat auseinanderhalten
HTTP bedeutet Hypertext Transfer Protocol und beschreibt den Austausch von Anfrage und Antwort. Eine URL, ein Uniform Resource Locator, bezeichnet die angesprochene Ressource. JSON, die JavaScript Object Notation, ist ein mögliches Format des Antwortinhalts. Ein HTTP-Erfolg bedeutet daher noch nicht, dass die Antwort gültiges JSON enthält oder dass etwa ein Betrag eine nichtnegative Ganzzahl ist.
Die Übung nutzt einen lokalen Testserver, damit Sie Antwort und Fehler gezielt verändern können. Sie benötigen nur die Python-Standardbibliothek und zwei Terminals mit derselben Projektumgebung. Der Server demonstriert die Kommunikation mit einem Client; er ist kein Beispiel für einen produktiven öffentlichen API-Dienst. Verwenden Sie einen eigenen Übungsordner ohne vertrauliche Dateien, weil der Dateiserver Inhalte dieses Ordners bereitstellt.
Bei externen Diensten kommen deren dokumentierte HTTPS-Adresse, Authentifizierung, Anfragelimits und Nutzungsvorgaben hinzu. Die allgemeine Schnittstelle erläutert die Referenz zu urllib.request. Die Dokumentation zum HTTP-Testserver beschreibt dessen Einsatzgrenzen.
Eine kontrollierte Antwort und den lokalen Server vorbereiten
Speichern Sie im neuen Ordner die Datei expenses.json als UTF-8 mit diesem vollständigen Inhalt:
[{"id":"e1","name":"coffee","amount":3500},{"id":"e2","name":"lunch","amount":8000}]
Öffnen Sie das erste Terminal in genau diesem Ordner und starten Sie:
python -m http.server 8000 --bind 127.0.0.1
Der Server bleibt in diesem Terminal aktiv. 127.0.0.1 ist die lokale Loopback-Adresse: Die Bindung stellt den Testdienst auf diesem Rechner bereit. Verwenden Sie keine Bindung an alle Netzwerkschnittstellen für diese Übung. Ist Port 8000 bereits belegt, wählen Sie einen freien anderen Port und passen Sie später dieselbe Portnummer in der Client-URL an. Beenden Sie keinen fremden Prozess nur für diesen Test.
Unter macOS oder Ubuntu kann außerhalb einer virtuellen Umgebung der Interpreterbefehl python3 heißen. Achten Sie in beiden Terminals darauf, welchen Interpreter Sie verwenden. Der Server sucht die angeforderte Datei unter seinem Arbeitsordner; ein Start im falschen Ordner führt deshalb oft zu einem 404-Fehler.
Den vollständigen Client ausführen und das Ergebnis prüfen
Speichern Sie daneben die folgende vollständige Datei fetch_expenses.py:
import json
import sys
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
url = sys.argv[1]
try:
with urlopen(url, timeout=5) as response:
if response.status != 200:
raise ValueError("unexpected status")
raw = response.read(1_000_001)
if len(raw) > 1_000_000:
raise ValueError("response too large")
data = json.loads(raw.decode("utf-8"))
except (HTTPError, URLError, TimeoutError, UnicodeError, ValueError) as error:
raise SystemExit(f"request failed: {type(error).__name__}")
if not isinstance(data, list):
raise SystemExit("expected a list")
total = 0
for row in data:
if not isinstance(row, dict) or type(row.get("amount")) is not int or row["amount"] < 0:
raise SystemExit("invalid record")
total += row["amount"]
print("records:", len(data))
print("total:", total)
Führen Sie im zweiten Terminal aus:
python fetch_expenses.py http://127.0.0.1:8000/expenses.json
Bei der unveränderten Beispieldatei erscheinen zwei Zeilen: records: 2 und total: 11500. Der erste Kommandozeilenparameter ist die URL; ohne diesen Parameter scheitert das Beispiel bereits beim Zugriff auf sys.argv[1]. Verwenden Sie daher den vollständigen Aufruf. Für eine weitergegebene Anwendung sollten Sie die Parameter später beispielsweise mit argparse prüfen.
Der Client erwartet ausdrücklich Status 200. Er liest höchstens 1.000.001 Bytes und lehnt mehr als 1.000.000 Bytes ab. Das zusätzliche Byte dient dazu, eine zu große Antwort zu erkennen. Die Grenze betrifft die hier eingelesenen Antwortbytes; sie beschreibt nicht die Größe aller späteren Python-Objekte im Arbeitsspeicher.
Nach UTF-8-Dekodierung und JSON-Einlesen muss das äußere Objekt eine Liste sein. Jeder Eintrag muss ein Dictionary mit einem nichtnegativen amount vom genauen Typ int sein. So werden auch boolesche Werte abgelehnt, obwohl bool in Python von int abgeleitet ist. Die Felder id und name kommen zwar in der Beispieldatei vor, werden von diesem Client aber nicht geprüft. Behaupten Sie daher keine vollständige fachliche Datensatzprüfung.
Das Einlesen und die Bedeutung der JSON-Typen behandelt auch der Kurs zur JSON-Verarbeitung. Bei einer gültigen leeren Liste sind Anzahl und Summe beide null; eine beschädigte oder unzulässige Antwort soll dagegen keine scheinbar erfolgreiche Nullsumme erzeugen.
HTTP-Fehler, Verbindungsfehler und Datenfehler unterscheiden
HTTPError bedeutet, dass eine HTTP-Fehlerantwort vorliegt, etwa beim fehlenden Dateinamen. URLError kann Verbindungs- oder Namensauflösungsprobleme kennzeichnen. Ein TimeoutError, ein Dekodierungsfehler oder ungültiges JSON ist ebenfalls kein erfolgreiches Ergebnis. Die Referenz zu urllib.error beschreibt die Ausnahmeklassen.
Die Meldung request failed fasst im Beispiel mehrere Fehlerarten im Anfrage- und Einleseschritt zusammen; der anschließende Klassenname hilft bei der Unterscheidung. Eine unzulässige äußere JSON-Struktur führt danach zu expected a list, ein falscher Betragswert zu invalid record. Das Programm beendet sich mit einem Fehlerstatus, statt die Summierung fortzusetzen. Die gezeigte Ausnahmebehandlung ist keine Garantie, dass jeder denkbare Abbruch einer Netzwerkverbindung unter genau diesen Meldungen erscheint.
timeout=5 begrenzt blockierende Netzwerkoperationen, legt aber keine feste Gesamtdauer für den gesamten Ablauf fest. Eine Antwort, die wiederholt Daten liefert, oder nachfolgende Verarbeitung kann eine gesonderte Zeitplanung erfordern. Falls Sie Wiederholungen ergänzen, legen Sie Anzahl, Pausen und die Wiederholbarkeit der Operation fest. Bei schreibenden Anfragen kann ein verlorener Antworttext bedeuten, dass die Operation bereits stattgefunden hat; blindes Wiederholen wäre dann falsch.
- Ändern Sie nur den Dateinamen in der angefragten URL. Ein fehlender Pfad muss als HTTP-Fehler erscheinen. Stellen Sie danach die richtige URL wieder her.
- Ändern Sie in der Datei einen Betrag in die JSON-Zeichenfolge
"3500". Die Antwort kann weiterhin HTTP-Status 200 haben, der Client muss aberinvalid recordmelden. Stellen Sie den Betrag anschließend als Zahl wieder her. - Beenden Sie den Testserver im ersten Terminal mit Ctrl+C und rufen Sie den Client erneut auf. Nun fehlt die Verbindung, statt dass eine erfolgreiche Datenantwort eintrifft. Starten Sie den Server nur bei Bedarf erneut und beenden Sie ihn nach Abschluss der Übung.
Für einen realen Dienst protokollieren Sie Fehler so, dass eine Diagnose möglich ist, ohne Zugangstoken oder vertrauliche Antwortinhalte auszugeben. Halten Sie Zugangsdaten aus Quelltext, URL und allgemeinen Protokollen heraus und verwenden Sie die vom jeweiligen Dienst vorgesehenen Verfahren.




