committed by
GitHub
1 changed files with 109 additions and 0 deletions
@ -0,0 +1,109 @@ |
|||
# Benutzerdefinierte Request- und APIRoute-Klasse |
|||
|
|||
In einigen Fällen möchten Sie möglicherweise die von den Klassen `Request` und `APIRoute` verwendete Logik überschreiben. |
|||
|
|||
Das kann insbesondere eine gute Alternative zur Logik in einer Middleware sein. |
|||
|
|||
Wenn Sie beispielsweise den Requestbody lesen oder manipulieren möchten, bevor er von Ihrer Anwendung verarbeitet wird. |
|||
|
|||
!!! danger "Gefahr" |
|||
Dies ist eine „fortgeschrittene“ Funktion. |
|||
|
|||
Wenn Sie gerade erst mit **FastAPI** beginnen, möchten Sie diesen Abschnitt vielleicht überspringen. |
|||
|
|||
## Anwendungsfälle |
|||
|
|||
Einige Anwendungsfälle sind: |
|||
|
|||
* Konvertieren von Nicht-JSON-Requestbodys nach JSON (z. B. <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>). |
|||
* Dekomprimierung gzip-komprimierter Requestbodys. |
|||
* Automatisches Loggen aller Requestbodys. |
|||
|
|||
## Handhaben von benutzerdefinierten Requestbody-Kodierungen |
|||
|
|||
Sehen wir uns an, wie Sie eine benutzerdefinierte `Request`-Unterklasse verwenden, um gzip-Requests zu dekomprimieren. |
|||
|
|||
Und eine `APIRoute`-Unterklasse zur Verwendung dieser benutzerdefinierten Requestklasse. |
|||
|
|||
### Eine benutzerdefinierte `GzipRequest`-Klasse erstellen |
|||
|
|||
!!! tip "Tipp" |
|||
Dies ist nur ein einfaches Beispiel, um zu demonstrieren, wie es funktioniert. Wenn Sie Gzip-Unterstützung benötigen, können Sie die bereitgestellte [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank} verwenden. |
|||
|
|||
Zuerst erstellen wir eine `GzipRequest`-Klasse, welche die Methode `Request.body()` überschreibt, um den Body bei Vorhandensein eines entsprechenden Headers zu dekomprimieren. |
|||
|
|||
Wenn der Header kein `gzip` enthält, wird nicht versucht, den Body zu dekomprimieren. |
|||
|
|||
Auf diese Weise kann dieselbe Routenklasse gzip-komprimierte oder unkomprimierte Requests verarbeiten. |
|||
|
|||
```Python hl_lines="8-15" |
|||
{!../../../docs_src/custom_request_and_route/tutorial001.py!} |
|||
``` |
|||
|
|||
### Eine benutzerdefinierte `GzipRoute`-Klasse erstellen |
|||
|
|||
Als Nächstes erstellen wir eine benutzerdefinierte Unterklasse von `fastapi.routing.APIRoute`, welche `GzipRequest` nutzt. |
|||
|
|||
Dieses Mal wird die Methode `APIRoute.get_route_handler()` überschrieben. |
|||
|
|||
Diese Methode gibt eine Funktion zurück. Und diese Funktion empfängt einen Request und gibt eine Response zurück. |
|||
|
|||
Hier verwenden wir sie, um aus dem ursprünglichen Request einen `GzipRequest` zu erstellen. |
|||
|
|||
```Python hl_lines="18-26" |
|||
{!../../../docs_src/custom_request_and_route/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "Technische Details" |
|||
Ein `Request` hat ein `request.scope`-Attribut, welches einfach ein Python-`dict` ist, welches die mit dem Request verbundenen Metadaten enthält. |
|||
|
|||
Ein `Request` hat auch ein `request.receive`, welches eine Funktion ist, die den Hauptteil des Requests empfängt. |
|||
|
|||
Das `scope`-`dict` und die `receive`-Funktion sind beide Teil der ASGI-Spezifikation. |
|||
|
|||
Und diese beiden Dinge, `scope` und `receive`, werden benötigt, um eine neue `Request`-Instanz zu erstellen. |
|||
|
|||
Um mehr über den `Request` zu erfahren, schauen Sie sich <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">Starlettes Dokumentation zu Requests</a> an. |
|||
|
|||
Das Einzige, was die von `GzipRequest.get_route_handler` zurückgegebene Funktion anders macht, ist die Konvertierung von `Request` in ein `GzipRequest`. |
|||
|
|||
Dabei kümmert sich unser `GzipRequest` um die Dekomprimierung der Daten (falls erforderlich), bevor diese an unsere *Pfadoperationen* weitergegeben werden. |
|||
|
|||
Danach ist die gesamte Verarbeitungslogik dieselbe. |
|||
|
|||
Aufgrund unserer Änderungen in `GzipRequest.body` wird der Requestbody jedoch bei Bedarf automatisch dekomprimiert, wenn er von **FastAPI** geladen wird. |
|||
|
|||
## Zugriff auf den Requestbody in einem Exceptionhandler |
|||
|
|||
!!! tip "Tipp" |
|||
Um dasselbe Problem zu lösen, ist es wahrscheinlich viel einfacher, den `body` in einem benutzerdefinierten Handler für `RequestValidationError` zu verwenden ([Fehlerbehandlung](../tutorial/handling-errors.md#den-requestvalidationerror-body-verwenden){.internal-link target=_blank}). |
|||
|
|||
Dieses Beispiel ist jedoch immer noch gültig und zeigt, wie mit den internen Komponenten interagiert wird. |
|||
|
|||
Wir können denselben Ansatz auch verwenden, um in einem Exceptionhandler auf den Requestbody zuzugreifen. |
|||
|
|||
Alles, was wir tun müssen, ist, den Request innerhalb eines `try`/`except`-Blocks zu handhaben: |
|||
|
|||
```Python hl_lines="13 15" |
|||
{!../../../docs_src/custom_request_and_route/tutorial002.py!} |
|||
``` |
|||
|
|||
Wenn eine Exception auftritt, befindet sich die `Request`-Instanz weiterhin im Gültigkeitsbereich, sodass wir den Requestbody lesen und bei der Fehlerbehandlung verwenden können: |
|||
|
|||
```Python hl_lines="16-18" |
|||
{!../../../docs_src/custom_request_and_route/tutorial002.py!} |
|||
``` |
|||
|
|||
## Benutzerdefinierte `APIRoute`-Klasse in einem Router |
|||
|
|||
Sie können auch den Parameter `route_class` eines `APIRouter` festlegen: |
|||
|
|||
```Python hl_lines="26" |
|||
{!../../../docs_src/custom_request_and_route/tutorial003.py!} |
|||
``` |
|||
|
|||
In diesem Beispiel verwenden die *Pfadoperationen* unter dem `router` die benutzerdefinierte `TimedRoute`-Klasse und haben in der Response einen zusätzlichen `X-Response-Time`-Header mit der Zeit, die zum Generieren der Response benötigt wurde: |
|||
|
|||
```Python hl_lines="13-20" |
|||
{!../../../docs_src/custom_request_and_route/tutorial003.py!} |
|||
``` |
Loading…
Reference in new issue