|
| 1 | +# Benutzerdefinierte Response – HTML, Stream, Datei, andere |
| 2 | + |
| 3 | +Standardmäßig gibt **FastAPI** die Responses mittels `JSONResponse` zurück. |
| 4 | + |
| 5 | +Sie können das überschreiben, indem Sie direkt eine `Response` zurückgeben, wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} gezeigt. |
| 6 | + |
| 7 | +Wenn Sie jedoch direkt eine `Response` zurückgeben, werden die Daten nicht automatisch konvertiert und die Dokumentation wird nicht automatisch generiert (wird zum Beispiel nicht den spezifischen „Medientyp“ im HTTP-Header `Content-Type` als Teil der generierten OpenAPI enthalten). |
| 8 | + |
| 9 | +Sie können aber auch die `Response`, die Sie verwenden möchten, im *Pfadoperation-Dekorator* deklarieren. |
| 10 | + |
| 11 | +Der Inhalt, den Sie von Ihrer *Pfadoperation-Funktion* zurückgeben, wird in diese `Response` eingefügt. |
| 12 | + |
| 13 | +Und wenn diese `Response` einen JSON-Medientyp (`application/json`) hat, wie es bei `JSONResponse` und `UJSONResponse` der Fall ist, werden die von Ihnen zurückgegebenen Daten automatisch mit jedem Pydantic `response_model` konvertiert (und gefiltert), das Sie im *Pfadoperation-Dekorator* deklariert haben. |
| 14 | + |
| 15 | +!!! note "Hinweis" |
| 16 | + Wenn Sie eine Response-Klasse ohne Medientyp verwenden, erwartet FastAPI, dass Ihre Response keinen Inhalt hat, und dokumentiert daher das Format der Response nicht in deren generierter OpenAPI-Dokumentation. |
| 17 | + |
| 18 | +## `ORJSONResponse` verwenden |
| 19 | + |
| 20 | +Um beispielsweise Performanz herauszuquetschen, können Sie <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> installieren und verwenden, und die Response als `ORJSONResponse` deklarieren. |
| 21 | + |
| 22 | +Importieren Sie die `Response`-Klasse (-Unterklasse), die Sie verwenden möchten, und deklarieren Sie sie im *Pfadoperation-Dekorator*. |
| 23 | + |
| 24 | +Bei umfangreichen Responses ist die direkte Rückgabe einer `Response` viel schneller als die Rückgabe eines Dicts. |
| 25 | + |
| 26 | +Das liegt daran, dass FastAPI standardmäßig jedes enthaltene Element überprüft und sicherstellt, dass es als JSON serialisierbar ist, und zwar unter Verwendung desselben [JSON-kompatiblen Encoders](../tutorial/encoder.md){.internal-link target=_blank}, der im Tutorial erläutert wurde. Dadurch können Sie **beliebige Objekte** zurückgeben, zum Beispiel Datenbankmodelle. |
| 27 | + |
| 28 | +Wenn Sie jedoch sicher sind, dass der von Ihnen zurückgegebene Inhalt **mit JSON serialisierbar** ist, können Sie ihn direkt an die Response-Klasse übergeben und die zusätzliche Arbeit vermeiden, die FastAPI hätte, indem es Ihren zurückgegebenen Inhalt durch den `jsonable_encoder` leitet, bevor es ihn an die Response-Klasse übergibt. |
| 29 | + |
| 30 | +```Python hl_lines="2 7" |
| 31 | +{!../../../docs_src/custom_response/tutorial001b.py!} |
| 32 | +``` |
| 33 | + |
| 34 | +!!! info |
| 35 | + Der Parameter `response_class` wird auch verwendet, um den „Medientyp“ der Response zu definieren. |
| 36 | + |
| 37 | + In diesem Fall wird der HTTP-Header `Content-Type` auf `application/json` gesetzt. |
| 38 | + |
| 39 | + Und er wird als solcher in OpenAPI dokumentiert. |
| 40 | + |
| 41 | +!!! tip "Tipp" |
| 42 | + Die `ORJSONResponse` ist derzeit nur in FastAPI verfügbar, nicht in Starlette. |
| 43 | + |
| 44 | +## HTML-Response |
| 45 | + |
| 46 | +Um eine Response mit HTML direkt von **FastAPI** zurückzugeben, verwenden Sie `HTMLResponse`. |
| 47 | + |
| 48 | +* Importieren Sie `HTMLResponse`. |
| 49 | +* Übergeben Sie `HTMLResponse` als den Parameter `response_class` Ihres *Pfadoperation-Dekorators*. |
| 50 | + |
| 51 | +```Python hl_lines="2 7" |
| 52 | +{!../../../docs_src/custom_response/tutorial002.py!} |
| 53 | +``` |
| 54 | + |
| 55 | +!!! info |
| 56 | + Der Parameter `response_class` wird auch verwendet, um den „Medientyp“ der Response zu definieren. |
| 57 | + |
| 58 | + In diesem Fall wird der HTTP-Header `Content-Type` auf `text/html` gesetzt. |
| 59 | + |
| 60 | + Und er wird als solcher in OpenAPI dokumentiert. |
| 61 | + |
| 62 | +### Eine `Response` zurückgeben |
| 63 | + |
| 64 | +Wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} gezeigt, können Sie die Response auch direkt in Ihrer *Pfadoperation* überschreiben, indem Sie sie zurückgeben. |
| 65 | + |
| 66 | +Das gleiche Beispiel von oben, das eine `HTMLResponse` zurückgibt, könnte so aussehen: |
| 67 | + |
| 68 | +```Python hl_lines="2 7 19" |
| 69 | +{!../../../docs_src/custom_response/tutorial003.py!} |
| 70 | +``` |
| 71 | + |
| 72 | +!!! warning "Achtung" |
| 73 | + Eine `Response`, die direkt von Ihrer *Pfadoperation-Funktion* zurückgegeben wird, wird in OpenAPI nicht dokumentiert (zum Beispiel wird der `Content-Type` nicht dokumentiert) und ist in der automatischen interaktiven Dokumentation nicht sichtbar. |
| 74 | + |
| 75 | +!!! info |
| 76 | + Natürlich stammen der eigentliche `Content-Type`-Header, der Statuscode, usw., aus dem `Response`-Objekt, das Sie zurückgegeben haben. |
| 77 | + |
| 78 | +### In OpenAPI dokumentieren und `Response` überschreiben |
| 79 | + |
| 80 | +Wenn Sie die Response innerhalb der Funktion überschreiben und gleichzeitig den „Medientyp“ in OpenAPI dokumentieren möchten, können Sie den `response_class`-Parameter verwenden UND ein `Response`-Objekt zurückgeben. |
| 81 | + |
| 82 | +Die `response_class` wird dann nur zur Dokumentation der OpenAPI-Pfadoperation* verwendet, Ihre `Response` wird jedoch unverändert verwendet. |
| 83 | + |
| 84 | +#### Eine `HTMLResponse` direkt zurückgeben |
| 85 | + |
| 86 | +Es könnte zum Beispiel so etwas sein: |
| 87 | + |
| 88 | +```Python hl_lines="7 21 23" |
| 89 | +{!../../../docs_src/custom_response/tutorial004.py!} |
| 90 | +``` |
| 91 | + |
| 92 | +In diesem Beispiel generiert die Funktion `generate_html_response()` bereits eine `Response` und gibt sie zurück, anstatt das HTML in einem `str` zurückzugeben. |
| 93 | + |
| 94 | +Indem Sie das Ergebnis des Aufrufs von `generate_html_response()` zurückgeben, geben Sie bereits eine `Response` zurück, die das Standardverhalten von **FastAPI** überschreibt. |
| 95 | + |
| 96 | +Aber da Sie die `HTMLResponse` auch in der `response_class` übergeben haben, weiß **FastAPI**, dass sie in OpenAPI und der interaktiven Dokumentation als HTML mit `text/html` zu dokumentieren ist: |
| 97 | + |
| 98 | +<img src="/img/tutorial/custom-response/image01.png"> |
| 99 | + |
| 100 | +## Verfügbare Responses |
| 101 | + |
| 102 | +Hier sind einige der verfügbaren Responses. |
| 103 | + |
| 104 | +Bedenken Sie, dass Sie `Response` verwenden können, um alles andere zurückzugeben, oder sogar eine benutzerdefinierte Unterklasse zu erstellen. |
| 105 | + |
| 106 | +!!! note "Technische Details" |
| 107 | + Sie können auch `from starlette.responses import HTMLResponse` verwenden. |
| 108 | + |
| 109 | + **FastAPI** bietet die gleichen `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für den Entwickler. Aber die meisten verfügbaren Responses kommen direkt von Starlette. |
| 110 | + |
| 111 | +### `Response` |
| 112 | + |
| 113 | +Die Hauptklasse `Response`, alle anderen Responses erben von ihr. |
| 114 | + |
| 115 | +Sie können sie direkt zurücksenden. |
| 116 | + |
| 117 | +Sie akzeptiert die folgenden Parameter: |
| 118 | + |
| 119 | +* `content` – Ein `str` oder `bytes`. |
| 120 | +* `status_code` – Ein `int`-HTTP-Statuscode. |
| 121 | +* `headers` – Ein `dict` von Strings. |
| 122 | +* `media_type` – Ein `str`, der den Medientyp angibt. Z.B. `"text/html"`. |
| 123 | + |
| 124 | +FastAPI (eigentlich Starlette) fügt automatisch einen Content-Length-Header ein. Es wird außerdem einen Content-Type-Header einfügen, der auf dem media_type basiert, und für Texttypen einen Zeichensatz anfügen. |
| 125 | + |
| 126 | +```Python hl_lines="1 18" |
| 127 | +{!../../../docs_src/response_directly/tutorial002.py!} |
| 128 | +``` |
| 129 | + |
| 130 | +### `HTMLResponse` |
| 131 | + |
| 132 | +Nimmt Text oder Bytes entgegen und gibt eine HTML-Response zurück, wie Sie oben gelesen haben. |
| 133 | + |
| 134 | +### `PlainTextResponse` |
| 135 | + |
| 136 | +Nimmt Text oder Bytes entgegen und gibt eine Plain-Text-Response zurück. |
| 137 | + |
| 138 | +```Python hl_lines="2 7 9" |
| 139 | +{!../../../docs_src/custom_response/tutorial005.py!} |
| 140 | +``` |
| 141 | + |
| 142 | +### `JSONResponse` |
| 143 | + |
| 144 | +Nimmt einige Daten entgegen und gibt eine `application/json`-codierte Response zurück. |
| 145 | + |
| 146 | +Dies ist die Standard-Response, die in **FastAPI** verwendet wird, wie Sie oben gelesen haben. |
| 147 | + |
| 148 | +### `ORJSONResponse` |
| 149 | + |
| 150 | +Eine schnelle alternative JSON-Response mit <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, wie Sie oben gelesen haben. |
| 151 | + |
| 152 | +### `UJSONResponse` |
| 153 | + |
| 154 | +Eine alternative JSON-Antwort mit <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>. |
| 155 | + |
| 156 | +!!! warning "Achtung" |
| 157 | + `ujson` ist bei der Behandlung einiger Randfälle weniger sorgfältig als Pythons eingebaute Implementierung. |
| 158 | + |
| 159 | +```Python hl_lines="2 7" |
| 160 | +{!../../../docs_src/custom_response/tutorial001.py!} |
| 161 | +``` |
| 162 | + |
| 163 | +!!! tip "Tipp" |
| 164 | + Möglicherweise ist `ORJSONResponse` eine schnellere Alternative. |
| 165 | + |
| 166 | +### `RedirectResponse` |
| 167 | + |
| 168 | +Gibt eine HTTP-Weiterleitung (HTTP-Redirect) zurück. Verwendet standardmäßig den Statuscode 307 – Temporäre Weiterleitung (Temporary Redirect). |
| 169 | + |
| 170 | +Sie können eine `RedirectResponse` direkt zurückgeben: |
| 171 | + |
| 172 | +```Python hl_lines="2 9" |
| 173 | +{!../../../docs_src/custom_response/tutorial006.py!} |
| 174 | +``` |
| 175 | + |
| 176 | +--- |
| 177 | + |
| 178 | +Oder Sie können sie im Parameter `response_class` verwenden: |
| 179 | + |
| 180 | + |
| 181 | +```Python hl_lines="2 7 9" |
| 182 | +{!../../../docs_src/custom_response/tutorial006b.py!} |
| 183 | +``` |
| 184 | + |
| 185 | +Wenn Sie das tun, können Sie die URL direkt von Ihrer *Pfadoperation*-Funktion zurückgeben. |
| 186 | + |
| 187 | +In diesem Fall ist der verwendete `status_code` der Standardcode für die `RedirectResponse`, also `307`. |
| 188 | + |
| 189 | +--- |
| 190 | + |
| 191 | +Sie können den Parameter `status_code` auch in Kombination mit dem Parameter `response_class` verwenden: |
| 192 | + |
| 193 | +```Python hl_lines="2 7 9" |
| 194 | +{!../../../docs_src/custom_response/tutorial006c.py!} |
| 195 | +``` |
| 196 | + |
| 197 | +### `StreamingResponse` |
| 198 | + |
| 199 | +Nimmt einen asynchronen Generator oder einen normalen Generator/Iterator und streamt den Responsebody. |
| 200 | + |
| 201 | +```Python hl_lines="2 14" |
| 202 | +{!../../../docs_src/custom_response/tutorial007.py!} |
| 203 | +``` |
| 204 | + |
| 205 | +#### Verwendung von `StreamingResponse` mit dateiähnlichen Objekten |
| 206 | + |
| 207 | +Wenn Sie ein dateiähnliches (file-like) Objekt haben (z. B. das von `open()` zurückgegebene Objekt), können Sie eine Generatorfunktion erstellen, um über dieses dateiähnliche Objekt zu iterieren. |
| 208 | + |
| 209 | +Auf diese Weise müssen Sie nicht alles zuerst in den Arbeitsspeicher lesen und können diese Generatorfunktion an `StreamingResponse` übergeben und zurückgeben. |
| 210 | + |
| 211 | +Dazu beinhaltet viele Bibliotheken zur Interaktion mit Cloud-Speicher, Videoverarbeitung und andere. |
| 212 | + |
| 213 | +```{ .python .annotate hl_lines="2 10-12 14" } |
| 214 | +{!../../../docs_src/custom_response/tutorial008.py!} |
| 215 | +``` |
| 216 | + |
| 217 | +1. Das ist die Generatorfunktion. Es handelt sich um eine „Generatorfunktion“, da sie `yield`-Anweisungen enthält. |
| 218 | +2. Durch die Verwendung eines `with`-Blocks stellen wir sicher, dass das dateiähnliche Objekt geschlossen wird, nachdem die Generatorfunktion abgeschlossen ist. Also, nachdem sie mit dem Senden der Response fertig ist. |
| 219 | +3. Dieses `yield from` weist die Funktion an, über das Ding namens `file_like` zu iterieren. Und dann für jeden iterierten Teil, diesen Teil so zurückzugeben, als wenn er aus dieser Generatorfunktion (`iterfile`) stammen würde. |
| 220 | + |
| 221 | + Es handelt sich also hier um eine Generatorfunktion, die die „generierende“ Arbeit intern auf etwas anderes überträgt. |
| 222 | + |
| 223 | + Auf diese Weise können wir das Ganze in einen `with`-Block einfügen und so sicherstellen, dass das dateiartige Objekt nach Abschluss geschlossen wird. |
| 224 | + |
| 225 | +!!! tip "Tipp" |
| 226 | + Beachten Sie, dass wir, da wir Standard-`open()` verwenden, welches `async` und `await` nicht unterstützt, hier die Pfadoperation mit normalen `def` deklarieren. |
| 227 | + |
| 228 | +### `FileResponse` |
| 229 | + |
| 230 | +Streamt eine Datei asynchron als Response. |
| 231 | + |
| 232 | +Nimmt zur Instanziierung einen anderen Satz von Argumenten entgegen als die anderen Response-Typen: |
| 233 | + |
| 234 | +* `path` – Der Dateipfad zur Datei, die gestreamt werden soll. |
| 235 | +* `headers` – Alle benutzerdefinierten Header, die inkludiert werden sollen, als Dictionary. |
| 236 | +* `media_type` – Ein String, der den Medientyp angibt. Wenn nicht gesetzt, wird der Dateiname oder Pfad verwendet, um auf einen Medientyp zu schließen. |
| 237 | +* `filename` – Wenn gesetzt, wird das in der `Content-Disposition` der Response eingefügt. |
| 238 | + |
| 239 | +Datei-Responses enthalten die entsprechenden `Content-Length`-, `Last-Modified`- und `ETag`-Header. |
| 240 | + |
| 241 | +```Python hl_lines="2 10" |
| 242 | +{!../../../docs_src/custom_response/tutorial009.py!} |
| 243 | +``` |
| 244 | + |
| 245 | +Sie können auch den Parameter `response_class` verwenden: |
| 246 | + |
| 247 | +```Python hl_lines="2 8 10" |
| 248 | +{!../../../docs_src/custom_response/tutorial009b.py!} |
| 249 | +``` |
| 250 | + |
| 251 | +In diesem Fall können Sie den Dateipfad direkt von Ihrer *Pfadoperation*-Funktion zurückgeben. |
| 252 | + |
| 253 | +## Benutzerdefinierte Response-Klasse |
| 254 | + |
| 255 | +Sie können Ihre eigene benutzerdefinierte Response-Klasse erstellen, die von `Response` erbt und diese verwendet. |
| 256 | + |
| 257 | +Nehmen wir zum Beispiel an, dass Sie <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> verwenden möchten, aber mit einigen benutzerdefinierten Einstellungen, die in der enthaltenen `ORJSONResponse`-Klasse nicht verwendet werden. |
| 258 | + |
| 259 | +Nehmen wir an, Sie möchten, dass sie eingerücktes und formatiertes JSON zurückgibt, also möchten Sie die orjson-Option `orjson.OPT_INDENT_2` verwenden. |
| 260 | + |
| 261 | +Sie könnten eine `CustomORJSONResponse` erstellen. Das Wichtigste, was Sie tun müssen, ist, eine `Response.render(content)`-Methode zu erstellen, die den Inhalt als `bytes` zurückgibt: |
| 262 | + |
| 263 | +```Python hl_lines="9-14 17" |
| 264 | +{!../../../docs_src/custom_response/tutorial009c.py!} |
| 265 | +``` |
| 266 | + |
| 267 | +Statt: |
| 268 | + |
| 269 | +```json |
| 270 | +{"message": "Hello World"} |
| 271 | +``` |
| 272 | + |
| 273 | +... wird die Response jetzt Folgendes zurückgeben: |
| 274 | + |
| 275 | +```json |
| 276 | +{ |
| 277 | + "message": "Hello World" |
| 278 | +} |
| 279 | +``` |
| 280 | + |
| 281 | +Natürlich werden Sie wahrscheinlich viel bessere Möglichkeiten finden, Vorteil daraus zu ziehen, als JSON zu formatieren. 😉 |
| 282 | + |
| 283 | +## Standard-Response-Klasse |
| 284 | + |
| 285 | +Beim Erstellen einer **FastAPI**-Klasseninstanz oder eines `APIRouter`s können Sie angeben, welche Response-Klasse standardmäßig verwendet werden soll. |
| 286 | + |
| 287 | +Der Parameter, der das definiert, ist `default_response_class`. |
| 288 | + |
| 289 | +Im folgenden Beispiel verwendet **FastAPI** standardmäßig `ORJSONResponse` in allen *Pfadoperationen*, anstelle von `JSONResponse`. |
| 290 | + |
| 291 | +```Python hl_lines="2 4" |
| 292 | +{!../../../docs_src/custom_response/tutorial010.py!} |
| 293 | +``` |
| 294 | + |
| 295 | +!!! tip "Tipp" |
| 296 | + Sie können dennoch weiterhin `response_class` in *Pfadoperationen* überschreiben, wie bisher. |
| 297 | + |
| 298 | +## Zusätzliche Dokumentation |
| 299 | + |
| 300 | +Sie können auch den Medientyp und viele andere Details in OpenAPI mit `responses` deklarieren: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}. |
0 commit comments