0%

Caching und bedingte Anfragen · Übung

Versieh jede Repräsentation mit einem Tag

make_etag kann jeden Body benennen, und noch verwendet es keine Antwort. Starte das Szenario etag des Hilfsprogramms, um zu sehen, was ein Client heute bekommt:

python pl_http.py etag

Es sendet GET /books/1, dann ein PUT /books/1 mit dem Beispiel-Token des Schreibers und einem neuen Titel, dann noch einmal GET /books/1, und am Ende zeigt es den Header etag der ersten und der letzten Antwort:

--- the two etag lines ---
before the PUT: None
after the PUT: None

Keine der Antworten trägt ein Tag, und keine sagt, wie lange sie wiederverwendet werden darf. Diese Lektion fügt beides jeder Antwort hinzu, die es haben sollte: ein etag und cache-control: max-age=60.

Welche Antworten ein Tag bekommen

Ein Tag benennt einen Body, den ein Client behalten und nach dem er später fragen darf. Das ist ein erfolgreicher Lesezugriff: eine 200 auf ein GET oder ein HEAD auf /books oder auf /books/{id}. Nichts anderes bekommt eins. Einen Fehler-Body wie {"error": "no book has that id"} behält ein Client nicht. Die Antwort auf einen Schreibzugriff meldet, was gerade passiert ist, und erst der nächste Lesezugriff ist es , behalten zu werden. , /login und /me bleiben, wie sie sind. SERVICE_CONTRACT.md hat einen neuen Abschnitt, „Caching“, der dasselbe in einer Tabelle sagt.

Jede bekommt das Tag ihrer eigenen Bytes. nennt den Body, den eine URL zurückgibt, eine Repräsentation, und /books, /books?limit=1 und /books/2 sind drei verschiedene Repräsentationen mit drei verschiedenen Tags, obwohl sie Bücher gemeinsam haben. Ein Client, der nach /books?limit=1 fragt, sendet das Tag, das er von dieser URL bekommen hat.

add_validators(response) fügt einem Antwort- beide Header hinzu und gibt es zurück. Ein Validator ist in der Sprache von HTTP ein Header, mit dem ein Client eine gespeicherte Kopie prüfen kann; das Tag ist der, den dieser Server sendet. Die tut also zwei Dinge:

  • response["headers"]["etag"] auf make_etag(response["body"]) setzen;

  • response["headers"]["cache-control"] auf max-age=60 setzen, gebaut aus der Konstante MAX_AGE oben in reading_server.py, zum Beispiel mit dem f"max-age={MAX_AGE}".

handle ruft sie an genau zwei Stellen auf, den zwei Zweigen, die eine 200 für einen Lesezugriff bauen. Der Listenzweig wird zu response = add_validators(json_response(200, {"books": books})), und der Zweig für ein Buch tut dasselbe mit json_response(200, state["books"][book_id]).

HEAD bekommt das Tag von GET

Kapitel 4 hat deinem Server beigebracht, HEAD mit genau den Headern zu beantworten, die GET senden würde, und ohne Body, und die letzten Zeilen von handle tun das, indem sie den Body leeren, wenn die HEAD ist. Die Reihenfolge dieser beiden Schritte zählt jetzt. Würde das Tag berechnet, nachdem der Body geleert wurde, trüge eine HEAD-Antwort das Tag eines leeren Bodys, "e3b0c442...b855", einen Namen für Bytes, die GET nie sendet. Ein Client, der eine Ressource mit HEAD prüft und das Tag mit seinem vergleicht, sähe immer einen Unterschied.

add_validators innerhalb der Lesezweige aufzurufen, setzt es vor die HEAD-Regel, weil die Regel zuletzt läuft, nach jedem Zweig. GET und HEAD teilen sich diese Zweige, also bekommen beide das Tag des vollen Bodys, und erst dann verliert HEAD den Body.

Welches etag trägt HEAD /books?

Ein Schreibzugriff ändert das Tag von selbst

Sieh dir an, woraus das Tag berechnet wird: aus dem Body, in dem Moment, in dem ein Lesezugriff ihn baut. Nichts speichert das Tag, also muss auch nichts daran denken, es zu ändern. Ein PUT, das Buch 2 umbenennt, ändert die Bytes von GET /books/2, also bekommt der nächste Lesezugriff darauf ein neues Tag. Dasselbe PUT ändert die , also bekommt auch /books ein neues Tag, ebenso jede ?limit=-Seite, die Buch 2 enthält. Die Bytes von Buch 1 haben sich nicht geändert, und sein Tag auch nicht.

Ein Tag, das auf andere Weise gebaut wird, muss von Hand im Gleichschritt gehalten werden, und früher oder später ist es das nicht. Ein Tag, das aus der ID des Buchs gemacht wird, bleibt nach einem PUT gleich, und ein Client würde weiter den alten Titel anzeigen, während der Server „unverändert“ sagt.

Welche Tags ändern sich nach PUT /books/2?

Jeden Body bei jedem Lesezugriff zu hashen, kostet etwas Arbeit. Ein viel genutzter echter Dienst berechnet ein Tag oft einmal, wenn sich die Daten ändern, und speichert es neben den Daten, was schneller ist und eine Sache mehr, die im Gleichschritt gehalten werden muss. Ein Tag, das bei jedem Lesezugriff berechnet wird, ist nie veraltet, und dieser Server hält es so.

Deine Aufgabe

Schreib in reading_server.py add_validators(response) und ruf es wie oben in den zwei Lesezweigen von handle auf. Fehler, Schreibzugriffe und jede andere Route bleiben unberührt. Starte dann das Szenario noch einmal:

python pl_http.py etag

Unter jeder 200 für einen Lesezugriff gibt das Hilfsprogramm jetzt die zwei Header aus, und die Tags der beiden Lesezugriffe unterscheiden sich, weil das PUT dazwischen die Bytes von Buch 1 geändert hat:

--- the two etag lines ---
before the PUT: '"ecfe550f34c7f26be888feb41dbb0bdc5dfff7e77c57a8e049eb0c721b08049b"'
after the PUT: '"929daebfa038d3a896d78c3e8b93fdac7fece4faed0d92572e8a88a4733bb5f4"'

Die Antwort auf das PUT selbst trägt keinen der beiden Header. Drück dann Check my work.

Jeder Lesezugriff hat jetzt ein Tag, und Clients laden trotzdem jeden Body vollständig herunter, weil keiner von ihnen ein Tag zurücksendet. Die nächste Lektion bringt deinem Server bei, einem Client zu antworten, der das tut: Eine Anfrage, die sagt „ich habe dieses Tag“, bekommt eine 304 ohne Body, wenn das Tag noch aktuell ist.

Aufgabe

Schreib add_validators(response) in reading_server.py und ruf es in handle auf.

  • add_validators setzt response["headers"]["etag"] auf make_etag(response["body"]) und response["headers"]["cache-control"] auf max-age=60 (aus MAX_AGE) und gibt dann response zurück.

  • Ruf es in handle auf der 200 im Zweig auf, der Bücher auflistet, und auf der 200 im Zweig, der ein Buch zurückgibt: response = add_validators(json_response(200, ...)). Diese Zweige laufen vor der HEAD-Regel am Ende von handle, also trägt eine HEAD-Antwort das Tag des Bodys, den GET sendet.

  • Keine andere Antwort bekommt einen der beiden Header: Fehler, , die Antworten auf POST, PUT und DELETE, /login, /logout und /me bleiben, wie sie sind.

Führ python pl_http.py etag im aus, um die beiden Tags vor und nach einem PUT zu sehen.

Drück dann Check my work. Es ruft add_validators direkt auf und dann handle mit einem Zustand aus Büchern, IDs und Tokens, die du noch nicht gesehen hast. Es prüft, dass GET auf /books, /books?limit=1 und /books/{id} ein etag trägt, das dem SHA-256-Digest des gesendeten Bodys in Anführungszeichen entspricht, und ein cache-control, das max-age=60 enthält; dass HEAD dasselbe Tag, dasselbe cache-control und content-length trägt und keinen Body; dass sich nach einem PUT die Tags dieses Buchs und von /books ändern, während sich das Tag eines anderen Buchs nicht ändert; dass ein POST und ein DELETE das Tag von /books ändern; und dass 404, 405, 400, 308, 401, 403, 201, 204, die PUT-Antwort und /me kein etag tragen. Nichts aus deinem Terminal wird bewertet.