Eine Antwort lesen
Statuscodes, Antwort-Header und der Body.
Jeder Request bekommt genau eine Antwort (außer die Verbindung bricht ab, was eine eigene Art von Antwort ist und in einer späteren Lektion vorkommt). Eine Antwort hat drei Teile: eine Statuszeile, Header und einen Body. Wer neu im Testen ist, schaut meist nur auf den Body, weil dort die Daten stehen. Die meisten Bugs, die dabei durchrutschen, stecken in den anderen beiden Teilen.
Wie eine Antwort aussieht
Wie der Request ist auch eine HTTP/1.1-Antwort reiner Text. Das ist die Antwort auf GET /api/health, so wie curl sie mit der Option -i zeigt (die die Header zusammen mit dem Body ausgibt). Ein paar Infrastruktur-Header sind entfernt.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
x-powered-by: TestingAPIs
{"status":"healthy","uptimeSeconds":3542,"timestamp":"2026-09-30T18:28:12Z"}Die erste Zeile ist die Statuszeile: Protokollversion, ein dreistelliger Statuscode und eine kurze Begründung. Dann folgen die Header, einer pro Zeile, dann eine leere Zeile, dann der Body. Die Begründung ist nur für Menschen. Clients und Tests sollten auf die Zahl schauen. HTTP/2 lässt die Begründung ganz weg, ein Test, der den Text "OK" prüft, kann also brechen, wenn der Server aktualisiert wird.
Klassen von Statuscodes
Die erste Ziffer des Statuscodes sagt dir die Klasse der Antwort. Oft kannst du ein Ergebnis schon an dieser Ziffer beurteilen.
| Klasse | Bedeutung | Wer schuld ist |
|---|---|---|
| 1xx | Information. Der Request läuft noch. | Niemand. Du siehst sie selten direkt. |
| 2xx | Erfolg. Der Server hat getan, worum du gebeten hast. | Niemand. |
| 3xx | Umleitung. Schau woanders nach. | Niemand. Der Client muss nachfassen. |
| 4xx | Clientfehler. Der Request war falsch. | Der Client: falsche Eingabe, keine Berechtigung, falsche URL. |
| 5xx | Serverfehler. Der Request war vielleicht in Ordnung. | Der Server oder etwas dahinter. |
Diese letzte Spalte ist der Grund, warum Statuscodes beim Testen so wichtig sind. Ein 4xx sagt "korrigier deinen Request". Ein 5xx sagt "bei uns ist etwas kaputtgegangen". Wenn du Unsinn schickst und ein 500 bekommst, ist das ein Bug, obwohl der Request schlecht war, denn der Server ist abgestürzt, wo er die Eingabe höflich mit einem 400 hätte ablehnen sollen. Diese 500er zu finden, ist eines der ergiebigsten Dinge, die du als Neuling tun kannst.
Die Codes, die du am häufigsten siehst
| Code | Name | Typische Verwendung |
|---|---|---|
| 200 | OK | Der Request hat funktioniert, und der Body enthält das Ergebnis. |
| 201 | Created | Ein POST hat einen neuen Datensatz angelegt. |
| 204 | No Content | Hat funktioniert, und es gibt absichtlich keinen Body. Häufig nach DELETE. |
| 301 / 302 | Moved Permanently / Found | Die Ressource liegt unter der URL im Location-Header. |
| 304 | Not Modified | Deine Kopie im Cache ist noch aktuell. Kommt beim Caching dran. |
| 400 | Bad Request | Der Request ist fehlerhaft aufgebaut oder ein Wert ist ungültig. |
| 401 | Unauthorized | Keine oder falsche Zugangsdaten. Trotz des Namens bedeutet es: nicht authentifiziert. |
| 403 | Forbidden | Die Zugangsdaten stimmen, aber dieser Nutzer darf das nicht. |
| 404 | Not Found | Nichts unter dieser URL oder kein Datensatz mit dieser ID. |
| 405 | Method Not Allowed | Die URL existiert, aber nicht mit dieser Methode. |
| 409 | Conflict | Der Request kollidiert mit dem aktuellen Zustand, etwa eine doppelte E-Mail. |
| 422 | Unprocessable Content | Die Syntax stimmt, aber die Daten verletzen eine Regel. |
| 429 | Too Many Requests | Du hast ein Rate Limit erreicht. Mach langsamer. |
| 500 | Internal Server Error | Ein unbehandelter Fehler auf dem Server. |
| 502 | Bad Gateway | Ein Proxy hat eine schlechte Antwort vom Server dahinter bekommen. |
| 503 | Service Unavailable | Der Server ist überlastet oder wird gewartet. |
Bei manchen davon sind sich APIs uneins. Ein Team liefert 400 für jeden Validierungsfehler, ein anderes 422. Manche antworten auf ein Löschen mit 200 und einer Nachricht im Body, andere mit 204 ohne alles. Beides ist nicht falsch. Was du prüfst, ist, dass die API ihrer eigenen Dokumentation entspricht und von Endpunkt zu Endpunkt konsistent ist.
Statuscodes ausprobieren
/api/status/{code} antwortet mit dem Code, den du in den Pfad schreibst. Das ist praktisch, um zu sehen, wie sich ein Werkzeug oder eine Client-Bibliothek bei einem bestimmten Status verhält. Probier ein paar aus:
/api/status/201/api/status/503In curl sieht der 503 so aus:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json; charset=utf-8
retry-after: 5
{
"status": 503,
"statusText": "Service Unavailable",
"category": "server-error",
"message": "This is a 503 response. Use /api/status/{code} to request any status."
}Achte auf den Header retry-after: 5. Ein 503 oder 429 sagt dem Client oft, wie viele Sekunden er warten soll, bevor er es erneut versucht, und ein gut erzogener Client hält sich daran. Du prüfst, dass der Header da ist, nicht nur den Status.
Codes außerhalb des gültigen Bereichs werden abgelehnt. /api/status/999 kommt als 400 zurück, mit der Nachricht Status code must be between 200 and 599 (1xx cannot be sent as a final response). Und /api/status/abc liefert ein 404 mit leerem Body, weil abc gar nicht zur Route passt. Zwei schlechte Eingaben, zwei verschiedene Arten von Fehlschlag. Dieses Muster begegnet dir oft: Ein Wert vom falschen Typ scheitert meist schon in der Routing-Schicht, bevor der Anwendungscode überhaupt läuft.
Umleitungen
Eine 3xx-Antwort hat meist keinen nützlichen Body. Wichtig ist der Header Location, der sagt, wohin es als Nächstes geht:
HTTP/1.1 302 Found
Content-Type: application/json; charset=utf-8
location: /api/echoBrowser und die meisten HTTP-Bibliotheken folgen Umleitungen automatisch und zeigen dir nur die letzte Antwort. Das ist bequem, bis du die Umleitung selbst testen musst. curl folgt ihnen nur, wenn du -L angibst, und ist dafür daher das bessere Werkzeug.
Antwort-Header
Antwort-Header beschreiben den Body und geben dem Client Anweisungen. Frag nach Seite 3 der Produktliste, zwei Produkte pro Seite:
/api/Products?limit=2&page=3HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
x-page: 3
x-per-page: 2
x-total-count: 20
x-total-pages: 10Content-Type sagt, dass der Body JSON in UTF-8 ist. Die vier Header x-page und x-total tragen die Details der Paginierung: Es gibt 20 Produkte, 10 Seiten, und das ist Seite 3. Der Body ist nur ein Array aus zwei Produkten, ein Client, der diese Header ignoriert, kann also nicht wissen, dass es mehr gibt. Header, die mit X- beginnen, sind eigene Erfindungen der API. Dokumentiert werden sie von der API, nicht vom HTTP-Standard.
Manche Header enthalten Zeiten als Unix-Timestamps: die Zahl der Sekunden seit dem 1. Januar 1970, UTC. APIs mit Rate Limits schicken oft einen in x-ratelimit-reset, um zu sagen, wann dein Kontingent zurückgesetzt wird. Rechne ihn um, bevor du ihm traust: Ein Reset-Zeitpunkt in der Vergangenheit oder in Jahren ist ein Bug, den du nur findest, wenn du Header liest, statt sie zu überspringen. (Der Timestamp-Konverter übernimmt die Umrechnung.)
Vielleicht erwartest du einen Header Content-Length. Diese API schickt stattdessen meist Transfer-Encoding: chunked, das heißt, der Body wird in Stücken gesendet und die Gesamtgröße nicht vorab genannt. Beides ist gültig. Eine Antwort hat das eine oder das andere, nicht beides.
Der Body
Der Body sind die Daten: ein Produkt, eine Liste, eine Fehlermeldung, ein Bild. Content-Type sagt, wie er zu lesen ist. Die meisten deiner Prüfungen betreffen den Body, und JSON lesen behandelt das ausführlich. Vorher zwei Sonderfälle.
Gar kein Body
Ein 204 bedeutet Erfolg ohne Body, und zwar absichtlich. Die Antwort hat auch keinen Content-Type, weil es nichts zu beschreiben gibt:
/api/formats/no-contentHTTP/1.1 204 No Content
x-powered-by: TestingAPIsClient-Code, der immer response.json() aufruft, stürzt hier ab, weil ein leerer String kein gültiges JSON ist. Wenn du Test-Helfer schreibst, die jede Antwort parsen, müssen sie mit einem leeren Body umgehen können.
Wenn die Header lügen
Status, Header und Body werden von verschiedenen Teilen des Servers erzeugt, und nichts zwingt sie, übereinzustimmen. Hier ist eine Antwort, die behauptet, HTML zu sein:
/api/formats/wrong-content-typeHTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
{"id":1,"name":"Alice Johnson","email":"alice@example.com","roles":["admin","editor"],"active":true,"score":98.5,"manager":null}Der Body ist einwandfreies JSON. Ein Mensch, der ihn liest, würde nie ein Problem bemerken. Ein Browser würde versuchen, ihn als Webseite darzustellen, und manche HTTP-Bibliotheken weigern sich, ihn als JSON zu parsen, oder geben ihn als String zurück. Dieser Endpunkt ist absichtlich kaputt. In echten Systemen kommt derselbe Bug meist von einer Fehlerseite, die ein Proxy oder eine Framework-Voreinstellung erzeugt. Dein Test sollte den Content-Type ausdrücklich prüfen.
Das Spiegelbild gibt es auch: ein Status 200 mit einem Body, der {"success": false} sagt. Manche APIs melden jeden Fehler so. Monitoring-Werkzeuge, die 5xx-Antworten zählen, sehen diese Fehler nie, und ein Test, der nur den Statuscode prüft, auch nicht. Wenn eine API das tut, müssen deine Tests bei jeder Antwort den Body lesen.
Was du bei jeder Antwort prüfst
Bevor du dir ein einziges Datenfeld ansiehst, bestätige die Grundlagen. Das dauert ein paar Sekunden und fängt erstaunlich viele Bugs.
- Der Statuscode ist der, den die Dokumentation für diese Situation verspricht.
Content-Typepasst zu dem Body, den du tatsächlich bekommen hast.- Header, die die Dokumentation erwähnt (Paginierung,
Location,Retry-After), sind vorhanden und ergeben Sinn. - Der Body lässt sich parsen und hat die Form, die du erwartest.
- Fehlerantworten folgen demselben Format wie andere Fehler dieser API.
Übung
Notier dir für jeden Request den Statuscode, den Content-Type und einen Satz zum Body.
/api/status/418. Schlag nach, warum es diesen Code gibt./api/formats/emptyanmelden zum Anzeigen. Was unterscheidet ihn von/api/formats/no-contentanmelden zum Anzeigen? Einer liefert 200, der andere 204. Mit welchem kommt ein Client deiner Meinung nach besser zurecht?/api/Products?limit=2&page=99. Es gibt keine Seite 99. Welchen Status und Body hast du bekommen, und was sagen die Paginierungs-Header? Hättest du es genauso entworfen?/api/status/list. Such in der Liste einen Code mit"supported": falseund versuch, ihn anzufordern.