Zelf HTTP serveren · eindproject
Eindproject: Project: een leeslijst om alleen te lezen
Je server somt de boeken op, weigert wat hij niet kan, en beantwoordt HEAD zoals hij GET beantwoordt. Een client kan nog steeds niet om één boek vragen. Dit project voegt die route toe en maakt de leeslijst af die je alleen kunt lezen, zoals SERVICE_CONTRACT.md in je werkruimte haar beschrijft.
Wat de dienst belooft
Open SERVICE_CONTRACT.md. De tabel daarin is de eis:
| Verzoek | Antwoord |
|---|---|
GET /books | 200, {"books": [every book, by id, ascending]} |
GET /books/{id} | 200, het boek; 404 als geen boek dat id heeft |
HEAD op een van beide | de status en headers die GET zou sturen, en geen body |
| een andere | 405 met allow: GET, HEAD |
| elk ander pad | 404 |
Elk antwoord met een body is content-length waarin de bytes zijn geteld, en elke foutbody is {"error": "..."}. De eerste, derde, vierde en vijfde rij kloppen al voor /books. Het nieuwe werk is de tweede rij, plus de rijen voor HEAD en 405 voor /books/{id}. {id} staat voor één padsegment: /books/2 vraagt om het boek waarvan het id 2 is.
Een id bestaat uit cijfers
Het segment komt aan als tekst, "2", en de sleutels van state["books"] zijn ints, dus "2" zou daar nooit gevonden worden. book_id_from(segment) zet een segment om in de int die het noemt, of geeft None terug als het geen id noemt. int(segment) alleen is niet genoeg: het werpt "abc", en het accepteert ook tekst die hier geen id is, zoals " 12" met een spatie, "-1", en cijfers uit andere schriften. De stringmethode isdigit() alleen is ook niet genoeg:
De superscript twee en de Arabisch-Indische cijfers voor twaalf tellen allebei als cijfers voor isdigit(). int maakt van de tweede 12 en werpt voor de eerste ValueError op. Beide kunnen je server bereiken: een client verstuurt %C2%B2 of %D9%A1%D9%A2 in het pad, en path_segments decodeert ze. Een id in deze dienst bestaat uit een of meer ASCII-cijfers en niets anders, dus controleer eerst segment.isascii() and segment.isdigit(), en roep pas daarna int aan. Een leeg segment slaagt voor geen van beide controles, dus /books/ krijgt nog steeds een 404.
Of 007 boek 7 noemt, beslis je zelf; int("007") is 7, en dit project beslist het niet.
Mijlpalen
book_id_from(segment). Geefint(segment)terug als het segment uit ASCII-cijfers bestaat, en andersNone.De tak voor
/books/{id}. Voeg inhandleeen tak toe voor segmenten van lengte twee waarvan het eerste segment"books"is. Zet het tweede om in een id. Als het idNoneis of geen sleutel vanstate["books"], antwoord dan met 404 viajson_error, welke methode het ook is: er is op dat pad niets om toe te staan of te weigeren. VoorGETenHEADantwoord je met 200 viajson_responseen het ene boek zoals het is opgeslagen.Ook daar 405. Elke andere methode op een bestaand boek antwoordt
not_allowed("GET, HEAD"). JeHEAD-regel aan het eind vanhandledekt de nieuwe tak al, omdat hij na het routeren wordt uitgevoerd.Voer het scenario uit. Het verstuurt vijf verzoeken:
GET /books,GET /books/2,HEAD /books/2,GET /books/99, en eenPOST /booksmet een lege body:
python pl_http.py reading-list
Je zou de content-length: 57; dan dezelfde kop voor HEAD /books/2 met niets na de lege regel; dan een 404 met je foutbody; dan een 405 met allow: GET, HEAD. Het resultaatblok eindigt met serve() returned after 5 connections; the server is closed.
Tests, als je wilt.
tests/test_server.pybevat al één test diehandlerechtstreeks aanroept, via een hulpfunctiemake_request(method, target). Je kunt er op dezelfde manier eigen tests aan toevoegen en ze allemaal uitvoeren vanuit het tabblad Shell:
def test_unknown_book_is_404():
"""GET /books/99 answers 404 with an error body."""
response = reading_server.handle(make_request("GET", "/books/99"), reading_server.sample_state())
assert response["status"] == 404
assert "error" in json.loads(response["body"].decode("utf-8"))
python -m pytest -q
Deze tests zijn voor jou; Check my work voert ze niet uit. Hoofdstuk 5 vraagt je tests te schrijven die een eigenschap van de server bewijzen, en beoordeelt die.
Wat deze server niet controleert
Een Host hebben, en een HTTP/1.1-server moet een verzoek zonder die header beantwoorden met 400 Bad Request (RFC 9112, sectie 3.2). Deze cursus laat die controle weg uit de leeslijst. Al het andere in het contract doet de server nu.
Als je op Check my work drukt, wordt het hele hoofdstuk opnieuw gecontroleerd met eigen boeken, id’s en paden, en draait je handle ook binnen de echte adapterklasse met verzoekbytes die in het geheugen worden gehouden, zodat de controle de bytes leest die een client zou ontvangen: de statusregel, een content-length gelijk aan de body die erop volgt, niets na de lege regel voor HEAD, en een regel allow bij een 405. Er wordt geen
De leeslijst kan op elke manier worden gelezen die HTTP toestaat, en op geen enkele manier worden veranderd: er is geen manier om een boek toe te voegen. Hoofdstuk 5 leest verzoekbodies en maakt de lijst schrijfbaar met POST, PUT en DELETE.
Opdracht
Maak de leeslijst die je alleen kunt lezen af in reading_server.py, zoals SERVICE_CONTRACT.md haar beschrijft.
Schrijf
book_id_from(segment): geefint(segment)terug als het segment uit een of meer ASCII-cijfers bestaat (segment.isascii()ensegment.isdigit()), enNonevoor al het andere.Voeg
/books/{id}toe aanhandle: voor een id datNoneis of geen sleutel vanstate["books"], antwoord je met 404 viajson_error, welkehet ook is; voor GETenHEADantwoord je met 200 en het boek zoals het is opgeslagen; voor elke andere methode antwoord jenot_allowed("GET, HEAD").Houd alles uit les 2 tot en met 5:
/booksbeantwoordtGETenHEADmet deen andere methoden met 405, elk ander pad antwoordt met 404, HEADkrijgt de status en headers vanGETmet een lege body, enmake_serverenserveblijven zoals je ze schreef.
Voer python pl_http.py reading-list uit in de tests/test_server.py en python -m pytest -q uitvoeren is optioneel.
Druk daarna op Check my work. Die herhaalt de controles van les 2 tot en met 5 met boeken en paden die je niet hebt gezien. Ze roept book_id_from aan met ASCII-cijfers en met tekst die geen id is, zoals "-1", "1.5", "²", " 12" en Arabisch-Indische cijfers. Ze roept handle aan voor bestaande en ontbrekende id’s, voor segmenten die geen id zijn, voor HEAD op elke route, en voor PUT, POST en DELETE op beide paden, en verwacht 200, 404 en 405 met allow dat GET en HEAD noemt, en een content-length waarin de bytes zijn geteld bij elk antwoord met een body. Daarna voert ze je handle uit binnen de handlerklasse van http_adapter op verzoekbytes in het geheugen en leest ze de bytes die worden teruggeschreven: een statusregel 200 en een content-length gelijk aan de body voor één boek, niets na de lege regel voor HEAD, en een regel allow bij een 405. Ze controleert make_server en serve zoals les 3 deed, zonder dat er een