Weiterleitungen · Abschlussprojekt
Abschlussprojekt: Projekt: Bücher, die umgezogen sind
Du hast beide Hälften einer /v0/books-Pfade mit einer 308, die die ID und die Query behält, und dein follow löst ein location auf, behält oder ändert die pl_http.py follow laufen lassen, aber jede Prüfung bisher hat eine Hälfte nach der anderen bewiesen, gegen Ersatzobjekte. Und nichts, was du bisher geschrieben hast, sendet einen Schreibzugriff über deine Folgefunktion: follow nimmt einen Body, aber keine send_json, und zeigt, dass das PUT eines alten Clients über die Weiterleitung deines Servers in einem Hop das gemeinte Buch erreicht.
Was send_json tut
send_json(method, url, data, *, make_connection, max_hops, timeout) ist der Client, den eine alte Leseliste-App aufrufen würde, um ein Buch zu speichern:
Kodiere
dataalsin -Bytes: json.dumps(data, ensure_ascii=False).encode("utf-8"), wie es dasjson_responsedeines Servers tut.Sende es mit
followund übergib dabei den Body, einen HeaderContent-Type: application/jsonsowiemake_connection,max_hopsundtimeoutgenau so, wiesend_jsonsie bekommen hat.Füge dem Datensatz, den
followzurückgegeben hat,"data"hinzu: den als UTF-8-JSON dekodierten Body, wenn dercontent-typeder Antwortapplication/jsonist (wie ; charset=utf-8erlaubt) und der Body nicht leer ist, und sonstNone.get_jsonhat in Kapitel 3 dieselbe Entscheidung getroffen.
Jeder Status ist ein Datensatz, wie bisher. Eine 422 vom Server kommt mit ihrem Fehler in "data" zurück; send_json löst nur aus, was follow auslöst, etwa TimeoutError, wenn ein Server stumm bleibt.
Etwa acht Zeilen reichen, weil der schwierige Teil schon in follow steckt. Sieh dir an, was follow mit diesem Body macht, wenn es auf eine Weiterleitung trifft: Nach einer 308 gehen das PUT und sein Body unverändert an den neuen Pfad; nach einer 303 fallen der Body und sein content-type weg. Die 308 deines Servers und die Regel deiner Folgefunktion für 308 müssen übereinstimmen, sonst geht der Schreibzugriff eines alten Clients verloren.
Meilensteine
Schreib zuerst send_json. Starte dann das Szenario agree des Hilfsprogramms:
python pl_http.py agree
Es ruft dein send_json("PUT", "http://books.test/v0/books/2", ...) mit einem neuen Titel für Buch 2 auf, gegen deinen eigenen Server auf diesem Rechner. Du solltest zwei Anfragen sehen und dann die Daten, die dein Server gespeichert hat:
request 1 PUT /v0/books/2 -> 308 Permanent Redirect
request 2 PUT /books/2 -> 200 OK
...
data: {'id': 2, 'title': 'Café Society (2nd edition)', 'author': 'Ana Ruiz'}
Dann sendet das Hilfsprogramm sein eigenes GET /books/2, und auch diese Antwort trägt den neuen Titel: Der Schreibzugriff hat den aktuellen Pfad erreicht, und nur einmal.
Dann die loop:
python pl_http.py loop
Hier betreibt dein Server einen kleinen, für diesen Kurs geschriebenen Handler, der im Kreis weiterleitet: /a antwortet mit 307 nach /b, und /b antwortet mit 307 nach /a. Das Hilfsprogramm ruft dein follow("GET", "http://books.test/a", max_hops=3, ...) auf. Eine korrekte Folgefunktion sendet vier Anfragen und löst ValueError aus; das Protokoll zeigt request 1 bis request 4, alle 307 Temporary Redirect, dann raised ValueError mit deiner Meldung, und das Szenario endet mit Status 0, weil hier der Fehler das richtige Ergebnis ist. Das Hilfsprogramm gibt dem Server fünf Verbindungen und öffnet die eine, die deine Folgefunktion nicht verwendet hat, selbst, was es als pl_http opened 1 empty connection(s) so serve() could stop waiting meldet. Eine Folgefunktion ohne Limit macht weiter, bis der Server aufgehört hat, und scheitert stattdessen mit ConnectionRefusedError.
Optional: Teste send_json selbst. fakes.py hat jetzt scripted_connections(responses, calls): ein make_connection, dessen Verbindungen der Reihe nach mit den übergebenen Antworten antworten, eine pro Verbindung, und für jede Verbindung Host, scripted_connections der Zeile from fakes import in tests/test_wire.py hinzu, und einen Test wie diesen:
def test_send_json_follows_a_308():
calls = []
moved = scripted_response(308, "Permanent Redirect", [["location", "/books/2"]], b"")
stored = scripted_response(200, "OK", [["content-type", "application/json"]], b'{"id": 2, "title": "Dune"}')
connections = scripted_connections([moved, stored], calls)
record = wire.send_json("PUT", "http://books.test/v0/books/2", {"title": "Dune"}, make_connection=connections)
assert record["data"] == {"id": 2, "title": "Dune"}
assert calls[1]["requests"][0]["method"] == "PUT"
assert calls[1]["closed"]
Führ deine Tests im Projektordner aus:
python -m pytest -q
Sie gehören dir; Check my work führt sie nicht aus.
Was bewiesen wird, und wie
Check my work beweist die Übereinstimmung ohne send_json in einem Prozess mit deinem eigenen reading_server.handle: Die Verbindung, die es deiner Folgefunktion übergibt, baut aus dem, was dein Code sendet, ein Anfrage-read_body und handle, wie es http_adapter täte, und verwandelt das Antwort-Dictionary zurück in eine Antwort. Die 308, die deine Folgefunktion liest, ist also die, die dein Server geschrieben hat, und das PUT, das es wiederholt, ändert den Zustand, den dein Server führt. Die Läufe agree und loop zeigen dasselbe über eine echte Verbindung auf diesem Rechner. Diese Läufe sind zum Zuschauen da; sie werden nicht bewertet.
Drück dann Check my work.
Jede Anfrage in diesem Kurs stand bisher für sich allein: Der Server hat jeden Client in dem Moment vergessen, in dem er geantwortet hat, und kein Client konnte beweisen, wer er ist. Kapitel 7 gibt der Leseliste ein Gedächtnis dafür, wer fragt, mit einer Session in einem
Aufgabe
Schreib send_json(method, url, data, *, make_connection, max_hops, timeout) in wire.py, damit ein
Kodiere
dataals JSON in-Bytes. Sende es mit
follow(method, url, ...), mit diesem Body, einem HeaderContent-Type: application/jsonund denmake_connection,max_hopsundtimeout, diesend_jsonbekommen hat.Gib den Datensatz zurück, den
followzurückgegeben hat, mit hinzugefügtem"data": dem als UTF-8-JSON dekodierten Body, wenn dercontent-typeder Antwortapplication/jsonist (mit oder ohne) und der Body nicht leer ist, sonst None.Jeder Status ist ein Datensatz. Lass
beim Hop-Limit und jede andere Ausnahme aus followsend_jsonverlassen.
Die follow aus Lektion 4 müssen weiterhin funktionieren: Diese Prüfung lässt sie zusammen laufen. Führ python pl_http.py agree und python pl_http.py loop im
Drück dann Check my work. Es übergibt send_json ein make_connection, dessen Verbindungen jede Anfrage als Anfrage-read_body und reading_server.handle schicken, mit einem Zustand aus Büchern, die du noch nicht gesehen hast, und das Antwort-Dictionary deines Servers zurück in eine Antwort verwandeln; es wird kein PUT an einen alten Pfad mit Query das Buch nach genau einer 308 ändert und 200 mit dem gespeicherten Buch in "data" zurückgibt; dass ein POST an /v0/books nach einer 308 mit 201 ein Buch anlegt; dass eine 422 als Datensatz mit dem Fehler in "data" zurückkommt; dass eine Antwort ohne Body oder mit einem Body, der kein JSON ist, für "data" None ergibt; dass ein mit 303 beantwortetes POST als GET ohne Body weitergeht; dass eine Weiterleitungsschleife nach max_hops + 1 Anfragen ValueError auslöst; und dass der Timeout jede Verbindung erreicht. Deine eigenen Tests werden nicht ausgeführt. Nichts aus deinem Terminal wird bewertet.