Zelf HTTP serveren · oefening
Een handler is een functie
De adapter staat klaar om voor elk verzoek handle(request, state) aan te roepen, en handle werpt nog NotImplementedError op. Deze les schrijft het, samen met een hulpfunctie die handle een
De antwoord-dict
Open reading_server.py. Een antwoord-dict heeft drie sleutels:
"status": deals int, zoals 200."headers": een dict van headernaam naar. Schrijf de namen in kleine letters, zoals "content-type".vergelijkt headernamen zonder op hoofdletters te letten, dus content-typeenContent-Typebetekenen voor elke client hetzelfde; HTTP/2 eist zelfs kleine letters. De waarden zijn. "body": bytes, omdat bytes zijn wat er op de socket gaat.
handle haalt de boeken uit zijn tweede state, niet uit een dict bovenaan het bestand. sample_state() bouwt de twee boeken die je in de
Waarom neemt handle state aan in plaats van een dict op moduleniveau te lezen?
Tel bytes, geen tekens
json.dumps(data) zet data om in JSON-tekst, en .encode("utf-8") zet die tekst om in bytes. Geef ensure_ascii=False mee om tekens zoals é te houden zoals ze zijn; zonder dat json.dumps é als de zes tekens \u00e9, wat ook geldige JSON is, alleen moeilijker te lezen op de lijn:
De tekst is 25 tekens, en de body is 26 bytes, omdat é in Content-Length telt bytes, precies zoals toen build_request hem in hoofdstuk 2 instelde voor een verzoekbody. Neem de lengte van de gecodeerde body, nooit van de tekst.
Wat drukt dit af?
print(len("Café Society"), len("Café Society".encode("utf-8")))
Het contenttype is application/json, zonder parameter charset: JSON is op de lijn altijd UTF-8, en de definitie van application/json heeft zo’n parameter niet. practice-api voegde er toch een toe; clients negeren hem.
Geen lengte, geen einde
Een client leest de kop tot aan de lege regel, en dan moet hij weten hoeveel bodybytes er volgen. content-length vertelt het hem. Zonder die header is de enige manier om het einde te vinden lezen totdat de server de verbinding sluit. Onder HTTP/1.0, dat de adapter spreekt, sluit de server na elk antwoord, dus het einde is op die manier nog te vinden, en http.client leest zo’n antwoord tot het sluiten. Je wire.py uit hoofdstuk 1 weigert het in plaats daarvan: zijn read_body werpt Content-Length aankwam. In hoofdstuk 9 blijft de verbinding na een antwoord open, en een antwoord zonder lengte laat de client wachten op een sluiting die nooit komt. Stuur de lengte mee bij elk antwoord dat een body heeft.
Je opdracht
Schrijf
json_response(status, data). Bouw de body metjson.dumps(data, ensure_ascii=False).encode("utf-8")en geef een antwoord-dict terug met die status, de headers"content-type"(de constanteJSON_TYPE, die al bovenaan het bestand staat) en"content-length"(str(len(body))), en de body.Schrijf de eerste versie van
handle(request, state). Bouw eenvan elk boek in state["books"], op id, oplopend, en geefjson_response(200, {"books": books})terug.SERVICE_CONTRACT.mdbelooft die volgorde. De state houdt zijn boeken al zo bij, omdat elk nieuw boek het volgende, hogere id krijgt, en een dict zijn sleutels bewaart in de volgorde waarin ze zijn toegevoegd, dus eenfor-over state["books"]komt ze op id tegen, oplopend. Voorlopig negeerthandlehet verzoek: elkeen elk doel krijgt de lijst. Les 4 leert het nee te zeggen.
Controleer het vanuit het tabblad Shell. main() roept handle aan voor drie doelen en drukt elke antwoord-dict af, nog steeds zonder server en zonder socket:
python reading_server.py
Je zou drie regels moeten zien, voor /books, /books/2 en /nowhere, elk met status 200, je twee headers met content-length '132', en dezelfde body. In de body verschijnt é als \xc3\xa9, zijn twee UTF-8-bytes. Alle drie de doelen krijgen de hele lijst, omdat handle nog niet naar het doel kijkt.
Druk daarna op Check my work. Daarna bereikt voor het eerst een echte verbinding handle.
Opdracht
Schrijf twee reading_server.py.
json_response(status, data)geeft een antwoord-terug: "status"is de status die het kreeg,"body"isjson.dumps(data, ensure_ascii=False).encode("utf-8"), en"headers"bevat"content-type"met deJSON_TYPEen"content-length"met de waardestr(len(body)), de lengte van de body in bytes.handle(request, state)geeftjson_response(200, {"books": books})terug, waarbijbookselk boek instate["books"]opsomt, op id, oplopend. Voorlopig beantwoordt het elk verzoek zo, welkeof welk doel het ook is. Het leest de boeken uit stateen verandert er niets in.
Voer python reading_server.py uit in de handle teruggeeft voor drie doelen.
Druk daarna op Check my work. Die roept json_response aan met statussen en data die je niet hebt gezien, ook tekst buiten ASCII, en controleert de status, dat de body bytes zijn die die data als application/json, en dat content-length gelijk is aan de lengte van de body in bytes. Ze roept handle aan voor GET /books met eigen states, met drie boeken, twee boeken en geen, en controleert de status, de headers, de body {"books": [...]} en dat de state daarna ongewijzigd is. Niets uit je terminal wordt beoordeeld, en er wordt geen server gestart.