Ga naar hoofdinhoud

Invoer controleren: grenzen in Form

Hier bouw je op verder: Geneste dictionaries

Weet je dit niet meer zeker? Lees het terug voordat je verdergaat.

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)}
  1. Regel 2-5:

    De parameters staan nu onder elkaar, want ze zijn te lang voor één regel.

  2. Regel 3-4:

    max_length zet de grens: hoogstens 30 tekens voor de naam, 280 voor het bericht. Dezelfde getallen als de maxlength in 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"])
  1. Regel 8:

    Het antwoord is een dictionary met onder detail een 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.

Waar de controle zit: invoer
  1. Browser: Je vult het formulier in. maxlength houdt je tegen, maar alleen in de browser. · maxlength is geen controle

  2. Browser: Een script slaat de browser over. httpx stuurt hetzelfde verzoek, zonder maxlength en zonder formulier. · Een bezoeker die onthoudt

  3. Hier zit de controleServer: FastAPI leest het formulier. max_length in Form: een te lang of leeg veld geeft een 422. · Grenzen in Form

  4. Hier zit de controleServer: Jouw functie controleert de inhoud. Wat max_length niet ziet, controleer je zelf, in je eigen code. · De inhoud controleren

  5. Server: Pas dan de database in. Wat hier aankomt, heeft beide controles gehad.

  6. Browser: Het antwoord komt terug. Een 200, of een 422 of 400 met in detail wat er mis is. · Een 422 lezen

Controlevraag
Je haalt maxlength weg uit formulier.html, en laat max_length=280 in Form staan. Iemand typt in het formulier 300 tekens en klikt op Verstuur. Wat gebeurt er?
Kies wat jij denkt. Bij het goede antwoord zie je de uitleg.

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.