Pure Python Reimplementierung von FastAPI von Scratch
Ethan Miller
Product Engineer · Leapcell

Implementierung einer FastAPI-ähnlichen Routing-Struktur von WSGI
Im Bereich der Python-Webentwicklung wird FastAPI von Entwicklern aufgrund seines effizienten und prägnanten Routing-Designs sowie seiner leistungsstarken Funktionalität sehr geschätzt. FastAPI basiert auf dem ASGI-Protokoll (Asynchronous Server Gateway Interface), das sich vom traditionellen WSGI (Web Server Gateway Interface) unterscheidet. In diesem Artikel wird untersucht, wie man von WSGI ausgehend ein ähnliches Routing-Schema wie FastAPI implementiert, während gleichzeitig Schlüsselkonzepte wie WSGI und Uvicorn und ihre Zusammenhänge eingehend analysiert werden.
I. Konzeptuelle Analyse von WSGI, ASGI und Uvicorn
1.1 WSGI: Web Server Gateway Interface
WSGI ist eine Standardschnittstelle in der Python-Webentwicklung, die die Kommunikationsspezifikation zwischen einem Webserver und einer Python-Webanwendung definiert. Sein Aufkommen löst das Kompatibilitätsproblem zwischen verschiedenen Webservern und Webframeworks und ermöglicht es Entwicklern, Webserver und Frameworks frei zu wählen, ohne sich Sorgen machen zu müssen, dass sie nicht zusammenarbeiten können.
WSGI abstrahiert eine Webanwendung als ein aufrufbares Objekt, das zwei Parameter akzeptiert: ein Dictionary mit Anforderungsinformationen (environ
) und eine Callback-Funktion zum Senden von Antwortstatuscodes und Header-Informationen (start_response
). Zum Beispiel kann eine einfache WSGI-Anwendung wie folgt geschrieben werden:
def simple_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [b'Hallo, Welt!']
In diesem Beispiel enthält environ
anforderungsbezogene Informationen wie die Anforderungsmethode, URL und HTTP-Header; die Funktion start_response
wird verwendet, um den Antwortstatuscode und die Header-Informationen festzulegen; und der Rückgabewert der Funktion ist der Antworttext.
1.2 ASGI: Asynchronous Server Gateway Interface
Mit dem Aufstieg der asynchronen Programmierung in Python, insbesondere der weitverbreiteten Verwendung der asyncio
-Bibliothek, war das traditionelle WSGI nicht mehr in der Lage, asynchrone I/O-Operationen zu bewältigen. ASGI entstand. Als Erweiterung von WSGI unterstützt es nicht nur synchrone Anwendungen, sondern kann auch asynchrone Anforderungen effizient bearbeiten und erfüllt so die Anforderungen moderner Webanwendungen an hohe Leistung und hohe Parallelität.
ASGI definiert auch das Kommunikationsprotokoll zwischen dem Server und der Anwendung, aber sein aufrufbares Anwendungsobjekt unterstützt asynchrone Methoden. Ein einfaches ASGI-Anwendungsbeispiel ist wie folgt:
async def simple_asgi_app(scope, receive, send): assert scope['type'] == 'http' await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ (b'content-type', b'text/plain') ] }) await send({ 'type': 'http.response.body', 'body': b'Hallo, ASGI!' })
Hier enthält scope
Informationen wie den Anforderungstyp (z. B. http
); receive
wird verwendet, um vom Client gesendete Daten zu empfangen; und send
wird verwendet, um Antwortdaten an den Client zu senden, wobei der gesamte Prozess asynchrone Operationen unterstützt.
1.3 Uvicorn: ASGI-Server
Uvicorn ist ein hochleistungsfähiger ASGI-Server, der auf Python basiert und ASGI-Anwendungen ausführen kann und auch mit WSGI-Anwendungen kompatibel ist. Uvicorn verwendet Bibliotheken wie uvloop
und httptools
, um effiziente Ereignisschleifen und HTTP-Parsing zu implementieren, wodurch die asynchronen Vorteile von ASGI voll ausgeschöpft und eine hervorragende Leistung erzielt werden kann.
In der tatsächlichen Entwicklung können wir Uvicorn verwenden, um ASGI- oder WSGI-Anwendungen zu starten. Zum Beispiel, um die obige ASGI-Anwendung zu starten:
uvicorn main:app --reload
Hier ist main
der Name des Python-Moduls, das den Anwendungscode enthält, app
ist das im Modul definierte ASGI-Anwendungsobjekt, und der Parameter --reload
wird verwendet, um die Anwendung automatisch neu zu laden, wenn sich der Code ändert.
II. Implementierung eines FastAPI-ähnlichen Routing-Schemas von WSGI
2.1 Grundprinzipien des Routing-Systems
Das Routing-System von FastAPI bindet Funktionen über Dekoratoren an bestimmte URL-Pfade. Wenn eine Anfrage empfangen wird, die mit dem Pfad übereinstimmt, wird die entsprechende Verarbeitungsfunktion aufgerufen und eine Antwort zurückgegeben. Die Kernidee der Implementierung eines ähnlichen Routing-Systems in WSGI besteht darin, die entsprechende Verarbeitungsfunktion basierend auf dem URL-Pfad der Anfrage zu finden und sie aufzurufen, um eine Antwort zu generieren.
Wir können eine Routing-Tabelle definieren, um die Zuordnung zwischen URL-Pfaden und Verarbeitungsfunktionen zu speichern. Wenn eine Anfrage eintrifft, wird der übereinstimmende Pfad in der Routing-Tabelle gefunden, und dann wird die entsprechende Verarbeitungsfunktion aufgerufen, um eine Antwort zu generieren.
2.2 Implementierung eines einfachen WSGI-Routing-Systems
Nachfolgend werden wir schrittweise ein einfaches WSGI-Routing-System implementieren, um die Routing-Funktionen von FastAPI zu simulieren.
Definieren Sie zunächst eine leere Routing-Tabelle:
route_table = {}
Erstellen Sie als Nächstes einen Dekorator, um Funktionen an URL-Pfade zu binden und sie der Routing-Tabelle hinzuzufügen:
def route(path): def decorator(func): route_table[path] = func return func return decorator
Implementieren Sie dann eine WSGI-Anwendung, um die entsprechende Verarbeitungsfunktion basierend auf dem Anforderungspfad zu finden und aufzurufen:
def wsgi_app(environ, start_response): path = environ.get('PATH_INFO', '/') if path in route_table: response_body = route_table[path]() status = '200 OK' else: response_body = [b'404 Not Found'] status = '404 Not Found' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return response_body
Jetzt können wir den definierten Routing-Dekorator verwenden, um Verarbeitungsfunktionen zu definieren:
@route('/') def index(): return [b'Willkommen auf der Indexseite!'] @route('/about') def about(): return [b'Dies ist die Über-uns-Seite.']
Verwenden Sie abschließend Uvicorn, um diese WSGI-Anwendung auszuführen:
uvicorn main:wsgi_app --reload
Durch die obigen Schritte haben wir ein einfaches WSGI-Routing-System implementiert, das entsprechend unterschiedlichen URL-Pfaden entsprechende Inhalte zurückgeben kann und so zunächst die Routing-Funktionen von FastAPI simuliert.
2.3 Verbesserung des Routing-Systems
Das obige Routing-System ist nur eine Basisversion und weist viele Mängel auf. Es unterstützt beispielsweise kein dynamisches Routing (z. B. parametrisierte Pfade) oder die Verarbeitung von Anforderungsmethoden. Nachfolgend werden wir es verbessern.
Unterstützung von dynamischem Routing
Um dynamisches Routing zu unterstützen, können wir reguläre Ausdrücke verwenden, um Pfade abzugleichen und Parameter aus Pfaden zu extrahieren. Importieren Sie das re
-Modul und ändern Sie die Routing-Tabelle und den Routing-Dekorator:
import re route_table = {} def route(path): def decorator(func): route_table[path] = func return func return decorator def dynamic_route(path_pattern): def decorator(func): route_table[path_pattern] = func return func return decorator
Ändern Sie gleichzeitig die WSGI-Anwendung, um dynamisches Routing zu verarbeiten:
def wsgi_app(environ, start_response): path = environ.get('PATH_INFO', '/') for pattern, handler in route_table.items(): if isinstance(pattern, str): if path == pattern: response_body = handler() status = '200 OK' break else: match = re.match(pattern, path) if match: args = match.groups() response_body = handler(*args) status = '200 OK' break else: response_body = [b'404 Not Found'] status = '404 Not Found' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return response_body
Jetzt können wir dynamische Routing-Verarbeitungsfunktionen definieren:
@dynamic_route(r'/user/(\d+)') def user_detail(user_id): return [f'Benutzer {user_id} Detailseite.'.encode()]
Unterstützung von Anforderungsmethoden
Um verschiedene Anforderungsmethoden (wie GET und POST) zu unterstützen, können wir die Verarbeitungsfunktionen für verschiedene Anforderungsmethoden, die jedem Pfad entsprechen, in der Routing-Tabelle speichern. Ändern Sie die Routing-Tabelle und den Routing-Dekorator wie folgt:
route_table = {} def route(path, methods=['GET']): def decorator(func): if path not in route_table: route_table[path] = {} for method in methods: route_table[path][method] = func return func return decorator
Ändern Sie gleichzeitig die WSGI-Anwendung, um die entsprechende Verarbeitungsfunktion basierend auf der Anforderungsmethode aufzurufen:
def wsgi_app(environ, start_response): path = environ.get('PATH_INFO', '/') method = environ.get('REQUEST_METHOD', 'GET') if path in route_table and method in route_table[path]: response_body = route_table[path][method]() status = '200 OK' else: response_body = [b'404 Not Found'] status = '404 Not Found' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return response_body
Jetzt können wir Verarbeitungsfunktionen definieren, die verschiedene Anforderungsmethoden unterstützen:
@route('/login', methods=['GET', 'POST']) def login(): return [b'Login-Seite.']
Durch die obigen Verbesserungen ist unser WSGI-Routing-System vollständiger geworden und kann dynamisches Routing und verschiedene Anforderungsmethoden unterstützen, wodurch es sich den Routing-Funktionen von FastAPI weiter annähert.
III. Zusammenfassung und Ausblick
Dieser Artikel beginnt mit den grundlegenden Konzepten von WSGI, führt relevantes Wissen über ASGI und Uvicorn ein und simuliert die Routing-Funktionen von FastAPI, indem er schrittweise ein einfaches WSGI-Routing-System implementiert. Durch diesen Prozess haben wir ein tiefes Verständnis des Kommunikationsprotokolls zwischen Webservern und Anwendungen sowie der Kernprinzipien von Routing-Systemen gewonnen.
Obwohl das von uns implementierte WSGI-Routing-System noch weit von FastAPI entfernt ist, bietet es uns eine klare Vorstellung, um die Implementierung von Routing-Systemen zu verstehen. In Zukunft können wir dieses Routing-System weiter verbessern, z. B. Funktionen zum Parsen von Anforderungstexten und Serialisieren von Antworttexten hinzufügen, um es Web-Frameworks, die in tatsächlichen Produktionsumgebungen verwendet werden, näher zu bringen. Gleichzeitig können wir auch untersuchen, wie wir dieses Routing-System auf die ASGI-Umgebung erweitern können, um die Vorteile der asynchronen Programmierung voll auszuschöpfen und die Leistungs- und Parallelverarbeitungsfähigkeiten von Anwendungen zu verbessern.
Der obige Inhalt beschreibt detailliert den Prozess der Implementierung eines Routing-Schemas mit WSGI. Wenn Sie der Meinung sind, dass bestimmte Teile ausführlicher behandelt werden müssen oder neue Funktionen hinzugefügt werden sollen, können Sie mich dies gerne wissen lassen.
Leapcell: Das Beste aus Serverless Web Hosting
Empfohlen als die beste Plattform für die Bereitstellung von Python-Diensten: Leapcell
🚀 Entwickeln Sie mit Ihrer Lieblingssprache
Entwickeln Sie mühelos in JavaScript, Python, Go oder Rust.
🌍 Stellen Sie unbegrenzt Projekte kostenlos bereit
Zahlen Sie nur für das, was Sie nutzen – keine Anfragen, keine Gebühren.
⚡ Pay-as-You-Go, keine versteckten Kosten
Keine Leerlaufgebühren, nur nahtlose Skalierbarkeit.
📖 Entdecken Sie unsere Dokumentation
🔹 Folgen Sie uns auf Twitter: @LeapcellHQ