Anfrage-Bodys und eine JSON-API · Übung
Anlegen, ersetzen, löschen
Die Tabelle aus der letzten Lektion sagt, was jeder Schreibzugriff tut und antwortet. Dein Server tut davon noch nichts. Starte im Tab Shell das Schreib-Szenario des Hilfsprogramms:
python pl_http.py writes
Es sendet POST /books mit einem neuen Buch, dann GET, PUT, DELETE und noch einmal GET auf /books/3, die ID, die das neue Buch bekommen sollte. Heute bekommt das POST 405, weil /books nur GET und HEAD erlaubt, und die vier Anfragen für /books/3 bekommen 404, weil nie ein Buch 3 angelegt wurde. Am Ende dieser Lektion zeigt derselbe Lauf, wie das Buch erscheint, sich ändert und verschwindet.
In dieser Lektion ist jeder Body ein gültiges Buch: ein title und author, gekennzeichnet als application/json. Lektion 5 fügt die Prüfungen für alles andere hinzu.
201 und location
Ein POST /books, das ein Buch speichert, antwortet mit 201 Created. Sein Body ist das Buch, wie es gespeichert wurde, einschließlich ID, und ein Header location sagt, wo das neue Buch liegt. Hier ist die Antwort, ohne die Zeilen Server und Date:
HTTP/1.0 201 Created
content-type: application/json
content-length: 58
location: /books/3
{"id": 3, "title": "Piranesi", "author": "Susanna Clarke"}
Eine 200 würde auch sagen, dass die Anfrage erfolgreich war; 201 sagt mehr, nämlich dass jetzt eine neue Ressource existiert, und location sagt, wo. location: /books/3 ist ein Pfad, keine vollständige Location gegen die URL auf, die er angefragt hat (RFC 9110, Abschnitt 10.2.2), so wie ein Browser einen Link auf einer Seite auflöst. Kapitel 6 schreibt diese Auflösung für deinen Client. Die ID im Pfad ist die, die der Server gewählt hat, also erfährt der Client sie aus der Antwort, ohne den Body zu parsen.
Die ID kommt aus state["next_id"]. Lies sie, speichere das Buch darunter und addiere 1, damit das nächste POST die nächste ID bekommt. Eine ID im Body wird ignoriert: Bei POST wählt der Server die IDs.
Nachdem POST /books Buch 41 angelegt hat, was sollte location sagen?
204 heißt kein Body
Ein DELETE, das ein Buch entfernt, antwortet mit 204 No Content: Erfolg, ohne etwas zu senden. Eine 204 hat per Definition keinen Body, also trägt sie auch kein content-length (RFC 9110, Abschnitt 8.6, verbietet es bei einer 204). Damit scheidet json_response(204, ...) aus, das beides hinzufügt. Bau diese Antwort von Hand:
Auf der Leitung ist sie eine Statuszeile, die Zeilen Server und Date, die http.server hinzufügt, und die Leerzeile. Danach folgt nichts.
Clients gehen mit einer 204 unterschiedlich um. http.client weiß, dass eine 204 keinen Body hat, und liest danach nichts. Das fetch in deinem wire.py, geschrieben in Kapitel 1, kennt nur HEAD als bodylos, also löst es bei einer 204 ohne content-length handle bei einer 204 doch einen Body zurückgäbe, würde http.server ihn trotzdem senden, und pl_http.py würde die überzähligen Bytes in einer eigenen Zeile unter der Antwort zeigen, etwa (5 bytes followed the reply: b'stray').
Welche Antwort auf DELETE /books/41 ist für diesen Server richtig?
Ersetzen heißt alles ersetzen
PUT /books/7 ersetzt Buch 7 durch das, was der Body sagt. Das neue Buch wird aus title und author des Bodys und der ID aus der URL gebaut, 7, nie aus dem Body. Bau ein neues PUT ersetzt, also überlebt vom alten Buch nichts außer seiner ID. Die Antwort ist 200 OK mit dem gespeicherten Buch.
Ein PUT oder DELETE an eine ID ohne Buch bekommt 404, was dein handle bereits beantwortet, bevor es sich die
Deine Aufgabe
In reading_server.py ist der Body bereits gelesen und in dieser Lektion gültig. Verwende das vorhandene json_body(request), um ihn zu dekodieren.
create_book(request, state): Speichere{"id": ..., "title": ..., "author": ...}unterstate["next_id"], addiere 1 zunext_idund gibjson_response(201, book)mit einem hinzugefügten Headerlocationzurück:/books/gefolgt von der ID.replace_book(request, state, book_id): Speichere ein neues Buch mitbook_idundtitleundauthoraus dem Body; gibjson_response(200, book)zurück.delete_book(state, book_id): Entferne das Buch und gib das 204-Dictionary von oben zurück.Ruf in
handlebeiPOSTauf/bookscreate_bookauf und beiPUTundDELETEauf/books/{id}replace_bookunddelete_book. Ändere dieallow-auf GET, HEAD, POSTfür die Sammlung undGET, HEAD, PUT, DELETEfür ein Element.
Führ python pl_http.py writes noch einmal aus. Das POST bekommt jetzt HTTP/1.0 201 Created mit location: /books/3, das GET liest das neue Buch, das PUT bekommt 200 OK mit dem neuen Titel, das DELETE bekommt HTTP/1.0 204 No Content, gefolgt von nichts als der Leerzeile, und das letzte GET bekommt 404: Das Buch ist weg.
Drück dann Check my work. Als Nächstes hört der Server auf, Bodys zu vertrauen: Jede Art fehlerhafter Anfrage bekommt ihren eigenen Status.
Aufgabe
Bring die drei Schreibzugriffe zum Laufen, für Bodys, die gültige Bücher sind.
Dekodiere in reading_server.py den Body mit dem vorhandenen json_body(request) und schreib:
create_book(request, state): Speichere{"id": ..., "title": ..., "author": ...}unterstate["next_id"], mittitleundauthoraus dem Body, und addiere dann 1 zustate["next_id"]. Gibjson_response(201, book)mit einem hinzugefügten Headerlocationzurück, dessen/books/gefolgt von der neuen ID ist.replace_book(request, state, book_id): Speichere ein neues Buch mit der IDbook_idundtitleundauthoraus dem Body und gibjson_response(200, book)zurück. Die ID kommt immer aus der, nie aus dem Body. delete_book(state, book_id): Entferne das Buch und gib{"status": 204, "headers": {}, "body": b""}zurück: kein Body und keincontent-length.In
handle:POSTauf/booksruftcreate_bookauf;PUTundDELETEauf/books/{id}rufenreplace_bookunddelete_bookauf, sobald feststeht, dass das Buch existiert (eine unbekannte ID bekommt weiterhin 404). Die 405-AntwortenGET, HEAD, POSTauf/booksundGET, HEAD, PUT, DELETEauf/books/{id}auf.
In dieser Lektion ist jeder Anfrage-Body ein title und author, gekennzeichnet als application/json. Lektion 5 behandelt den Rest.
Im python pl_http.py writes über deinen Server ein Buch an, liest es, ersetzt es und löscht es.
Drück dann Check my work. Es ruft handle mit einem Zustand und Büchern auf, die du noch nicht gesehen hast: zwei POSTs, ein zweimal gesendetes PUT, ein DELETE, Schreibzugriffe auf IDs ohne Buch, Lesezugriffe nach den Schreibzugriffen und id, die ignoriert werden muss. Es prüft Status, location, den gespeicherten Zustand, next_id und dass eine 204 weder einen Body noch ein content-length hat. Dann sendet es ein POST und ein DELETE im Speicher durch den Adapter, mit deinem read_body, und liest die Bytes. Nichts aus deinem Terminal wird bewertet.