FastAPIs Säulen verstehen durch Starlette
Emily Parker
Product Engineer · Leapcell

FastAPI hat sich schnell zu einem der beliebtesten Web-Frameworks im Python-Ökosystem entwickelt und wird für seine unglaubliche Geschwindigkeit, automatische Datenvalidierung und nahtlose OpenAPI-Integration gelobt. Entwickler bestaunen oft seine elegante API und beeindruckende Leistung, aber nur wenige verstehen wirklich die zugrunde liegende Architektur, die all dies möglich macht. Die Geheimzutat, oder besser gesagt, die leistungsstarke Grundlage, ist Starlette. Starlette ist ein leichtgewichtiges ASGI-Framework/Toolkit, das für die Erstellung von hochperformanten asynchronen Diensten entwickelt wurde und das Fundament bildet, auf dem FastAPI aufbaut. Das Verständnis der Kernkomponenten von Starlette – nämlich Request, Response, Routing und Middleware – ist entscheidend für jeden, der das volle Potenzial von FastAPI ausschöpfen, komplexe Probleme beheben oder sogar zu seiner Entwicklung beitragen möchte. Dieser tiefgehende Einblick wird die wesentlichen Mechanismen beleuchten, die FastAPIs robusten Fähigkeiten ermöglichen und uns vom einfachen Benutzen von FastAPI zum wirklichen Verstehen seiner leistungsstarken Grundlage führen.
Die Kernkomponenten von Starlette
Bevor wir uns den praktischen Aspekten zuwenden, definieren wir die grundlegenden Konzepte, die Starlette und damit auch FastAPI zugrunde liegen.
- ASGI (Asynchronous Server Gateway Interface): Dies ist eine Spezifikation für eine gemeinsame Schnittstelle zwischen asynchronen Python-Webservern und asynchronen Python-Webanwendungen. Starlette ist ein ASGI-Framework, was bedeutet, dass es diese Spezifikation einhält und nahtlos mit ASGI-Servern wie Uvicorn oder Hypercorn zusammenarbeitet.
 - Request: Im Kontext einer Webanwendung kapselt ein Request-Objekt alle Informationen, die von einem Client (z. B. einem Webbrowser) an den Server gesendet werden. Dazu gehören HTTP-Header, URL-Parameter, Query-Strings, der Body-Inhalt (wie JSON oder Formulardaten) und die HTTP-Methode.
 - Response: Umgekehrt repräsentiert ein Response-Objekt die Daten, die vom Server an den Client zurückgesendet werden. Es enthält den HTTP-Statuscode, die Header und den eigentlichen Inhalt (z. B. HTML, JSON oder eine Datei).
 - Routing: Dies ist der Prozess der Zuordnung eingehender HTTP-Anfragen zu bestimmten Handler-Funktionen oder Views innerhalb der Anwendung. Basierend auf dem URL-Pfad und der HTTP-Methode der Anfrage bestimmt der Router, welcher Code ausgeführt werden soll.
 - Middleware: Middleware-Komponenten sind Funktionen oder Klassen, die sich zwischen dem Server und Ihrer Anwendung befinden. Sie können eingehende Anfragen und ausgehende Antworten abfangen und es Ihnen ermöglichen, häufige Aufgaben wie Authentifizierung, Protokollierung, Fehlerbehandlung oder das Hinzufügen von Headern auszuführen, ohne Ihre Hauptanwendungslogik zu überladen.
 
Request-Handling
Das Request-Objekt in Starlette ist ein umfassendes Werkzeug für den Zugriff auf alle Aspekte einer eingehenden Client-Anfrage. Es bietet asynchrone Methoden zum Lesen des Anfrage-Bodys, was es effizient für die Verarbeitung großer Datenströme macht.
# main.py from starlette.applications import Starlette from starlette.routing import Route from starlette.responses import JSONResponse import uvicorn async def homepage(request): # Zugriff auf Query-Parameter name = request.query_params.get("name", "World") # Zugriff auf Header user_agent = request.headers.get("user-agent", "Unknown") return JSONResponse({"message": f"Hello, {name}!", "user_agent": user_agent}) async def submit_data(request): if request.method == "POST": # Asynchrones Parsen des JSON-Bodys data = await request.json() return JSONResponse({"received_data": data, "message": "Data processed!"}) return JSONResponse({"message": "Please send a POST request with JSON data."}, status_code=400) routes = [ Route("/", homepage), Route("/submit", submit_data, methods=["POST"]), ] app = Starlette(routes=routes) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)
In diesem Beispiel zeigt homepage das Abrufen von Query-Parametern und Headern. Die Funktion submit_data zeigt, wie der JSON-Anfrage-Body mit await request.json() asynchron geparst wird. FastAPI nutzt Starlettes Request-Objekt, abstrahiert aber bequem viel von diesem manuellen Parsing durch Pydantic-Modelle in den Argumenten von Routenfunktionen, wodurch Daten automatisch validiert und deserialisiert werden.
Response-Generierung
Starlette bietet eine Reihe von Response-Klassen für verschiedene Inhaltstypen, die korrekte Content-Type-Header und eine effiziente Daten-Serialisierung gewährleisten. Gängige Typen sind JSONResponse, HTMLResponse, PlainTextResponse, RedirectResponse, StreamingResponse und FileResponse.
# main.py (fortgesetzt vom vorherigen Beispiel) from starlette.responses import HTMLResponse, PlainTextResponse, RedirectResponse async def html_page(request): content = "<h1>Welcome to Starlette!</h1><p>This is an HTML response.</p>" return HTMLResponse(content) async def plain_text(request): return PlainTextResponse("This is a plain text response.") async def redirect_example(request): return RedirectResponse(url="/", status_code=302) routes = [ Route("/", homepage), Route("/submit", submit_data, methods=["POST"]), Route("/html", html_page), Route("/text", plain_text), Route("/redirect", redirect_example), ] app = Starlette(routes=routes)
Hier haben wir Routen hinzugefügt, die HTMLResponse, PlainTextResponse und RedirectResponse demonstrieren. Wenn Sie ein Pydantic-Modell oder ein Dictionary aus einer FastAPI-Route zurückgeben, verwendet FastAPI intern Starlettes JSONResponse, um die Daten in JSON zu serialisieren.
Routing-Mechanismus
Das Routing von Starlette ist deklarativ und leistungsstark und ermöglicht es Ihnen, URL-Pfade zu definieren, die bestimmten asynchronen Funktionen zugeordnet werden. Es unterstützt Pfadparameter, die dynamische Segmente innerhalb der URL sind.
# main.py (fortgesetzt) async def user_profile(request): user_id = request.path_params["user_id"] return JSONResponse({"user_id": user_id, "message": f"Fetching profile for user {user_id}"}) routes = [ Route("/", homepage), Route("/submit", submit_data, methods=["POST"]), Route("/html", html_page), Route("/text", plain_text), Route("/redirect", redirect_example), Route("/users/{user_id}", user_profile), # Pfadparameter ] app = Starlette(routes=routes)
Die Zeile Route("/users/{user_id}", user_profile) definiert eine Route mit einem Pfadparameter {user_id}. Dieser Wert ist dann über request.path_params["user_id"] zugänglich. FastAPI vereinfacht dies weiter, indem es Pfadparameter direkt in Ihrer Funktionssignatur deklarieren lässt (z. B. def read_user(user_id: int):).
Middleware für übergreifende Anliegen
Middleware ist dort, wo sich die Leistungsfähigkeit von Starlette für die Behandlung übergreifender Anliegen zeigt. Sie umschließt Ihre Anwendung und ermöglicht die Vorverarbeitung von Anfragen und die Nachbearbeitung von Antworten. Starlette bietet mehrere integrierte Middleware-Komponenten, wie z. B. CORSMiddleware, GZipMiddleware und Middleware (für benutzerdefinierte Middleware).
# main.py from starlette.middleware import Middleware from starlette.middleware.cors import CORSMiddleware from starlette.middleware.gzip import GZipMiddleware import time # Benutzerdefinierte Middleware async def custom_middleware(request, call_next): start_time = time.time() response = await call_next(request) process_time = time.time() - start_time response.headers["X-Process-Time"] = str(process_time) print(f"Request to {request.url.path} processed in {process_time:.4f} seconds") return response middleware = [ Middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]), Middleware(GZipMiddleware, minimum_size=1000), # Gzip-Antwort, wenn die Inhaltgröße > 1KB ist Middleware(custom_middleware), # Unsere benutzerdefinierte Protokollierungs-/Zeitplanung-Middleware ] app = Starlette(routes=routes, middleware=middleware)
Hier haben wir drei Middleware-Komponenten verkettet: CORSMiddleware für die Behandlung von Cross-Origin Resource Sharing, GZipMiddleware für die automatische Komprimierung von Antworten und eine custom_middleware für die Zeitmessung von Anfragen. Jede Middleware erhält das request-Objekt und eine call_next-Funktion. Durch den Aufruf von await call_next(request) wird die Kontrolle an die nächste Middleware oder die Anwendung selbst übergeben. Die Antwort von der call_next-Funktion kann dann modifiziert werden, bevor sie zurückgesendet wird. FastAPI stellt eine ähnliche app.add_middleware()-Schnittstelle bereit und nutzt transparent das Middleware-System von Starlette.
Fazit
Starlette bietet eine robuste, asynchrone Grundlage, die FastAPI seine hohe Leistung und Modularität verleiht. Durch das Verständnis seiner Request- und Response-Objekte, des deklarativen Routing und des vielseitigen Middleware-Systems erhalten Entwickler tiefere Einblicke in die Art und Weise, wie FastAPI Anfragen verarbeitet, Antworten generiert und gängige Webanwendungsanliegen behandelt. Dieses grundlegende Wissen ist der Schlüssel zum Erstellen effizienterer, wartungsfähigerer und leistungsfähigerer Webdienste mit FastAPI, sodass Sie die zugrunde liegenden Mechanismen wirklich nutzen können, anstatt nur die Fassade zu verwenden. Letztendlich bietet Starlette die leistungsstarken, asynchronen Primitiven, die FastAPI zum Besten aus beiden Welten machen: eine entwicklerfreundliche Erfahrung, die auf einem blitzschnellen Kern aufbaut.