Wat een framework voor je doet · oefening
Laat een model de body controleren
In hoofdstuk 5 bewaakten twee reading_server.py. body_error keek naar het verzoek, en book_error naar de geparste gegevens:
| Functie | Weigert als | Status |
|---|---|---|
body_error(request) | content-type niet application/json is | 415 |
body_error(request) | de body geen | 400 |
book_error(data) | het geen JSON- | 422 |
book_error(data) | titel of auteur ontbreekt | 422 |
book_error(data) | titel of auteur geen niet-lege | 422 |
handle riep ze in die volgorde aan, en create_book en replace_book draaiden alleen als beide None teruggaven. De app doet beide taken met het model dat je in les 1 las, BookIn, en één annotatie in elke def-regel: book: BookIn.
Het model controleert eerst
Als POST /books binnenkomt, leest BookIn, allemaal voordat create_book draait. Je functie wordt alleen aangeroepen met een body die door de controle kwam, en ze ontvangt die body als een BookIn-object. Het object geeft zijn gecontroleerde book.title en book.author. Het is geen book["title"] werpt
Op de versies die deze cursus gebruikt, weigert het model:
een body die geen JSON-object is, of waarin
titleofauthorontbreekt;een veld dat geen string is:
7entrueworden geweigerd, en nooit omgezet in"7"of"true";een veld zonder enig teken behalve spaties, zoals
""of" ". Daar vraagtpattern=r"\S"om: één teken, waar dan ook, dat geen spatie, tab, regeleinde of ander soort witruimte is.
De rest accepteert het zoals het is. " Ærø " houdt zijn spaties, "7" is een string, en een titel van 3000 tekens heeft geen limiet om te overschrijden. Velden die het model niet noemt, zoals "id": 99, worden weggelaten voordat je functie de body ziet, dus een client kan het id van een boek niet kiezen.
De body van een POST is {"title": "Dune"}. Wanneer draait create_book?
De 422 van FastAPI lezen
Op die body antwoordde je server uit hoofdstuk 5 {"error": "author is missing"}. De app antwoordt 422 met deze body:
{"detail":[{"type":"missing","loc":["body","author"],"msg":"Field required","input":{"title":"Dune"}}]}
detail is een type de regel die niet werd gehaald (hier missing; string_type voor een getal waar een string hoort; string_pattern_mismatch voor een lege titel), zegt loc waar (["body", "author"], het veld author van de body), zegt msg het in woorden, en herhaalt input de waarde die werd gecontroleerd. Jouw fout was één zin voor een mens. Deze is data waar een clientprogramma op kan reageren, veld voor veld. Beide hebben 422.
Waar het framework anders beslist
De meeste slechte body’s krijgen de status die ze van je server kregen. Deze niet:
| Verzoek | Hoofdstuk 5 | De FastAPI-app |
|---|---|---|
een geldig JSON-boek met label text/plain | 415 | 422 |
een geldig JSON-boek zonder content-type | 415 | 422 |
| JSON die niet te parsen is | 400 | 422 |
| bytes die geen UTF-8 zijn | 400 | 400, {"detail":"There was an error parsing the body"} |
een boek met label application/merge-patch+json | 415 | geaccepteerd, 201 |
PUT /books/9999, zo’n boek bestaat niet, met een kapotte body | 404 | 422 |
FastAPI parset een body alleen als JSON wanneer de content-type JSON zegt: application/json, of een application/-type waarvan het subtype op +json eindigt, zoals application/merge-patch+json. Elk ander label, of geen label, laat de body als tekst staan, en het model weigert tekst omdat het geen object is; de 422 zegt dat het model een object verwachtte. Er is geen aparte 415. Kapotte JSON is ook een 422, met het type json_invalid. Bytes die niet als UTF-8 te decoderen zijn, zijn het enige bodyprobleem waarop FastAPI met 400 antwoordt, en de detail is daar een gewone string.
De rij met PUT gaat over volgorde. Je server zocht het id op voordat hij de body las, dus een onbekend id was een 404, wat de body ook bevatte. FastAPI controleert de body voordat het replace_book aanroept, dus een slechte body is een 422 voordat je functie iets kan opzoeken. Vanaf hoofdstuk 7 vroeg je server ook vóór beide om een token; deze app heeft geen tokens.
Twee statussen uit hoofdstuk 5 komen hier nooit voor. 411 en 413 waren framingwerk in je read_body; TestClient stuurt altijd een lengte, en de app stelt geen limiet aan de grootte.
Een geldig JSON-boek heeft het label text/plain. Hoofdstuk 5 antwoordde 415. Wat antwoordt de app?
PUT /books/9999, waarbij geen boek 9999 bestaat, heeft een kapotte body. Hoofdstuk 5 antwoordde 404. Wat antwoordt de app?
201 en location
De regel boven create_book is @app.post("/books", status_code=201): elke waarde die de functie teruggeeft, gaat de deur uit met status 201. De def-regel vraagt ook om response: Response. FastAPI geeft een responseobject mee, en elke header die je erop zet, wordt naar het antwoord gekopieerd:
response.headers["location"] = f"/books/{book_id}"
Geef het opgeslagen boek terug, en FastAPI stuurt het als JSON-body van de 201, met jouw location. replace_book heeft geen van beide nodig: een vervangen boek is een gewone 200.
Je opdracht
Schrijf de body’s van create_book en replace_book in reading_app.py:
create_book(book, response)neemt het id uitSTATE["next_id"]en telt één op bijSTATE["next_id"], slaat{"id": ..., "title": book.title, "author": book.author}onder dat id op inSTATE["books"], zetlocationop/books/plus het id, en geeft het opgeslagen boek terug.replace_book(book_id, book)werptHTTPException(status_code=404, detail="no such book")op voor een id datSTATE["books"]niet bevat. Anders slaat het het boek op onderbook_id, het id uit de, en geeft het terug.
Schrijf geen eigen controles op title of author: het model heeft dat al gedaan, en een regel van jou die iets weigert wat het model accepteert, zoals een titel met spaties eromheen, zou de app laten verschillen van zijn eigen model. Om één verzoek te proberen in het tabblad Shell, met de importwaarschuwing van de testclient uitgezet:
python -W ignore -c "from fastapi.testclient import TestClient; import reading_app; r = TestClient(reading_app.app).post('/books', json={'title': 'Piranesi', 'author': 'Susanna Clarke'}); print(r.status_code, r.headers['location'], r.json())"
Het drukt 201 /books/3 {'id': 3, 'title': 'Piranesi', 'author': 'Susanna Clarke'} af. Druk daarna op Check my work. Tot nu toe is geen enkele byte van de antwoorden van je app over een
Opdracht
Schrijf de body’s van create_book en replace_book in reading_app.py, onder de regels @app.post en @app.put die er al staan.
create_book(book, response)neemt het nieuwe id uitSTATE["next_id"]en telt één op bijSTATE["next_id"]. Het slaat{"id": ..., "title": book.title, "author": book.author}onder dat id op inSTATE["books"], zetresponse.headers["location"]op/books/gevolgd door het id, en geeft het opgeslagen boek terug. De meegeleverdestatus_code=201maakt van het antwoord een 201.replace_book(book_id, book)werptHTTPException(status_code=404, detail="no such book")op alsSTATE["books"]geen boek met dat id heeft. Anders slaat het{"id": book_id, "title": book.title, "author": book.author}op onderbook_id, het id uit de, en geeft het terug.
book is een BookIn die book.title en book.author, en voeg er geen eigen controles op toe. Laat elke @app-regel, elke def-regel en BookIn zoals ze meekwamen.
In de python -m pytest -q nog steeds je tests, en het commando van één regel python -W ignore -c ... uit de les stuurt één POST.
Druk daarna op Check my work. Het vult STATE via reset_state met boeken die je niet hebt gezien en een next_id van 41, en stuurt daarna POST- en PUT-verzoeken de app in, in hetzelfde proces, met TestClient, met titels en auteurs die je niet hebt gezien. Een nieuw boek moet 201 krijgen, een location die zijn id noemt, en het opgeslagen boek in de body, en next_id moet opschuiven; een PUT moet het id uit de URL houden; een PUT naar een onbekend id moet 404 krijgen. Body’s die het model weigert, moeten FastAPI’s 422 krijgen, of zijn 400 voor bytes die geen BookIn-