Bodies van verzoeken en een JSON-API · oefening
Weiger foute verzoeken naar oorzaak
Je schrijfacties werken voor geldige boeken, en alleen daarvoor. Stuur de foute verzoeken van het hulpprogramma en kijk wat er gebeurt:
python pl_http.py bad-requests
Het eerste verzoek is POST /books met de body title=Dune, gelabeld als text/plain. handle roept create_book aan, die json_body aanroept, die een (no bytes: the server closed the connection without answering), en het logblok bevat een json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0). Als handle een exception opwerpt, drukt http.server de traceback af en sluit hij de verbinding zonder antwoord, dus de client komt niets te weten. pl_http stopt na dat verzoek met verzenden en zegt dat ook.
Een data["title"]; een boek met een lege titel zou worden opgeslagen. Elk fout verzoek heeft een status nodig die zegt wat er mis is, en geen ervan mag de state veranderen.
Status naar oorzaak
SERVICE_CONTRACT.md somt deze controles op, en de eerste die mislukt, bepaalt het antwoord. Hier staan ze in de volgorde waarin de server ze uitvoert:
| Controle | Mislukt als | Status | Wie controleert |
|---|---|---|---|
| framing | geen lengte bij POST of PUT; een misvormde lengte of een te korte body; te lang | 411; 400; 413 | read_body |
| route en | zo’n pad bestaat niet, of het pad accepteert deze methode niet | 404, 405 | handle |
| boek-id | geen boek heeft die id | 404 | handle |
| mediatype | content-type is niet application/json | 415 | body_error |
| syntaxis | de bytes zijn geen | 400 | body_error |
| vorm en | geen title en author | 422 | book_error |
Een verzoek dat door elke rij komt, bereikt de schrijfactie en krijgt 201, 200 of 204. Over de plaats van twee rijen moet iets gezegd worden. Framing komt vóór de route, omdat read_body in de adapter draait voordat handle iets ziet: daarom krijgt een POST /nowhere zonder lengte 411, niet 404. En de boek-id komt vóór de body, dus een PUT naar een boek dat niet bestaat, is een 404, wat de body ook zegt. Volg in het diagram een fout verzoek langs de controles omlaag tot het rechts vertrekt:

400 of 422
Een 400 en een 422 kunnen allebei een foute body beantwoorden; het verschil is hoe ver de server kwam. 400 Bad Request zegt dat het verzoek helemaal niet gelezen kon worden: deze bytes laten zich niet als JSON parsen. 422 Unprocessable Content zegt dat de server het prima kon lezen en niet kan doen wat erin staat (RFC 9110, sectie 15.5.21): de JSON is geldig, maar het is een lijst waar een object hoort, of de titel is 7, of de auteur is drie spaties.
Vorm controleren betekent isinstance(value, dict) vraagt of een waarde een isinstance(value, str) vraagt of het een string is. Een titel die leeg is zodra strip() de spaties weghaalt, is net zo leeg als "".
Wat drukt dit af?
print(isinstance([1, 2], dict), isinstance({"title": 3}["title"], str))
PUT /books/7 (boek 7 bestaat) draagt {"title": "", "author": "Ana Ruiz"}, gelabeld als application/json, met een juiste lengte. Welke status?
Eerst controleren, dan wijzigen
book_error(data) controleert een gedecodeerde waarde: geen dict, een ontbrekende title of author, of een die geen niet-lege string is, elk geval krijgt een 422 waarvan de fout het probleem noemt. Schrijf elke melding voor de persoon die het antwoord leest: author is missing vertelt wat er veranderd moet worden, en bad request niet. Je mag eerst title of eerst author controleren; hoe dan ook is het eerste gevonden probleem het probleem dat wordt gemeld. book_request_error(request) voert de hele bodycontrole uit: eerst body_error, en alleen als die niets vindt, book_error(json_body(request)). Elk van beide geeft None terug of het antwoord dat verstuurd moet worden, zoals read_body en body_error doen, dus handle houdt de eerste fout die hij krijgt:
response = book_request_error(request)
if response is None:
response = create_book(request, state)
De volgorde binnenin doet er twee keer toe. book_error mag niet draaien op bytes die body_error heeft geweigerd, omdat json_body daarop een exception zou opwerpen. En create_book mag niet draaien voordat elke controle geslaagd is: een boek dat vóór een 422 is opgeslagen, of een next_id die is opgehoogd, zou een wijziging zijn waarvan de client te horen kreeg dat die niet gebeurd is.
PUT /books/99 (zo'n boek bestaat niet) draagt een body gelabeld als text/csv. Welke status, volgens de volgorde van dit contract?
Jouw opdracht
In reading_server.py:
Schrijf
book_error(data): geef een 422-antwoord terug, gebouwd metjson_error, alsdatageen dict is, alstitleofauthorontbreekt, of als een van beide geen string is of leeg is na het strippen. Geef andersNoneterug.Schrijf
book_request_error(request): geefbody_error(request)terug als dat nietNoneis; geef andersbook_error(json_body(request))terug.Roep in
handlebook_request_erroraan vóórcreate_booken vóórreplace_book, en stuur het antwoord ervan als het er een teruggeeft.
Voer python pl_http.py bad-requests opnieuw uit. Elk verzoek krijgt nu een eigen antwoord: HTTP/1.0 415 Unsupported Media Type voor de text/plain-body, HTTP/1.0 400 Bad Request voor de afgebroken JSON, HTTP/1.0 422 Unprocessable Content voor de lijst, HTTP/1.0 404 Not Found voor de PUT naar boek 99, en HTTP/1.0 411 Length Required voor de POST zonder lengte, die je read_body beantwoordde voordat handle draaide.
Klik daarna op Check my work. Je server weigert nu elke foute schrijfactie met een reden; de volgende les bewijst met je eigen tests dat de goede schrijfacties hun beloften nakomen.
Opdracht
Geef elke foute schrijfactie een eigen status, en verander niets als een verzoek wordt geweigerd.
In reading_server.py:
Schrijf
book_error(data). Geef een 422-antwoord terug alsdatageenis, als het geen titleof geenauthorheeft, of als een van beide geenis of leeg is nadat de spaties zijn weggehaald. Geef anders Noneterug.Schrijf
book_request_error(request). Geef terug watbody_error(request)teruggeeft als dat nietNoneis; geef andersbook_error(json_body(request))terug.Roep in
handlebook_request_erroraan vóórcreate_booken vóórreplace_book. Als die een antwoord teruggeeft, stuur je dat antwoord en roep je de schrijfactie niet aan.
Bouw elk foutantwoord met json_error en een zin die het probleem noemt. Houd de volgorde van handle aan: route en
In de python pl_http.py bad-requests vijf foute verzoeken naar je server.
Klik daarna op Check my work. Die roept book_error aan met null, ontbrekende velden, getallen en lege waarden waar strings horen, en geldige boeken met extra velden. Hij roept book_request_error en handle aan met telkens één oorzaak, op POST /books en op PUT naar een bestaand boek, en controleert voor elk de status en dat de state, inclusief next_id, precies is zoals ervoor. Hij stuurt ook een PUT met een verkeerd label naar een boek dat niet bestaat, en controleert dat geldige schrijfacties nog steeds werken. Niets uit je terminal wordt beoordeeld.