Ga naar hoofdinhoud

Wat je server laat zien: de handleiding uitzetten

In les 2 gaf je server elk sessie-id weg, via een endpoint dat op de handleiding stond. Er zijn twee dingen te doen: de handleiding uit, en het endpoint weg. Je begint met het eerste, en ziet dan waarom het tweede nodig is.

Drie adressen uit​

De handleiding staat op drie adressen. /docs ken je uit les 1. /redoc is dezelfde lijst in een andere opmaak, en /openapi.json is de lijst zelf. Alle drie zet je uit in de regel waar je app begint:

app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
  1. Regel 1:

    docs_url is het adres van /docs, redoc_url dat van /redoc, en openapi_url dat van de lijst. Met None maakt FastAPI dat adres niet aan.

Vervang je regel app = FastAPI() door deze. De rest van main.py blijft.

Doe de test​

Maak een script dicht.py dat de vier adressen afloopt:

import httpx

for pad in ["/docs", "/redoc", "/openapi.json", "/sessies"]:
antwoord = httpx.get("http://127.0.0.1:8000" + pad)
print(pad, antwoord.status_code)

Uitvoer:

/docs 404
/redoc 404
/openapi.json 404
/sessies 200

De handleiding is weg: een 404, alsof het adres nooit bestond. Maar /sessies antwoordt nog. Draai sessies.py uit les 2, en je krijgt dezelfde sessie-id's als eerst.

Verstoppen is geen slot​

Wie het pad /sessies kent, heeft de handleiding niet nodig. Hij heeft het bijvoorbeeld eerder gezien, voordat je de handleiding uitzette, of hij probeert een paar voor de hand liggende namen. De lijst uitzetten maakt een gat moeilijker te vinden, maar het gat blijft. Iets beveiligen door het te verstoppen heet security through obscurity, en het houdt niemand tegen die het toch vindt.

Haal daarom het endpoint zelf weg. Verwijder alle_sessies met zijn @app.get("/sessies") uit main.py, en draai dicht.py opnieuw:

/docs 404
/redoc 404
/openapi.json 404
/sessies 404

Nu valt er niets te vinden en niets te gebruiken. Draai je sessies.py nog een keer, dan print het één vreemde regel:

detail {'naam': 'onbekend'}

Het script leest de 404-melding {"detail": "Not Found"} alsof het een lijst met sessies is, en probeert detail als sessie-id. Je server kent dat niet.

Waar de controle zit​

Wat je server laat zien, bepaal je in main.py: met de drie instellingen, en met welke endpoints er staan. De bezoeker kan alleen gebruiken wat er is.

Waar de controle zit: wat je server laat zien
  1. Browser: Een bezoeker opent /docs. Of /openapi.json: daar stond de lijst met al je endpoints. · De handleiding op /docs

  2. Hier zit de controleServer: FastAPI zoekt het pad. Met docs_url, redoc_url en openapi_url op None bestaat de handleiding niet: 404. · De handleiding uitzetten

  3. Browser: Hij probeert een pad dat hij kent. Bijvoorbeeld GET /sessies, van een lijst die hij eerder zag. · Een vergeten endpoint

  4. Hier zit de controleServer: Staat het endpoint in main.py?. Alleen wat er staat, antwoordt. Een weggehaald endpoint geeft een 404. · De handleiding uitzetten

  5. Browser: Hij krijgt twee keer een 404. Er valt niets te vinden en niets te gebruiken.

Er gaat iets mis​

Je hebt de handleiding uitgezet, maar in de terminal van fastapi dev staat nog steeds:

Documentation at http://127.0.0.1:8000/docs

Oorzaak: fastapi dev print die regel altijd bij het starten, ook als je app geen /docs heeft. Hij kijkt niet in je instellingen.

Oplossing: niets, je server doet het goed. Kijk met dicht.py of in de browser wat er echt op /docs staat: een 404 met {"detail":"Not Found"}.

Opdrachten​

Opdracht 1: Predict - Alleen docs_url​

Je zet alleen app = FastAPI(docs_url=None) en draait dicht.py, met /sessies nog in je main.py.

Vraag: welke vier statuscodes print het script?

Tip

Elke instelling zet één adres uit.

Antwoord
/docs 404
/redoc 200
/openapi.json 200
/sessies 200

Alleen /docs is weg. Op /redoc staat dezelfde lijst in een andere opmaak, en /openapi.json is de lijst zelf. Daarom zet je ze alle drie op None.

Opdracht 2: Investigate - Alleen openapi_url​

Probeer nu app = FastAPI(openapi_url=None), zonder de andere twee. Wat geeft dicht.py voor /docs en /redoc, en waarom?

Antwoord

Allebei 404. De pagina's op /docs en /redoc lezen hun lijst uit /openapi.json, zoals je in les 1 zag. Zonder die lijst maakt FastAPI ze niet aan. Zet de andere twee er toch bij: dan ziet wie je main.py leest, dat de handleiding bewust uit staat.

Zo ziet je main.py er nu uit
import secrets
import time

from fastapi import Cookie, FastAPI, Form
from fastapi.responses import JSONResponse
from sqlitedict import SqliteDict

app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)

@app.post("/bericht")
async def bericht_plaatsen(
naam: str = Form(...),
bericht: str = Form(...),
sessie_id: str = Cookie(default=""),
):
if not sessie_id:
sessie_id = secrets.token_hex(16)
sleutel = f"bericht_{time.time_ns()}"
with SqliteDict("gastenboek.db") as db:
db[sleutel] = {"naam": naam, "bericht": bericht}
db.commit()
with SqliteDict("sessies.db") as sessies:
mijn = sessies.get(sessie_id, {"naam": naam, "berichten": []})
mijn["naam"] = naam
mijn["berichten"].append(sleutel)
sessies[sessie_id] = mijn
sessies.commit()
antwoord = JSONResponse({"opgeslagen": sleutel})
antwoord.set_cookie(key="sessie_id", value=sessie_id, httponly=True, samesite="lax")
return antwoord

@app.get("/berichten")
async def berichten():
with SqliteDict("gastenboek.db") as db:
return list(db.values())

@app.get("/ik")
async def wie_ben_ik(sessie_id: str = Cookie(default="")):
with SqliteDict("sessies.db") as sessies:
mijn = sessies.get(sessie_id, {})
return {"naam": mijn.get("naam", "onbekend")}

De handleiding staat uit, en GET /sessies is weg. In de volgende lessen verandert main.py niet meer: die gaan over de bestanden naast je code.

Door naar les 4: wat er in je map staat.