0%

Hoofdstuk 12 · eindproject

Eindproject: de leeslijstservice en -client

Eindproject: De server afmaken

Hoofdstuk 11 bouwde de leeslijst opnieuw in . Dit hoofdstuk gaat terug naar de zelfgebouwde server en naar de requests-client uit hoofdstuk 10, en vraagt beide om tegelijk aan één document te voldoen: SERVICE_CONTRACT.md.

Het contract ligt vast. Niets in dit hoofdstuk verandert er een route, een status, een header of een exitcode in. Twee programma’s vertrouwen nu op dezelfde woorden. Als de server location: /books/3 stuurt, maakt de client er een van en drukt die af; als de server met 304 antwoordt, hergebruikt de client een boek dat hij een minuut geleden heeft opgeslagen. Als een van de twee helften van het contract afwijkt, breekt de andere helft, terwijl de eigen code er goed uitziet. Elke helft heeft eerder zijn eigen tests doorstaan; nieuw is dat je beide tegelijk aan één contract houdt.

Lees het contract, vind de gaten

Open SERVICE_CONTRACT.md in het tabblad Project en scrol naar de laatste sectie, “Not built yet” (nog niet gebouwd). Die noemt drie rijen. Elke rij heeft een serverhelft, die je in deze les herstelt, en een clienthelft, die de volgende twee lessen bouwen:

RijServerhelft (deze les)Clienthelft
Oude padenelke op /v0/books en /v0/books/... krijgt een 308 naar hetzelfde pad zonder /v0, met behoud van de queryeen regel followed 308 from URL voor elke die de client volgde (les 3)
Hervalidatieeen overeenkomende If-None-Match maakt van een 200 voor een leesverzoek een 304 zonder bodyeen cachebestand dat show hergebruikt zolang het vers is en hervalideert als het verlopen is (les 2)
Nieuwe boekende 201 voor POST /books draagt location: /books/{id}add drukt de absolute URL van het nieuwe boek af (les 2)

Geen van de serverhelften is nieuw voor je. De location-regel komt uit hoofdstuk 5, legacy_location en de 308-tak uit hoofdstuk 6, en etag_matches, not_modified en de 304-regel uit hoofdstuk 8. Ze zijn uit een verder afgemaakte server gehaald, zodat de clienthelften iets hebben om mee overeen te stemmen.

De rest van reading_server.py is de nagekeken server van het eind van hoofdstuk 9: /1.1 met keep-alive, read_body die na elke weigering de verbinding sluit, inloggen en sessies, bearer-tokens, en etag en cache-control op elke 200 voor een leesverzoek. Laat die zoals hij is. Check my work bewijst die rijen opnieuw, met boeken, gebruikers en tokens die je niet hebt gezien, omdat een wijziging op de ene plek een rij ver weg kan breken.

Mijlpalen

Eerst de oude paden. Schrijf legacy_location(target): neem het pad met urlsplit(target).path, knip de eerste drie tekens eraf, /v0, en voeg ? en de query toe als die er is. Werk vanuit het ruwe target, niet vanuit de gedecodeerde segmenten, zodat de ASCII blijft. Voeg dan de tak helemaal bovenaan handle toe, vóór elke andere route: als de eerste twee segmenten v0 en books zijn, is het antwoord redirect(308, legacy_location(request["target"])). Hij komt eerst, zodat een schrijfverzoek naar een oud pad wordt doorverwezen voordat het token wordt gecontroleerd, en er niets wordt opgeslagen.

Dan de hervalidatie. Schrijf etag_matches(values, etag): values is de met if-none-match-regels, en elke regel kan meerdere tags bevatten, gescheiden door komma’s. Strip elk element, haal een W/ aan het begin weg, en geef True terug voor * of voor een tag die gelijk is aan etag. Schrijf not_modified(response): een nieuwe antwoord- met status 304, de etag en cache-control van de 200 gekopieerd, en de body b"". Voeg dan de regel aan handle toe, na de routering en vóór de regels voor no-store en HEAD: als het antwoord een etag heeft en het verzoek if-none-match stuurde, en die twee overeenkomen, wordt het antwoord not_modified(response). Alleen een 200 voor een leesverzoek draagt een etag, dus fouten en schrijfverzoeken blijven ongemoeid, en * op een ontbrekend boek krijgt nog steeds zijn 404.

Dan de location. Voeg in create_book één regel toe vóór de : in de headers van de 201 wordt location gezet op /books/ plus het nieuwe id. De client zet deze relatieve waarde in les 2 om.

Twee helperruns uit eerdere hoofdstukken werken nog steeds op deze server:

python pl_http.py moved
python pl_http.py conditional

In de moved-run zou het log van je server 308 moeten tonen voor de twee /v0-verzoeken waar de server waarmee je dit hoofdstuk begon 404 antwoordde. In de conditional-run zou het log 200, 304, 304, 200 moeten tonen: de eerste GET krijgt de tag, de volgende GET en de HEAD sturen hem terug en krijgen 304, en het laatste verzoek stuurt een verouderde tag en krijgt de hele lijst opnieuw.

Welke functie van de client werkt niet meer als de 304-regel van de server ontbreekt?

Je tests

tests/test_server.py roept handle rechtstreeks aan met make_request, zoals sinds hoofdstuk 5. Voer het uit na elke mijlpaal:

python -m pytest -q

Elke test die met het project meekwam, zou nog moeten slagen. Les 4 voegt tests toe voor de drie rijen.

Je opdracht

  1. Schrijf legacy_location, en de 308-tak bovenaan handle.

  2. Schrijf etag_matches en not_modified, en de 304-regel in handle.

  3. Voeg location toe aan de 201 in create_book.

  4. Voer python -m pytest -q uit, en daarna de twee helperruns.

  5. Klik op Check my work.

Als een controle mislukt, noemt de melding het verzoek en de regel. Vraag Monty om die samen met je naast de bijbehorende tabel in SERVICE_CONTRACT.md te lezen, zonder de oplossing te schrijven.

De server voldoet nu aan elke serverrij van het contract. De client downloadt nog steeds elk boek elke keer opnieuw en drukt een nieuw boek af zonder zijn URL; de volgende les verhelpt beide.

Opdracht

Maak de serverhelft van SERVICE_CONTRACT.md af: de drie serverrijen onder “Not built yet”.

  1. legacy_location(target): het pad van het target zonder de eerste drie tekens, /v0, met ? en de query erachter als die er is. Bovenaan handle, vóór elke andere route, krijgt een pad waarvan de eerste twee segmenten v0 en books zijn redirect(308, legacy_location(request["target"])), voor elke .

  2. etag_matches(values, etag): True als een element van een if-none-match-regel, gestript en zonder W/ aan het begin, gelijk is aan etag, of * is. not_modified(response): een 304 met alleen de etag en cache-control van de 200 en de body b"". In handle, na de routering en vóór de regels voor no-store en HEAD, wordt een antwoord met een etag waarmee de if-none-match van het verzoek overeenkomt not_modified(response).

  3. In create_book draagt de 201 location: /books/{id}.

Houd elke andere rij van het contract zoals de server er al aan voldoet. Voer python -m pytest -q, python pl_http.py moved en python pl_http.py conditional uit in de .

Klik daarna op Check my work. Die roept handle aan met boeken, gebruikers en tokens die je niet hebt gezien (de id’s 7, 41 en 1000, de gebruiker zoë), en controleert: leesverzoeken, HEAD, 404’s en 405 met allow; dat content-length bij elke body past, op bytes die de handlerklasse van je server in het geheugen schrijft; de location van de 201, gelezen tegen de van het verzoek en opnieuw opgehaald; PUT en DELETE; elke weigering naar haar oorzaak, in de volgorde van het contract, zonder dat er iets wordt opgeslagen; de 308 voor GET, HEAD, POST, PUT en DELETE op /v0-paden met een percent-gecodeerde query; inloggen, de HttpOnly-, /me en uitloggen; 401 en 403 voor schrijfverzoeken; etag en cache-control met MAX_AGE op 45; de 304 voor één tag, een , twee regels, W/ en *, eindigend bij de lege regel; drie verzoeken op één verbinding, en een weigering die haar sluit; en make_server en serve met een vervangende server die verbindingen telt. Niets opent een , en niets uit je terminal wordt beoordeeld.