De standaardclient en statusklassen · oefening
Elk scenario indelen
get_json leest de status en de headers die een beslissing nodig heeft. Nu kan het de klasseregel uit les 2 toepassen op elk antwoord, ook op de mislukkingen. practice-api heeft routes die met opzet mislukken, zodat je kunt zien hoe een client ze tegenkomt zonder iets kapot te maken. Deze les schrijft classify, voegt de klasse aan het record toe, en decodeert de body van elk
Vier manieren om te mislukken
Elk van deze doelen mislukt op een andere manier:
/v1/items/99: een item dat niet bestaat;/v1/scenarios/not-found: een 404 die met opzet wordt gestuurd;/v1/scenarios/server-error: een 500 die met opzet wordt gestuurd;/v1/items?page=11&page_size=3: een paginanummer buiten bereik.
Elk ervan komt met een JSON-body, en de bodies hebben één vorm: een "error" met een "code" die een programma kan testen, een "message" die een mens kan lezen, en soms meer. Dit zijn de vier bodies die practice-api stuurt:
{"error":{"code":"not_found","message":"item not found","scenario":"resource"}}
{"error":{"code":"scenario_not_found","message":"intentional not found scenario","scenario":"not_found"}}
{"error":{"code":"scenario_server_error","message":"intentional server error scenario","scenario":"server_error"}}
{"error":{"code":"invalid_parameter","message":"page must be between 1 and 10"}}
Kijk naar de laatste. De status, 400, zegt alleen dat het verzoek fout was. De body zegt welk deel, en wat het zou oplossen: pagina’s lopen van 1 tot en met 10. Een client die bij elke 4xx een raise_for_status deed, verliest precies die zin. Twee van de bodies zijn 404’s, en alleen hun "code" vertelt je dat bij de ene een item ontbreekt, terwijl de andere route met opzet mislukte.
Waarom moet een client de body van een 404 houden?
De functie classify
classify(status) geeft de klasse van elke code terug als een woord dat een mens kan lezen:
status // 100 | classify geeft terug |
|---|---|
| 1 | "informational" |
| 2 | "success" |
| 3 | "redirection" |
| 4 | "client error" |
| 5 | "server error" |
Alles onder 100 of boven 599 is geen classify werpt er ValueError voor op. De regel is het eerste cijfer voor elke code in het bereik, ook voor codes die geen enkele "success" en 418 een "client error". Eén manier om het te schrijven is een lijst van de vijf namen, geïndexeerd met status // 100 - 1; een reeks elif over status // 100 werkt ook.
Een sleutel data voor elke JSON-body
get_json voegt twee sleutels aan zijn record toe:
"class":classify(status), voor elk antwoord."data": de body gedecodeerd metjson.loads(body.decode("utf-8"))als het antwoord als JSON gelabeld is en de body niet leeg is; andersNone.
“Als JSON gelabeld” betekent dat het mediatype application/json is. De content_type uit les 3 kan een ; met .split(";")[0], haal de spaties weg met .strip(), en vergelijk in kleine letters, omdat mediatypen hoofdletters negeren. Een antwoord zonder Content-Type, een text/plain-antwoord en een lege body laten "data" allemaal None, en "body" bevat nog steeds de bytes.
De status speelt in die beslissing geen rol. Een 404 die als JSON gelabeld is, wordt precies zo gedecodeerd als een 200, en zo bereikt de foutbody je aanroeper. En get_json werpt niets op vanwege een status: een 500 is een antwoord als elk ander, met als klasse "server error".
Eén geval werpt wel iets op. Een antwoord dat als JSON gelabeld is maar waarvan de body geen JSON is, laat json.loads json.JSONDecodeError opwerpen, een soort ValueError. Laat die door: het label en de body spreken elkaar tegen, en de aanroeper moet dat te horen krijgen. Decodeer na het with-blok of erbinnen; hoe dan ook is de verbinding gesloten als de exception get_json verlaat.
Je opdracht
Schrijf in
wire.pydeclassify(status).Voeg
"class"en"data"toe aan het record datget_jsonteruggeeft, zoals hierboven beschreven. Houd alles wat les 1 en 3 schreven.Voer alle vier de doelen uit:
python -c "
import wire
for target in ['/v1/items/99', '/v1/scenarios/not-found', '/v1/scenarios/server-error', '/v1/items?page=11&page_size=3']:
record = wire.get_json(target)
print(record['status'], record['class'], record['data'])
"
Als je code geschreven is, drukt het af:
404 client error {'error': {'code': 'not_found', 'message': 'item not found', 'scenario': 'resource'}}
404 client error {'error': {'code': 'scenario_not_found', 'message': 'intentional not found scenario', 'scenario': 'not_found'}}
500 server error {'error': {'code': 'scenario_server_error', 'message': 'intentional server error scenario', 'scenario': 'server_error'}}
400 client error {'error': {'code': 'invalid_parameter', 'message': 'page must be between 1 and 10'}}
Druk daarna op Check my work. Elk antwoord dat aankomt, heeft nu een klasse en houdt zijn body. De volgende les komt het geval tegen waarin helemaal geen antwoord aankomt.
Opdracht
Deel elk antwoord in op klasse en houd elke
Schrijf classify(status) in wire.py. Het geeft "informational", "success", "redirection", "client error" of "server error" terug op basis van het eerste cijfer van de code, status // 100, voor elke code van 100 tot en met 599, en werpt
Voeg twee sleutels toe aan het record dat get_json teruggeeft:
"class":classify(status), voor elk antwoord."data": de body gedecodeerd metjson.loads(body.decode("utf-8"))als het mediatype vancontent_type(het deel vóór een eventuele;, vergeleken in kleine letters)application/jsonis en de body niet leeg is; andersNone.
get_json werpt niets op vanwege een status: een 404 of een 500 geeft zijn record terug, met de body gedecodeerd zoals elke andere. Een body die als JSON gelabeld is maar geen JSON is, laat de ValueError van json.loads door, met de verbinding gesloten. Houd alles wat les 1 en 3 schreven.
Voer de vier mislukkende doelen uit in de
Druk daarna op Check my work. Die roept classify aan met codes uit elke klasse, ook codes zoals 199 en 299 die geen get_json aan met een vervanger voor make_connection waarvan je de gescripte antwoorden niet hebt gezien: 400, 404, 429, 500 en 503 met JSON-foutbodies, een 200 gelabeld als application/json zonder text/plain 418, een lege JSON-body, een antwoord zonder Content-Type, en een antwoord gelabeld als JSON waarvan de body HTML is. Niets uit je terminal wordt beoordeeld.