Doorverwijzingen · oefening
Stuur het oude pad naar een nieuwe plek
De clientkant kan nu elke location oplossen. Deze les geeft hem iets om op te lossen: de leeslijst begint zijn oude /v0/books-paden met moved van het hulpprogramma uit, dat drie verzoeken stuurt die een oude of slordige client zou kunnen sturen:
python pl_http.py moved
De verzoeken zijn GET /v0/books/2?x=1, een PUT /v0/books/2 met een GET /books/ met een slash aan het eind. Elk antwoord begint met HTTP/1.0 404 Not Found. Voor de eerste twee bestaat er onder /v0 niets meer; voor de derde geeft path_segments ["books", ""], en een leeg segment is geen boek-id. Alle drie vragen om dingen die de server heeft, op adressen die hij niet meer beantwoordt.
308 voor elke methode
SERVICE_CONTRACT.md zegt wat er in plaats daarvan gestuurd moet worden. Elk verzoek voor /v0/books of een pad daaronder, zoals /v0/books/2, krijgt een 308 Permanent Redirect, wat de PUT of POST van een oude client moet het nieuwe pad ongewijzigd bereiken, met zijn body. Het antwoord draagt het nieuwe adres in location, content-length: 0 en een lege body.
Het is verleidelijk om het werk meteen te doen: de PUT is er al, met een geldig boek in de body, dus waarom niet opslaan en 200 antwoorden? Omdat de server dan twee paden heeft die schrijven, en elke regel die je vanaf nu aan /books/{id} toevoegt (hoofdstuk 7 voegt toe wie überhaupt mag schrijven), ook aan /v0 moet worden toegevoegd, anders wordt het oude pad een manier om eromheen te gaan. Een doorverwijzing houdt één pad dat het werk doet. De client stuurt het verzoek opnieuw naar het nieuwe pad, en alleen dat verzoek verandert iets. Een doorverwijzing als antwoord verandert helemaal geen state.
De nieuwe tak komt bovenaan handle, vóór de routes die je al hebt, zodat hij elke methode op dezelfde manier beantwoordt en er eerst geen methodecontrole draait.
Behoud de rest
Een oude client die om /v0/books/7?draft=1 vroeg, wil boek 7, met dezelfde querystring. De location moet alles na /v0 behouden: de rest van het pad en de querystring. Als hij /books zegt, komt een oude PUT die voor boek 7 bedoeld was, op de collectie terecht en krijgt die een 405; als hij ?draft=1 kwijtraakt, vraagt het verzoek stilletjes om iets anders.
legacy_location(target) bouwt die urlsplit(target).path, knip de eerste drie tekens, /v0, eraf met een ? en de querystring toe als die er is. Gebruik het target zelf, niet de gedecodeerde segmenten uit path_segments: het target is al percent-gecodeerd, dus het is gewone ASCII en past in een header. Een headerwaarde moet in Latin-1 passen om überhaupt geschreven te kunnen worden, en http.server werpt UnicodeEncodeError op bij een location die gebouwd is uit een gedecodeerd segment zoals ✓, voordat er één byte van het antwoord is verstuurd. Omdat de waarde met / begint, is hij relatief: de client lost hem op ten opzichte van de resolve_location doet, en de server hoeft de regel Host niet in elke location te kopiëren.
Er komt PUT /v0/books/7?draft=1 binnen. Welke location stuurt de server?
Een slash te veel
/books/ is een tikfout voor /books, en een veelvoorkomende. Het contract beantwoordt die met een 301 Moved Permanently naar /books. 301 past bij een opruimactie die voor leesacties bedoeld is: een GET voor /books/ wordt een GET voor /books. Een client die een POST naar /books/ stuurde, kan na een 301 als GET aankomen, dus zijn body gaat verloren; een schrijfactie hoort op het exacte pad, en het contract vraagt alleen om 308 waar oude clients schrijven. Schrijf de tak voor precies het pad /books/, dat path_segments omzet in ["books", ""]. Niets anders wordt doorverwezen: /books/7/ blijft een 404, net als elk /v0-pad dat niet /v0/books of daaronder is.
redirect(status, location) bouwt beide antwoorden: de status, een header location, content-length op "0", en de body b"". Een doorverwijzing heeft in zijn body niets te zeggen, en content-length: 0 vertelt de client dat, en bakent het antwoord af zoals elk ander antwoord.
Jouw opdracht
In reading_server.py:
Schrijf
redirect(status, location).Schrijf
legacy_location(target).Voeg bovenaan
handle, vóór de/books-takken, twee takken toe: een pad waarvan de eerste twee segmentenv0enbookszijn, krijgtredirect(308, legacy_location(...))met het target van het verzoek, en de segmenten["books", ""]krijgenredirect(301, "/books"). Laat elke andere route zoals hij is.
Voer het scenario opnieuw uit:
python pl_http.py moved
Elk antwoord draagt nu een doorverwijzing. Lees de regel location ervan:
server -> client b'HTTP/1.0 308 Permanent Redirect\r\nServer: ...\r\nDate: ...\r\nlocation: /books/2?x=1\r\ncontent-length: 0\r\n\r\n'
server -> client b'HTTP/1.0 308 Permanent Redirect\r\nServer: ...\r\nDate: ...\r\nlocation: /books/2\r\ncontent-length: 0\r\n\r\n'
server -> client b'HTTP/1.0 301 Moved Permanently\r\nServer: ...\r\nDate: ...\r\nlocation: /books\r\ncontent-length: 0\r\n\r\n'
De waarden van Server en Date zijn hier weggelaten; de jouwe noemen je Python-versie en het tijdstip van de run. Het logblok somt de drie verzoeken op met 308, 308 en 301. De PUT veranderde niets op de server: alleen een verzoek dat /books/2 bereikt, kan boek 2 veranderen.
pl_http stuurt deze verzoeken zelf en leest precies wat er terugkomt, dus het stopt bij elke doorverwijzing. http.client ook: gevraagd om /v0/books/2?x=1 geeft het de 308 terug en gaat niet verder. Volgen is de taak van de client, en de volgende les schrijft de client die dat doet.
Klik daarna op Check my work.
Opdracht
Beantwoord de oude paden in reading_server.py met
redirect(status, location)geeft een antwoord-terug met die status, een header locationmetlocationerin,content-lengthop"0", en de bodyb"".legacy_location(target)geeft terug waar een/v0/books...-target nu staat: het pad zonder de/v0aan het begin, gevolgd door?en de querystring als die er is. Bouw het uit het target zoals het aankwam, nog steeds percent-gecodeerd.Bovenaan
handle, vóór de andere routes: elkeop een pad waarvan de eerste twee segmenten v0enbookszijn, krijgtredirect(308, legacy_location(request["target"])), en de segmenten["books", ""](het pad/books/) krijgenredirect(301, "/books").Een doorverwijzing verandert niets in
state. Elk ander pad, ook/books/7/en/v0/other, houdt het antwoord dat het had.
Voer python pl_http.py moved uit in de location.
Klik daarna op Check my work. Die roept redirect en legacy_location rechtstreeks aan, en roept daarna handle aan met een state en boek-id’s die je nog niet hebt gezien: GET, HEAD, POST, PUT en DELETE op oude paden met en zonder querystring (een PUT en een POST dragen een geldig boek), /books/, paden die niet mogen doorverwijzen, en de routes uit hoofdstuk 5. Hij controleert de status, dat location de id, de rest van het pad en de querystring exact behoudt (een absolute http://-content-length 0, de lege body, en dat de state niet veranderd is. Hij schrijft ook één doorverwijzing in het geheugen door http_adapter en leest de bytes. Niets uit je terminal wordt beoordeeld.