Konvertieren von Nicht-JSON-Requestbodys nach JSON (z. B. msgpack).
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¶
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 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.
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¶
Tipp
Um dasselbe Problem zu lösen, ist es wahrscheinlich viel einfacher, den body in einem benutzerdefinierten Handler für RequestValidationError zu verwenden (Fehlerbehandlung).
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:
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:
Benutzerdefinierte APIRoute-Klasse in einem Router¶
Sie können auch den Parameter route_class eines APIRouter festlegen:
importtimefromtypingimportCallablefromfastapiimportAPIRouter,FastAPI,Request,Responsefromfastapi.routingimportAPIRouteclassTimedRoute(APIRoute):defget_route_handler(self)->Callable:original_route_handler=super().get_route_handler()asyncdefcustom_route_handler(request:Request)->Response:before=time.time()response:Response=awaitoriginal_route_handler(request)duration=time.time()-beforeresponse.headers["X-Response-Time"]=str(duration)print(f"route duration: {duration}")print(f"route response: {response}")print(f"route response headers: {response.headers}")returnresponsereturncustom_route_handlerapp=FastAPI()router=APIRouter(route_class=TimedRoute)@app.get("/")asyncdefnot_timed():return{"message":"Not timed"}@router.get("/timed")asyncdeftimed():return{"message":"It's the time of my life"}app.include_router(router)
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:
importtimefromtypingimportCallablefromfastapiimportAPIRouter,FastAPI,Request,Responsefromfastapi.routingimportAPIRouteclassTimedRoute(APIRoute):defget_route_handler(self)->Callable:original_route_handler=super().get_route_handler()asyncdefcustom_route_handler(request:Request)->Response:before=time.time()response:Response=awaitoriginal_route_handler(request)duration=time.time()-beforeresponse.headers["X-Response-Time"]=str(duration)print(f"route duration: {duration}")print(f"route response: {response}")print(f"route response headers: {response.headers}")returnresponsereturncustom_route_handlerapp=FastAPI()router=APIRouter(route_class=TimedRoute)@app.get("/")asyncdefnot_timed():return{"message":"Not timed"}@router.get("/timed")asyncdeftimed():return{"message":"It's the time of my life"}app.include_router(router)