Invoer controleren: grenzen in Form
Hier bouw je op verder: Geneste dictionaries
Weet je dit niet meer zeker? Lees het terug voordat je verdergaat.
- Python Geneste dictionaries
De grens hoort op de server, waar de bezoeker er niet bij kan. Je zou daarvoor
zelf een if met len kunnen schrijven; wie de uitbreiding JavaScript deed,
heeft dat in Server of browser? al gedaan.
FastAPI kan het ook voor je doen: je zet de grens in Form, en een verzoek dat
eroverheen gaat, komt niet eens in je functie.
max_length in Form
@app.post("/bericht")
async def bericht_plaatsen(
naam: str = Form(..., max_length=30),
bericht: str = Form(..., max_length=280),
):
with SqliteDict("gastenboek.db") as db:
db[f"bericht_{time.time_ns()}"] = {"naam": naam, "bericht": bericht}
db.commit()
return {"lengte": len(bericht)}
- Regel 2-5:
De parameters staan nu onder elkaar, want ze zijn te lang voor één regel.
- Regel 3-4:
max_lengthzet de grens: hoogstens 30 tekens voor de naam, 280 voor het bericht. Dezelfde getallen als demaxlengthin de HTML, maar nu telt hij.
Vervang bericht_plaatsen in je main.py door deze. De rest blijft gelijk.
Doe de test opnieuw
Het antwoord op een te lang bericht bevat de hele invoer nog een keer, dus ook
de miljoen letters a. Vervang daarom in groot.py de regel
print(antwoord.json()) door twee regels die alleen de statuscode en de
melding printen. Het bericht blijft een miljoen tekens:
import httpx
adres = "http://127.0.0.1:8000/bericht"
bericht = "a" * 1_000_000
antwoord = httpx.post(adres, data={"naam": "Sara", "bericht": bericht})
print(antwoord.status_code)
print(antwoord.json()["detail"][0]["msg"])
- Regel 8:
Het antwoord is een dictionary met onder
detaileen lijst, met één fout per veld.[0]pakt de eerste fout uit die lijst, en["msg"]de melding daarin. Wat er verder in staat, zie je in les 4.
Draai het met python groot.py.
Een 422 betekent: het verzoek klopt niet met wat het endpoint verwacht. Het
bericht komt niet in de database, en je eigen code draait niet eens. De
maxlength in de HTML laat je staan: die helpt nog steeds wie zich in de
browser vergist.
Waar de controle zit
Een verzoek komt nu langs twee controles, allebei op de server. De grens in Form ken je nu; de controle op de inhoud komt in les 5.
Browser: Je vult het formulier in. maxlength houdt je tegen, maar alleen in de browser. · maxlength is geen controle
Browser: Een script slaat de browser over. httpx stuurt hetzelfde verzoek, zonder maxlength en zonder formulier. · Een bezoeker die onthoudt
Hier zit de controleServer: FastAPI leest het formulier. max_length in Form: een te lang of leeg veld geeft een 422. · Grenzen in Form
Hier zit de controleServer: Jouw functie controleert de inhoud. Wat max_length niet ziet, controleer je zelf, in je eigen code. · De inhoud controleren
Server: Pas dan de database in. Wat hier aankomt, heeft beide controles gehad.
Browser: Het antwoord komt terug. Een 200, of een 422 of 400 met in detail wat er mis is. · Een 422 lezen
Er gaat iets mis
Je draait het oude groot.py, met print(antwoord.json()), en je terminal
loopt vol met letters a.
Oorzaak: bij een 422 stuurt FastAPI in input terug wat er binnenkwam.
Bij een miljoen tekens zijn dat een miljoen letters a.
Oplossing: print alleen wat je nodig hebt, zoals de statuscode en
antwoord.json()["detail"][0]["msg"]. Stop een script dat blijft printen
met Ctrl+C.
Opdrachten
Opdracht 1: Run - Precies op de grens
Stuur een bericht van 280 tekens en een van 281. Welke statuscodes krijg je?
Tip
"a" * 280 maakt 280 letters.
Antwoord
import httpx
adres = "http://127.0.0.1:8000/bericht"
for lengte in [280, 281]:
antwoord = httpx.post(adres, data={"naam": "Sara", "bericht": "a" * lengte})
print(lengte, antwoord.status_code)
Uitvoer:
280 200
281 422
max_length=280 betekent: 280 mag nog, 281 niet.
Opdracht 2: Predict - Een lange naam
Je stuurt een naam van 31 tekens en het bericht hoi.
Vraag: welke statuscode en welke melding krijg je?
Antwoord
422, met de melding String should have at most 30 characters. De grens op
de naam werkt net zo als die op het bericht.
Zo ziet je main.py er nu uit
import time
from fastapi import FastAPI, Form
from fastapi.staticfiles import StaticFiles
from sqlitedict import SqliteDict
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.post("/bericht")
async def bericht_plaatsen(
naam: str = Form(..., max_length=30),
bericht: str = Form(..., max_length=280),
):
with SqliteDict("gastenboek.db") as db:
db[f"bericht_{time.time_ns()}"] = {"naam": naam, "bericht": bericht}
db.commit()
return {"lengte": len(bericht)}
Beide velden hebben nu een grens op de server.
Door naar les 4: een 422 lezen.