0%

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 - mit einem 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 . erlaubt das: Ein Client löst ein relatives 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:

Try it

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 aus, dieselbe Ablehnung, die es bei jeder Antwort ohne Länge gibt. Wenn dein 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 , statt das alte zu aktualisieren: 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 ansieht. Lass das so: Die Schreibfunktionen werden nur für ein Buch aufgerufen, das existiert.

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.

  1. create_book(request, state): Speichere {"id": ..., "title": ..., "author": ...} unter state["next_id"], addiere 1 zu next_id und gib json_response(201, book) mit einem hinzugefügten Header location zurück: /books/ gefolgt von der ID.

  2. replace_book(request, state, book_id): Speichere ein neues Buch mit book_id und title und author aus dem Body; gib json_response(200, book) zurück.

  3. delete_book(state, book_id): Entferne das Buch und gib das 204-Dictionary von oben zurück.

  4. Ruf in handle bei POST auf /books create_book auf und bei PUT und DELETE auf /books/{id} replace_book und delete_book. Ändere die allow- auf GET, HEAD, POST für die Sammlung und GET, HEAD, PUT, DELETE fü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:

  1. create_book(request, state): Speichere {"id": ..., "title": ..., "author": ...} unter state["next_id"], mit title und author aus dem Body, und addiere dann 1 zu state["next_id"]. Gib json_response(201, book) mit einem hinzugefügten Header location zurück, dessen /books/ gefolgt von der neuen ID ist.

  2. replace_book(request, state, book_id): Speichere ein neues Buch mit der ID book_id und title und author aus dem Body und gib json_response(200, book) zurück. Die ID kommt immer aus der , nie aus dem Body.

  3. delete_book(state, book_id): Entferne das Buch und gib {"status": 204, "headers": {}, "body": b""} zurück: kein Body und kein content-length.

  4. In handle: POST auf /books ruft create_book auf; PUT und DELETE auf /books/{id} rufen replace_book und delete_book auf, sobald feststeht, dass das Buch existiert (eine unbekannte ID bekommt weiterhin 404). Die 405-Antworten GET, HEAD, POST auf /books und GET, HEAD, PUT, DELETE auf /books/{id} auf.

In dieser Lektion ist jeder Anfrage-Body ein - mit den title und author, gekennzeichnet als application/json. Lektion 5 behandelt den Rest.

Im legt 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 , die ein Pfad nicht annimmt. Bodys tragen eine 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.