mirror of https://github.com/tiangolo/fastapi.git
Merge branch 'master' into jsonable_encoder
This commit is contained in:
commit
6730f75a85
|
|
@ -26,7 +26,7 @@ Jedes dieser Response-`dict`s kann einen Schlüssel `model` haben, welcher ein P
|
|||
|
||||
Um beispielsweise eine weitere Response mit dem Statuscode `404` und einem Pydantic-Modell `Message` zu deklarieren, können Sie schreiben:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ Sie können beispielsweise eine Response mit dem Statuscode `404` deklarieren, d
|
|||
|
||||
Und eine Response mit dem Statuscode `200`, die Ihr `response_model` verwendet, aber ein benutzerdefiniertes Beispiel (`example`) enthält:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
|
||||
|
||||
Es wird alles kombiniert und in Ihre OpenAPI eingebunden und in der API-Dokumentation angezeigt:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Nicht die Klasse selbst (die bereits aufrufbar ist), sondern eine Instanz dieser
|
|||
|
||||
Dazu deklarieren wir eine Methode `__call__`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
In diesem Fall ist dieses `__call__` das, was **FastAPI** verwendet, um nach zusätzlichen Parametern und Unterabhängigkeiten zu suchen, und das ist es auch, was später aufgerufen wird, um einen Wert an den Parameter in Ihrer *Pfadoperation-Funktion* zu übergeben.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ In diesem Fall ist dieses `__call__` das, was **FastAPI** verwendet, um nach zus
|
|||
|
||||
Und jetzt können wir `__init__` verwenden, um die Parameter der Instanz zu deklarieren, die wir zum „Parametrisieren“ der Abhängigkeit verwenden können:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
In diesem Fall wird **FastAPI** `__init__` nie berühren oder sich darum kümmern, wir werden es direkt in unserem Code verwenden.
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ In diesem Fall wird **FastAPI** `__init__` nie berühren oder sich darum kümmer
|
|||
|
||||
Wir könnten eine Instanz dieser Klasse erstellen mit:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
Und auf diese Weise können wir unsere Abhängigkeit „parametrisieren“, die jetzt `"bar"` enthält, als das Attribut `checker.fixed_content`.
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ checker(q="somequery")
|
|||
|
||||
... und übergibt, was immer das als Wert dieser Abhängigkeit in unserer *Pfadoperation-Funktion* zurückgibt, als den Parameter `fixed_content_included`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
# Fortgeschrittene Python-Typen { #advanced-python-types }
|
||||
|
||||
Hier sind einige zusätzliche Ideen, die beim Arbeiten mit Python-Typen nützlich sein könnten.
|
||||
|
||||
## `Union` oder `Optional` verwenden { #using-union-or-optional }
|
||||
|
||||
Wenn Ihr Code aus irgendeinem Grund nicht `|` verwenden kann, z. B. wenn es nicht in einer Typannotation ist, sondern in etwas wie `response_model=`, können Sie anstelle des senkrechten Strichs (`|`) `Union` aus `typing` verwenden.
|
||||
|
||||
Zum Beispiel könnten Sie deklarieren, dass etwas ein `str` oder `None` sein könnte:
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` hat außerdem eine Abkürzung, um zu deklarieren, dass etwas `None` sein könnte, mit `Optional`.
|
||||
|
||||
Hier ist ein Tipp aus meiner sehr **subjektiven** Perspektive:
|
||||
|
||||
* 🚨 Vermeiden Sie die Verwendung von `Optional[SomeType]`
|
||||
* Verwenden Sie stattdessen ✨ **`Union[SomeType, None]`** ✨.
|
||||
|
||||
Beides ist äquivalent und unter der Haube identisch, aber ich würde `Union` statt `Optional` empfehlen, weil das Wort „**optional**“ implizieren könnte, dass der Wert optional ist; tatsächlich bedeutet es jedoch „es kann `None` sein“, selbst wenn es nicht optional ist und weiterhin erforderlich bleibt.
|
||||
|
||||
Ich finde, `Union[SomeType, None]` ist expliziter in dem, was es bedeutet.
|
||||
|
||||
Es geht nur um Wörter und Namen. Aber diese Wörter können beeinflussen, wie Sie und Ihr Team über den Code denken.
|
||||
|
||||
Als Beispiel nehmen wir diese Funktion:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Der Parameter `name` ist als `Optional[str]` definiert, aber er ist **nicht optional**, Sie können die Funktion nicht ohne den Parameter aufrufen:
|
||||
|
||||
```Python
|
||||
say_hi() # Oh nein, das löst einen Fehler aus! 😱
|
||||
```
|
||||
|
||||
Der Parameter `name` ist **weiterhin erforderlich** (nicht *optional*), weil er keinen Defaultwert hat. Dennoch akzeptiert `name` den Wert `None`:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Das funktioniert, None ist gültig 🎉
|
||||
```
|
||||
|
||||
Die gute Nachricht ist: In den meisten Fällen können Sie einfach `|` verwenden, um Unions von Typen zu definieren:
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Sie müssen sich also normalerweise keine Gedanken über Namen wie `Optional` und `Union` machen. 😎
|
||||
|
|
@ -32,11 +32,11 @@ Betrachten wir als einfaches Beispiel eine Dateistruktur ähnlich der in [Größ
|
|||
|
||||
Die Datei `main.py` hätte als Inhalt:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
Die Datei `test_main.py` hätte die Tests für `main.py`, das könnte jetzt so aussehen:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## Es ausführen { #run-it }
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ $ pytest
|
|||
|
||||
Der Marker `@pytest.mark.anyio` teilt pytest mit, dass diese Testfunktion asynchron aufgerufen werden soll:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ Beachten Sie, dass die Testfunktion jetzt `async def` ist und nicht nur `def` wi
|
|||
|
||||
Dann können wir einen `AsyncClient` mit der App erstellen und mit `await` asynchrone Requests an ihn senden.
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
Das ist das Äquivalent zu:
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ $ fastapi run --forwarded-allow-ips="*"
|
|||
|
||||
Angenommen, Sie definieren eine *Pfadoperation* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
Wenn der Client versucht, zu `/items` zu gehen, würde er standardmäßig zu `/items/` umgeleitet.
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ In diesem Fall würde der ursprüngliche Pfad `/app` tatsächlich unter `/api/v1
|
|||
|
||||
Auch wenn Ihr gesamter Code unter der Annahme geschrieben ist, dass es nur `/app` gibt.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
Und der Proxy würde das **Pfadpräfix** on-the-fly **„entfernen“**, bevor er den <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> an den Anwendungsserver (wahrscheinlich Uvicorn via FastAPI CLI) übermittelt, dafür sorgend, dass Ihre Anwendung davon überzeugt ist, dass sie unter `/app` bereitgestellt wird, sodass Sie nicht Ihren gesamten Code dahingehend aktualisieren müssen, das Präfix `/api/v1` zu verwenden.
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ Sie können den aktuellen `root_path` abrufen, der von Ihrer Anwendung für jede
|
|||
|
||||
Hier fügen wir ihn, nur zu Demonstrationszwecken, in die Nachricht ein.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Wenn Sie Uvicorn dann starten mit:
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ wäre die <abbr title="Response – Antwort: Daten, die der Server zum anfragend
|
|||
|
||||
Falls Sie keine Möglichkeit haben, eine Kommandozeilenoption wie `--root-path` oder ähnlich zu übergeben, können Sie, alternativ dazu, beim Erstellen Ihrer FastAPI-Anwendung den Parameter `root_path` setzen:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Die Übergabe des `root_path` an `FastAPI` wäre das Äquivalent zur Übergabe der `--root-path`-Kommandozeilenoption an Uvicorn oder Hypercorn.
|
||||
|
||||
|
|
@ -400,7 +400,7 @@ Wenn Sie eine benutzerdefinierte Liste von Servern (`servers`) übergeben und es
|
|||
|
||||
Zum Beispiel:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
Erzeugt ein OpenAPI-Schema, wie:
|
||||
|
||||
|
|
@ -455,7 +455,7 @@ Wenn Sie den Parameter `servers` nicht angeben und `root_path` den Wert `/` hat,
|
|||
|
||||
Wenn Sie nicht möchten, dass **FastAPI** einen automatischen Server inkludiert, welcher `root_path` verwendet, können Sie den Parameter `root_path_in_servers=False` verwenden:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
Dann wird er nicht in das OpenAPI-Schema aufgenommen.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Das liegt daran, dass FastAPI standardmäßig jedes enthaltene Element überprü
|
|||
|
||||
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.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ Um eine Response mit HTML direkt von **FastAPI** zurückzugeben, verwenden Sie `
|
|||
* Importieren Sie `HTMLResponse`.
|
||||
* Übergeben Sie `HTMLResponse` als den Parameter `response_class` Ihres *Pfadoperation-Dekorators*.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ Wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link
|
|||
|
||||
Das gleiche Beispiel von oben, das eine `HTMLResponse` zurückgibt, könnte so aussehen:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Achtung
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ Die `response_class` wird dann nur zur Dokumentation der OpenAPI-*Pfadoperation*
|
|||
|
||||
Es könnte zum Beispiel so etwas sein:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
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.
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ Sie akzeptiert die folgenden Parameter:
|
|||
|
||||
FastAPI (eigentlich Starlette) fügt automatisch einen Content-Length-Header ein. Außerdem wird es einen Content-Type-Header einfügen, der auf dem media_type basiert, und für Texttypen einen Zeichensatz (charset) anfügen.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ Nimmt Text oder Bytes entgegen und gibt eine HTML-Response zurück, wie Sie oben
|
|||
|
||||
Nimmt Text oder Bytes entgegen und gibt eine Plain-Text-Response zurück.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ Dazu muss `ujson` installiert werden, z. B. mit `pip install ujson`.
|
|||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -194,13 +194,13 @@ Gibt eine HTTP-Weiterleitung (HTTP-Redirect) zurück. Verwendet standardmäßig
|
|||
|
||||
Sie können eine `RedirectResponse` direkt zurückgeben:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
Oder Sie können sie im Parameter `response_class` verwenden:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
Wenn Sie das tun, können Sie die URL direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
|
||||
|
||||
|
|
@ -210,13 +210,13 @@ In diesem Fall ist der verwendete `status_code` der Standardcode für die `Redir
|
|||
|
||||
Sie können den Parameter `status_code` auch in Kombination mit dem Parameter `response_class` verwenden:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Nimmt einen asynchronen Generator oder einen normalen Generator/Iterator und streamt den Responsebody.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### Verwendung von `StreamingResponse` mit dateiartigen Objekten { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ Auf diese Weise müssen Sie nicht alles zuerst in den Arbeitsspeicher lesen und
|
|||
|
||||
Das umfasst viele Bibliotheken zur Interaktion mit Cloud-Speicher, Videoverarbeitung und anderen.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. Das ist die Generatorfunktion. Es handelt sich um eine „Generatorfunktion“, da sie `yield`-Anweisungen enthält.
|
||||
2. Durch die Verwendung eines `with`-Blocks stellen wir sicher, dass das dateiartige Objekt geschlossen wird, nachdem die Generatorfunktion fertig ist. Also, nachdem sie mit dem Senden der Response fertig ist.
|
||||
|
|
@ -255,11 +255,11 @@ Nimmt zur Instanziierung einen anderen Satz von Argumenten entgegen als die ande
|
|||
|
||||
Datei-Responses enthalten die entsprechenden `Content-Length`-, `Last-Modified`- und `ETag`-Header.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
Sie können auch den Parameter `response_class` verwenden:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
In diesem Fall können Sie den Dateipfad direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
|
||||
|
||||
|
|
@ -273,7 +273,7 @@ Sie möchten etwa, dass Ihre Response eingerücktes und formatiertes JSON zurüc
|
|||
|
||||
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:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
Statt:
|
||||
|
||||
|
|
@ -299,7 +299,7 @@ Der Parameter, der das definiert, ist `default_response_class`.
|
|||
|
||||
Im folgenden Beispiel verwendet **FastAPI** standardmäßig `ORJSONResponse` in allen *Pfadoperationen*, anstelle von `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Verwendung von Datenklassen { #using-dataclasses }
|
||||
# Datenklassen verwenden { #using-dataclasses }
|
||||
|
||||
FastAPI basiert auf **Pydantic**, und ich habe Ihnen gezeigt, wie Sie Pydantic-Modelle verwenden können, um <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> und <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Responses</abbr> zu deklarieren.
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.da
|
|||
|
||||
6. Hier geben wir ein <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> zurück, das `items` enthält, welches eine Liste von Datenklassen ist.
|
||||
|
||||
FastAPI ist weiterhin in der Lage, die Daten nach JSON zu <abbr title="Konvertieren der Daten in ein übertragbares Format">serialisieren</abbr>.
|
||||
FastAPI ist weiterhin in der Lage, die Daten nach JSON zu <dfn title="Konvertieren der Daten in ein übertragbares Format">Serialisieren</dfn>.
|
||||
|
||||
7. Hier verwendet das `response_model` als Typannotation eine Liste von `Author`-Datenklassen.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Beginnen wir mit einem Beispiel und sehen es uns dann im Detail an.
|
|||
|
||||
Wir erstellen eine asynchrone Funktion `lifespan()` mit `yield` wie folgt:
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
Hier simulieren wir den langsamen *Startup*, das Laden des Modells, indem wir die (Fake-)Modellfunktion vor dem `yield` in das <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> mit Modellen für maschinelles Lernen einfügen. Dieser Code wird ausgeführt, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**, während des *Startups*.
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ Möglicherweise müssen Sie eine neue Version starten, oder Sie haben es einfach
|
|||
|
||||
Das Erste, was auffällt, ist, dass wir eine asynchrone Funktion mit `yield` definieren. Das ist sehr ähnlich zu Abhängigkeiten mit `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
Der erste Teil der Funktion, vor dem `yield`, wird ausgeführt **bevor** die Anwendung startet.
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ Wie Sie sehen, ist die Funktion mit einem `@asynccontextmanager` versehen.
|
|||
|
||||
Dadurch wird die Funktion in einen sogenannten „**asynchronen Kontextmanager**“ umgewandelt.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
Ein **Kontextmanager** in Python ist etwas, das Sie in einer `with`-Anweisung verwenden können, zum Beispiel kann `open()` als Kontextmanager verwendet werden:
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ In unserem obigen Codebeispiel verwenden wir ihn nicht direkt, sondern übergebe
|
|||
|
||||
Der Parameter `lifespan` der `FastAPI`-App benötigt einen **asynchronen Kontextmanager**, wir können ihm also unseren neuen asynchronen Kontextmanager `lifespan` übergeben.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## Alternative Events (<abbr title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</abbr>) { #alternative-events-deprecated }
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ Diese Funktionen können mit `async def` oder normalem `def` deklariert werden.
|
|||
|
||||
Um eine Funktion hinzuzufügen, die vor dem Start der Anwendung ausgeführt werden soll, deklarieren Sie diese mit dem Event `startup`:
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
In diesem Fall initialisiert die Eventhandler-Funktion `startup` die „Datenbank“ der Items (nur ein `dict`) mit einigen Werten.
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ Und Ihre Anwendung empfängt erst dann Requests, wenn alle `startup`-Eventhandle
|
|||
|
||||
Um eine Funktion hinzuzufügen, die beim Shutdown der Anwendung ausgeführt werden soll, deklarieren Sie sie mit dem Event `shutdown`:
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
Hier schreibt die `shutdown`-Eventhandler-Funktion eine Textzeile `"Application shutdown"` in eine Datei `log.txt`.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Da **FastAPI** auf der **OpenAPI**-Spezifikation basiert, können dessen APIs in einem standardisierten Format beschrieben werden, das viele Tools verstehen.
|
||||
|
||||
Dies vereinfacht es, aktuelle **Dokumentation** und Client-Bibliotheken (<abbr title="Software Development Kit – Software-Entwicklungspaket">**SDKs**</abbr>) in verschiedenen Sprachen zu generieren sowie **Test-** oder **Automatisierungs-Workflows**, die mit Ihrem Code synchron bleiben.
|
||||
Dies vereinfacht es, aktuelle **Dokumentation** und Client-Bibliotheken (<abbr title="Software Development Kits - Software-Entwicklungspakete">**SDKs**</abbr>) in verschiedenen Sprachen zu generieren sowie **Test-** oder **Automatisierungs-Workflows**, die mit Ihrem Code synchron bleiben.
|
||||
|
||||
In diesem Leitfaden erfahren Sie, wie Sie ein **TypeScript-SDK** für Ihr FastAPI-Backend generieren.
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ Einige dieser Lösungen sind möglicherweise auch Open Source oder bieten kosten
|
|||
|
||||
Beginnen wir mit einer einfachen FastAPI-Anwendung:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Beachten Sie, dass die *Pfadoperationen* die Modelle definieren, die sie für die <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr>- und <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-<abbr title="Die eigentlichen Nutzdaten, abzüglich der Metadaten">Payload</abbr> verwenden, indem sie die Modelle `Item` und `ResponseMessage` verwenden.
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ In vielen Fällen wird Ihre FastAPI-App größer sein und Sie werden wahrscheinl
|
|||
|
||||
Zum Beispiel könnten Sie einen Abschnitt für **Items (Artikel)** und einen weiteren Abschnitt für **Users (Benutzer)** haben, und diese könnten durch Tags getrennt sein:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### Einen TypeScript-Client mit Tags generieren { #generate-a-typescript-client-with-tags }
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ Hier verwendet sie beispielsweise den ersten Tag (Sie werden wahrscheinlich nur
|
|||
|
||||
Anschließend können Sie diese benutzerdefinierte Funktion als `generate_unique_id_function`-Parameter an **FastAPI** übergeben:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### Einen TypeScript-Client mit benutzerdefinierten Operation-IDs generieren { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ Aber für den generierten Client könnten wir die OpenAPI-Operation-IDs direkt v
|
|||
|
||||
Wir könnten das OpenAPI-JSON in eine Datei `openapi.json` herunterladen und dann mit einem Skript wie dem folgenden **den präfixierten Tag entfernen**:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
|
|
@ -179,7 +179,7 @@ Wir könnten das OpenAPI-JSON in eine Datei `openapi.json` herunterladen und dan
|
|||
|
||||
Damit würden die Operation-IDs von Dingen wie `items-get_items` in `get_items` umbenannt, sodass der Client-Generator einfachere Methodennamen generieren kann.
|
||||
|
||||
### Einen TypeScript-Client mit der modifizierten OpenAPI generieren { #generate-a-typescript-client-with-the-preprocessed-openapi }
|
||||
### Einen TypeScript-Client mit der vorverarbeiteten OpenAPI generieren { #generate-a-typescript-client-with-the-preprocessed-openapi }
|
||||
|
||||
Da das Endergebnis nun in einer `openapi.json`-Datei vorliegt, müssen Sie Ihren Eingabeort aktualisieren:
|
||||
|
||||
|
|
|
|||
|
|
@ -57,13 +57,13 @@ Erzwingt, dass alle eingehenden <abbr title="Request – Anfrage: Daten, die der
|
|||
|
||||
Alle eingehenden Requests an `http` oder `ws` werden stattdessen an das sichere Schema umgeleitet.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
Erzwingt, dass alle eingehenden Requests einen korrekt gesetzten `Host`-Header haben, um sich vor HTTP-Host-Header-Angriffen zu schützen.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
|
||||
|
||||
Die folgenden Argumente werden unterstützt:
|
||||
|
||||
|
|
@ -74,11 +74,11 @@ Wenn ein eingehender Request nicht korrekt validiert wird, wird eine `400`-<abbr
|
|||
|
||||
## `GZipMiddleware` { #gzipmiddleware }
|
||||
|
||||
Verarbeitet GZip-Responses für alle Requests, die `"gzip"` im `Accept-Encoding`-Header enthalten.
|
||||
Verarbeitet GZip-Responses für alle Requests, die „gzip“ im `Accept-Encoding`-Header enthalten.
|
||||
|
||||
Diese Middleware verarbeitet sowohl Standard- als auch Streaming-Responses.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
|
||||
|
||||
Die folgenden Argumente werden unterstützt:
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Das wird normalerweise als **Web<abbr title="Haken, Einhängepunkt">hook</abbr>*
|
|||
|
||||
## Webhooks-Schritte { #webhooks-steps }
|
||||
|
||||
Der Prozess besteht normalerweise darin, dass **Sie in Ihrem Code definieren**, welche Nachricht Sie senden möchten, den **Body des Requests**.
|
||||
Der Prozess besteht normalerweise darin, dass **Sie in Ihrem Code definieren**, welche Nachricht Sie senden möchten, den **Requestbody**.
|
||||
|
||||
Sie definieren auch auf irgendeine Weise, in welchen **Momenten** Ihre App diese Requests oder Events senden wird.
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ Die gesamte **Logik** zur Registrierung der URLs für Webhooks und der Code zum
|
|||
|
||||
## Webhooks mit **FastAPI** und OpenAPI dokumentieren { #documenting-webhooks-with-fastapi-and-openapi }
|
||||
|
||||
Mit **FastAPI**, mithilfe von OpenAPI, können Sie die Namen dieser Webhooks, die Arten von HTTP-Operationen, die Ihre App senden kann (z. B. `POST`, `PUT`, usw.) und die Request**bodys** definieren, die Ihre App senden würde.
|
||||
Mit **FastAPI**, mithilfe von OpenAPI, können Sie die Namen dieser Webhooks, die Arten von HTTP-Operationen, die Ihre App senden kann (z. B. `POST`, `PUT`, usw.) und die **Requestbodys** definieren, die Ihre App senden würde.
|
||||
|
||||
Dies kann es Ihren Benutzern viel einfacher machen, **deren APIs zu implementieren**, um Ihre **Webhook**-Requests zu empfangen. Möglicherweise können diese sogar einen Teil ihres eigenen API-Codes automatisch generieren.
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ Webhooks sind in OpenAPI 3.1.0 und höher verfügbar und werden von FastAPI `0.9
|
|||
|
||||
Wenn Sie eine **FastAPI**-Anwendung erstellen, gibt es ein `webhooks`-Attribut, das Sie verwenden können, um *Webhooks* zu definieren, genauso wie Sie *Pfadoperationen* definieren würden, zum Beispiel mit `@app.webhooks.post()`.
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
|
||||
|
||||
Die von Ihnen definierten Webhooks landen im **OpenAPI**-Schema und der automatischen **Dokumentations-Oberfläche**.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Mit dem Parameter `operation_id` können Sie die OpenAPI `operationId` festlegen
|
|||
|
||||
Sie müssten sicherstellen, dass sie für jede Operation eindeutig ist.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
|
||||
|
||||
### Verwendung des Namens der *Pfadoperation-Funktion* als operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ Wenn Sie die Funktionsnamen Ihrer API als `operationId`s verwenden möchten, kö
|
|||
|
||||
Sie sollten dies tun, nachdem Sie alle Ihre *Pfadoperationen* hinzugefügt haben.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ Auch wenn diese sich in unterschiedlichen Modulen (Python-Dateien) befinden.
|
|||
|
||||
Um eine *Pfadoperation* aus dem generierten OpenAPI-Schema (und damit aus den automatischen Dokumentationssystemen) auszuschließen, verwenden Sie den Parameter `include_in_schema` und setzen Sie ihn auf `False`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
|
||||
|
||||
## Fortgeschrittene Beschreibung mittels Docstring { #advanced-description-from-docstring }
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ Sie können das OpenAPI-Schema für eine *Pfadoperation* erweitern, indem Sie de
|
|||
|
||||
Dieses `openapi_extra` kann beispielsweise hilfreich sein, um [OpenAPI-Erweiterungen](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) zu deklarieren:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
|
||||
|
||||
Wenn Sie die automatische API-Dokumentation öffnen, wird Ihre Erweiterung am Ende der spezifischen *Pfadoperation* angezeigt.
|
||||
|
||||
|
|
@ -135,13 +135,13 @@ Das <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash
|
|||
|
||||
Sie können dem automatisch generierten Schema also zusätzliche Daten hinzufügen.
|
||||
|
||||
Sie könnten sich beispielsweise dafür entscheiden, den <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> mit Ihrem eigenen Code zu lesen und zu validieren, ohne die automatischen Funktionen von FastAPI mit Pydantic zu verwenden, aber Sie könnten den Request trotzdem im OpenAPI-Schema definieren wollen.
|
||||
Sie könnten sich beispielsweise dafür entscheiden, den <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> mit Ihrem eigenen Code zu lesen und zu validieren, ohne FastAPIs automatische Funktionen mit Pydantic zu verwenden, aber Sie könnten den Request trotzdem im OpenAPI-Schema definieren wollen.
|
||||
|
||||
Das könnte man mit `openapi_extra` machen:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
|
||||
|
||||
In diesem Beispiel haben wir kein Pydantic-Modell deklariert. Tatsächlich wird der Requestbody nicht einmal als JSON <abbr title="von einem einfachen Format, wie Bytes, in Python-Objekte konvertieren">geparst</abbr>, sondern direkt als `bytes` gelesen und die Funktion `magic_data_reader()` wäre dafür verantwortlich, ihn in irgendeiner Weise zu parsen.
|
||||
In diesem Beispiel haben wir kein Pydantic-Modell deklariert. Tatsächlich wird der Requestbody nicht einmal als JSON <dfn title="von einem einfachen Format, wie Bytes, in Python-Objekte konvertiert">geparst</dfn>, sondern direkt als `bytes` gelesen und die Funktion `magic_data_reader()` wäre dafür verantwortlich, ihn in irgendeiner Weise zu parsen.
|
||||
|
||||
Dennoch können wir das zu erwartende Schema für den Requestbody deklarieren.
|
||||
|
||||
|
|
@ -151,9 +151,9 @@ Mit demselben Trick könnten Sie ein Pydantic-Modell verwenden, um das JSON-Sche
|
|||
|
||||
Und Sie könnten dies auch tun, wenn der Datentyp im Request nicht JSON ist.
|
||||
|
||||
In der folgenden Anwendung verwenden wir beispielsweise weder die integrierte Funktionalität von FastAPI zum Extrahieren des JSON-Schemas aus Pydantic-Modellen noch die automatische Validierung für JSON. Tatsächlich deklarieren wir den Request-Content-Type als YAML und nicht als JSON:
|
||||
In der folgenden Anwendung verwenden wir beispielsweise weder FastAPIs integrierte Funktionalität zum Extrahieren des JSON-Schemas aus Pydantic-Modellen noch die automatische Validierung für JSON. Tatsächlich deklarieren wir den Request-Content-Type als YAML und nicht als JSON:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
|
||||
|
||||
Obwohl wir nicht die standardmäßig integrierte Funktionalität verwenden, verwenden wir dennoch ein Pydantic-Modell, um das JSON-Schema für die Daten, die wir in YAML empfangen möchten, manuell zu generieren.
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ Dann verwenden wir den Request direkt und extrahieren den Body als `bytes`. Das
|
|||
|
||||
Und dann parsen wir in unserem Code diesen YAML-Inhalt direkt und verwenden dann wieder dasselbe Pydantic-Modell, um den YAML-Inhalt zu validieren:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion*
|
|||
|
||||
Anschließend können Sie den `status_code` in diesem *vorübergehenden* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt festlegen.
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
|
||||
|
||||
Und dann können Sie jedes benötigte Objekt zurückgeben, wie Sie es normalerweise tun würden (ein `dict`, ein Datenbankmodell usw.).
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion*
|
|||
|
||||
Und dann können Sie Cookies in diesem *vorübergehenden* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt setzen.
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
|
||||
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
|
||||
|
||||
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ Dazu können Sie eine Response erstellen, wie unter [Eine Response direkt zurüc
|
|||
|
||||
Setzen Sie dann Cookies darin und geben Sie sie dann zurück:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ Nehmen wir an, Sie möchten eine <a href="https://en.wikipedia.org/wiki/XML" cla
|
|||
|
||||
Sie könnten Ihren XML-Inhalt als String in eine `Response` einfügen und sie zurückgeben:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
## Anmerkungen { #notes }
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion*
|
|||
|
||||
Und dann können Sie Header in diesem *vorübergehenden* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt festlegen.
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
|
||||
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
|
||||
|
||||
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ Sie können auch Header hinzufügen, wenn Sie eine `Response` direkt zurückgebe
|
|||
|
||||
Erstellen Sie eine Response wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} beschrieben und übergeben Sie die Header als zusätzlichen Parameter:
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// note | Technische Details
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Wenn Sie dann den Benutzernamen und das Passwort eingeben, sendet der Browser di
|
|||
* Diese gibt ein Objekt vom Typ `HTTPBasicCredentials` zurück:
|
||||
* Es enthält den gesendeten `username` und das gesendete `password`.
|
||||
|
||||
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
|
||||
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
|
||||
|
||||
Wenn Sie versuchen, die URL zum ersten Mal zu öffnen (oder in der Dokumentation auf den Button „Execute“ zu klicken), wird der Browser Sie nach Ihrem Benutzernamen und Passwort fragen:
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ Um dies zu lösen, konvertieren wir zunächst den `username` und das `password`
|
|||
|
||||
Dann können wir `secrets.compare_digest()` verwenden, um sicherzustellen, dass `credentials.username` `"stanleyjobson"` und `credentials.password` `"swordfish"` ist.
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
|
||||
|
||||
Dies wäre das gleiche wie:
|
||||
|
||||
|
|
@ -104,4 +104,4 @@ So ist Ihr Anwendungscode, dank der Verwendung von `secrets.compare_digest()`, v
|
|||
|
||||
Nachdem Sie festgestellt haben, dass die Anmeldeinformationen falsch sind, geben Sie eine `HTTPException` mit dem Statuscode 401 zurück (derselbe, der auch zurückgegeben wird, wenn keine Anmeldeinformationen angegeben werden) und fügen den Header `WWW-Authenticate` hinzu, damit der Browser die Anmeldeaufforderung erneut anzeigt:
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
|
||||
|
|
|
|||
|
|
@ -46,12 +46,6 @@ $ pip install "fastapi[all]"
|
|||
|
||||
</div>
|
||||
|
||||
/// info | Info
|
||||
|
||||
In Pydantic v1 war es im Hauptpackage enthalten. Jetzt wird es als unabhängiges Package verteilt, sodass Sie wählen können, ob Sie es installieren möchten oder nicht, falls Sie die Funktionalität nicht benötigen.
|
||||
|
||||
///
|
||||
|
||||
### Das `Settings`-Objekt erstellen { #create-the-settings-object }
|
||||
|
||||
Importieren Sie `BaseSettings` aus Pydantic und erstellen Sie eine Unterklasse, ganz ähnlich wie bei einem Pydantic-Modell.
|
||||
|
|
@ -60,7 +54,7 @@ Auf die gleiche Weise wie bei Pydantic-Modellen deklarieren Sie Klassenattribute
|
|||
|
||||
Sie können dieselben Validierungs-Funktionen und -Tools verwenden, die Sie für Pydantic-Modelle verwenden, z. B. verschiedene Datentypen und zusätzliche Validierungen mit `Field()`.
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -76,7 +70,7 @@ Als Nächstes werden die Daten konvertiert und validiert. Wenn Sie also dieses `
|
|||
|
||||
Dann können Sie das neue `settings`-Objekt in Ihrer Anwendung verwenden:
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
|
||||
|
||||
### Den Server ausführen { #run-the-server }
|
||||
|
||||
|
|
@ -110,11 +104,11 @@ Sie könnten diese Einstellungen in eine andere Moduldatei einfügen, wie Sie in
|
|||
|
||||
Sie könnten beispielsweise eine Datei `config.py` haben mit:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/config.py *}
|
||||
{* ../../docs_src/settings/app01_py310/config.py *}
|
||||
|
||||
Und dann verwenden Sie diese in einer Datei `main.py`:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
|
||||
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -132,7 +126,7 @@ Dies könnte besonders beim Testen nützlich sein, da es sehr einfach ist, eine
|
|||
|
||||
Ausgehend vom vorherigen Beispiel könnte Ihre Datei `config.py` so aussehen:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
|
||||
|
||||
Beachten Sie, dass wir jetzt keine Standardinstanz `settings = Settings()` erstellen.
|
||||
|
||||
|
|
@ -140,7 +134,7 @@ Beachten Sie, dass wir jetzt keine Standardinstanz `settings = Settings()` erste
|
|||
|
||||
Jetzt erstellen wir eine Abhängigkeit, die ein neues `config.Settings()` zurückgibt.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -152,13 +146,13 @@ Im Moment nehmen Sie an, dass `get_settings()` eine normale Funktion ist.
|
|||
|
||||
Und dann können wir das von der *Pfadoperation-Funktion* als Abhängigkeit einfordern und es überall dort verwenden, wo wir es brauchen.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
|
||||
|
||||
### Einstellungen und Tests { #settings-and-testing }
|
||||
|
||||
Dann wäre es sehr einfach, beim Testen ein anderes Einstellungsobjekt bereitzustellen, indem man eine Abhängigkeitsüberschreibung für `get_settings` erstellt:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
|
||||
|
||||
Bei der Abhängigkeitsüberschreibung legen wir einen neuen Wert für `admin_email` fest, wenn wir das neue `Settings`-Objekt erstellen, und geben dann dieses neue Objekt zurück.
|
||||
|
||||
|
|
@ -199,7 +193,7 @@ APP_NAME="ChimichangApp"
|
|||
|
||||
Und dann aktualisieren Sie Ihre `config.py` mit:
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -232,7 +226,7 @@ würden wir dieses Objekt für jeden Request erstellen und die `.env`-Datei für
|
|||
|
||||
Da wir jedoch den `@lru_cache`-Dekorator oben verwenden, wird das `Settings`-Objekt nur einmal erstellt, nämlich beim ersten Aufruf. ✔️
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
|
||||
|
||||
Dann wird bei allen nachfolgenden Aufrufen von `get_settings()`, in den Abhängigkeiten für darauffolgende Requests, dasselbe Objekt zurückgegeben, das beim ersten Aufruf zurückgegeben wurde, anstatt den Code von `get_settings()` erneut auszuführen und ein neues `Settings`-Objekt zu erstellen.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Wenn Sie zwei unabhängige FastAPI-Anwendungen mit deren eigenen unabhängigen O
|
|||
|
||||
Erstellen Sie zunächst die Hauptanwendung **FastAPI** und deren *Pfadoperationen*:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
|
||||
|
||||
### Unteranwendung { #sub-application }
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ Erstellen Sie dann Ihre Unteranwendung und deren *Pfadoperationen*.
|
|||
|
||||
Diese Unteranwendung ist nur eine weitere Standard-FastAPI-Anwendung, aber diese wird „gemountet“:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
|
||||
|
||||
### Die Unteranwendung mounten { #mount-the-sub-application }
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Mounten Sie in Ihrer Top-Level-Anwendung `app` die Unteranwendung `subapi`.
|
|||
|
||||
In diesem Fall wird sie im Pfad `/subapi` gemountet:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
|
||||
|
||||
### Die automatische API-Dokumentation testen { #check-the-automatic-api-docs }
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ $ pip install jinja2
|
|||
* Deklarieren Sie einen `<abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr>`-Parameter in der *Pfadoperation*, welcher ein Template zurückgibt.
|
||||
* Verwenden Sie die von Ihnen erstellten `templates`, um eine `TemplateResponse` zu rendern und zurückzugeben, übergeben Sie den Namen des Templates, das Requestobjekt und ein „Kontext“-<abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> mit Schlüssel-Wert-Paaren, die innerhalb des Jinja2-Templates verwendet werden sollen.
|
||||
|
||||
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
|
||||
{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Wenn Sie `lifespan` in Ihren Tests ausführen müssen, können Sie den `TestClient` mit einer `with`-Anweisung verwenden:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
|
||||
|
||||
Sie können mehr Details unter [„Lifespan in Tests ausführen in der offiziellen Starlette-Dokumentation.“](https://www.starlette.dev/lifespan/#running-lifespan-in-tests) nachlesen.
|
||||
|
||||
Für die deprecateten Events <abbr title="Hochfahren">`startup`</abbr> und <abbr title="Herunterfahren">`shutdown`</abbr> können Sie den `TestClient` wie folgt verwenden:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
|
||||
{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Sie können den schon bekannten `TestClient` zum Testen von WebSockets verwenden
|
|||
|
||||
Dazu verwenden Sie den `TestClient` in einer `with`-Anweisung, eine Verbindung zum WebSocket herstellend:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
|
||||
{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Angenommen, Sie möchten auf die IP-Adresse/den Host des Clients in Ihrer *Pfado
|
|||
|
||||
Dazu müssen Sie direkt auf den Request zugreifen.
|
||||
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
|
||||
|
||||
Durch die Deklaration eines *Pfadoperation-Funktionsparameters*, dessen Typ der `Request` ist, weiß **FastAPI**, dass es den `Request` diesem Parameter übergeben soll.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ In der Produktion hätten Sie eine der oben genannten Optionen.
|
|||
|
||||
Aber es ist der einfachste Weg, sich auf die Serverseite von WebSockets zu konzentrieren und ein funktionierendes Beispiel zu haben:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
|
||||
|
||||
## Einen `websocket` erstellen { #create-a-websocket }
|
||||
|
||||
Erstellen Sie in Ihrer **FastAPI**-Anwendung einen `websocket`:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
|
||||
|
||||
/// note | Technische Details
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ Sie könnten auch `from starlette.websockets import WebSocket` verwenden.
|
|||
|
||||
In Ihrer WebSocket-Route können Sie Nachrichten `await`en und Nachrichten senden.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
|
||||
|
||||
Sie können Binär-, Text- und JSON-Daten empfangen und senden.
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ Damit können Sie den WebSocket verbinden und dann Nachrichten senden und empfan
|
|||
|
||||
Wenn eine WebSocket-Verbindung geschlossen wird, löst `await websocket.receive_text()` eine `WebSocketDisconnect`-Exception aus, die Sie dann wie in folgendem Beispiel abfangen und behandeln können.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
|
||||
{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
|
||||
|
||||
Zum Ausprobieren:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Wrappen Sie dann die WSGI-Anwendung (z. B. Flask) mit der Middleware.
|
|||
|
||||
Und dann mounten Sie das auf einem Pfad.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Es ist das beliebteste Python-Framework und genießt großes Vertrauen. Es wird
|
|||
|
||||
Es ist relativ eng mit relationalen Datenbanken (wie MySQL oder PostgreSQL) gekoppelt, daher ist es nicht sehr einfach, eine NoSQL-Datenbank (wie Couchbase, MongoDB, Cassandra, usw.) als Hauptspeicherengine zu verwenden.
|
||||
|
||||
Es wurde erstellt, um den HTML-Code im Backend zu generieren, nicht um APIs zu erstellen, die von einem modernen Frontend (wie React, Vue.js und Angular) oder von anderen Systemen (wie <abbr title="Internet of Things – Internet der Dinge">IoT</abbr>-Geräten) verwendet werden, um mit ihm zu kommunizieren.
|
||||
Es wurde erstellt, um den HTML-Code im Backend zu generieren, nicht um APIs zu erstellen, die von einem modernen Frontend (wie React, Vue.js und Angular) oder von anderen Systemen (wie <abbr title="Internet of Things - Internet der Dinge">IoT</abbr>-Geräten) verwendet werden, um mit ihm zu kommunizieren.
|
||||
|
||||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ Aus diesem Grund heißt es auf der offiziellen Website:
|
|||
|
||||
> Requests ist eines der am häufigsten heruntergeladenen Python-Packages aller Zeiten
|
||||
|
||||
Die Art und Weise, wie Sie es verwenden, ist sehr einfach. Um beispielsweise einen `GET`-<abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> zu machen, würden Sie schreiben:
|
||||
Die Art und Weise, wie Sie es verwenden, ist sehr einfach. Um beispielsweise einen `GET`-<abbr title="Request - Anfrage: Daten, die der Client zum Server sendet">Request</abbr> zu machen, würden Sie schreiben:
|
||||
|
||||
```Python
|
||||
response = requests.get("http://example.com/some/url")
|
||||
|
|
@ -137,7 +137,7 @@ Es gibt mehrere Flask REST Frameworks, aber nachdem ich die Zeit und Arbeit inve
|
|||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
|
||||
|
||||
Eine der von API-Systemen benötigten Hauptfunktionen ist die Daten-<abbr title="Auch „Marshalling“, „Konvertierung“ genannt">„Serialisierung“</abbr>, welche Daten aus dem Code (Python) entnimmt und in etwas umwandelt, was durch das Netzwerk gesendet werden kann. Beispielsweise das Konvertieren eines Objekts, welches Daten aus einer Datenbank enthält, in ein JSON-Objekt. Konvertieren von `datetime`-Objekten in Strings, usw.
|
||||
Eine der von API-Systemen benötigten Hauptfunktionen ist die Daten-<dfn title="auch genannt: Marshalling, Konvertierung">„Serialisierung“</dfn>, welche Daten aus dem Code (Python) entnimmt und in etwas umwandelt, was durch das Netzwerk gesendet werden kann. Beispielsweise das Konvertieren eines Objekts, welches Daten aus einer Datenbank enthält, in ein JSON-Objekt. Konvertieren von `datetime`-Objekten in Strings, usw.
|
||||
|
||||
Eine weitere wichtige Funktion, benötigt von APIs, ist die Datenvalidierung, welche sicherstellt, dass die Daten unter gegebenen Umständen gültig sind. Zum Beispiel, dass ein Feld ein `int` ist und kein zufälliger String. Das ist besonders nützlich für hereinkommende Daten.
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ Ohne ein Datenvalidierungssystem müssten Sie alle Prüfungen manuell im Code du
|
|||
|
||||
Für diese Funktionen wurde Marshmallow entwickelt. Es ist eine großartige Bibliothek und ich habe sie schon oft genutzt.
|
||||
|
||||
Aber sie wurde erstellt, bevor Typhinweise in Python existierten. Um also ein <abbr title="die Definition, wie Daten geformt sein sollen">Schema</abbr> zu definieren, müssen Sie bestimmte Werkzeuge und Klassen verwenden, die von Marshmallow bereitgestellt werden.
|
||||
Aber sie wurde erstellt, bevor Typhinweise in Python existierten. Um also ein <dfn title="die Definition, wie Daten geformt sein sollen">Schema</dfn> zu definieren, müssen Sie bestimmte Werkzeuge und Klassen verwenden, die von Marshmallow bereitgestellt werden.
|
||||
|
||||
/// check | Inspirierte **FastAPI**
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ Code zu verwenden, um „Schemas“ zu definieren, welche Datentypen und Validie
|
|||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
|
||||
|
||||
Eine weitere wichtige Funktion, die von APIs benötigt wird, ist das <abbr title="Lesen und Konvertieren nach Python-Daten">Parsen</abbr> von Daten aus eingehenden Requests.
|
||||
Eine weitere wichtige Funktion, die von APIs benötigt wird, ist das <dfn title="Lesen und Konvertieren nach Python-Daten">Parsen</dfn> von Daten aus eingehenden Requests.
|
||||
|
||||
Webargs wurde entwickelt, um dieses für mehrere Frameworks, einschließlich Flask, bereitzustellen.
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ Aus diesem Grund basiert **FastAPI** auf Starlette, da dieses das schnellste ver
|
|||
|
||||
Falcon ist ein weiteres leistungsstarkes Python-Framework. Es ist minimalistisch konzipiert und dient als Grundlage für andere Frameworks wie Hug.
|
||||
|
||||
Es ist so konzipiert, dass es über Funktionen verfügt, welche zwei Parameter empfangen, einen <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">„Request“</abbr> und eine <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">„Response“</abbr>. Dann „lesen“ Sie Teile des Requests und „schreiben“ Teile der Response. Aufgrund dieses Designs ist es nicht möglich, Request-Parameter und -Bodys mit Standard-Python-Typhinweisen als Funktionsparameter zu deklarieren.
|
||||
Es ist so konzipiert, dass es über Funktionen verfügt, welche zwei Parameter empfangen, einen <abbr title="Request - Anfrage: Daten, die der Client zum Server sendet">„Request“</abbr> und eine <abbr title="Response - Antwort: Daten, die der Server zum anfragenden Client zurücksendet">„Response“</abbr>. Dann „lesen“ Sie Teile des Requests und „schreiben“ Teile der Response. Aufgrund dieses Designs ist es nicht möglich, Request-Parameter und -Bodys mit Standard-Python-Typhinweisen als Funktionsparameter zu deklarieren.
|
||||
|
||||
Daher müssen Datenvalidierung, Serialisierung und Dokumentation im Code und nicht automatisch erfolgen. Oder sie müssen als Framework oberhalb von Falcon implementiert werden, so wie Hug. Dieselbe Unterscheidung findet auch in anderen Frameworks statt, die vom Design von Falcon inspiriert sind und ein Requestobjekt und ein Responseobjekt als Parameter haben.
|
||||
|
||||
|
|
@ -419,7 +419,7 @@ Die gesamte Datenvalidierung, Datenserialisierung und automatische Modelldokumen
|
|||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
|
||||
|
||||
Starlette ist ein leichtgewichtiges <abbr title="Der neue Standard für die Erstellung asynchroner Python-Webanwendungen">ASGI</abbr>-Framework/Toolkit, welches sich ideal für die Erstellung hochperformanter asynchroner Dienste eignet.
|
||||
Starlette ist ein leichtgewichtiges <dfn title="Der neue Standard für die Erstellung asynchroner Python-Webanwendungen">ASGI</dfn>-Framework/Toolkit, welches sich ideal für die Erstellung hochperformanter asynchroner Dienste eignet.
|
||||
|
||||
Es ist sehr einfach und intuitiv. Es ist so konzipiert, dass es leicht erweiterbar ist und über modulare Komponenten verfügt.
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Die Hierarchie ist wie folgt:
|
|||
* Sie würden eine Anwendung nicht direkt in Uvicorn schreiben. Das würde bedeuten, dass Ihr Code zumindest mehr oder weniger den gesamten von Starlette (oder **FastAPI**) bereitgestellten Code enthalten müsste. Und wenn Sie das täten, hätte Ihre endgültige Anwendung den gleichen Overhead wie bei der Verwendung eines Frameworks und der Minimierung Ihres Anwendungscodes und der Fehler.
|
||||
* Wenn Sie Uvicorn vergleichen, vergleichen Sie es mit Anwendungsservern wie Daphne, Hypercorn, uWSGI, usw.
|
||||
* **Starlette**:
|
||||
* Wird nach Uvicorn die nächstbeste Performanz erbringen. Tatsächlich verwendet Starlette intern Uvicorn, um zu laufen. Daher kann es wahrscheinlich nur „langsamer“ als Uvicorn werden, weil mehr Code ausgeführt werden muss.
|
||||
* Wird nach Uvicorn die nächstbeste Performanz erbringen. Tatsächlich verwendet Starlette Uvicorn, um zu laufen. Daher kann es wahrscheinlich nur „langsamer“ als Uvicorn werden, weil mehr Code ausgeführt werden muss.
|
||||
* Aber es bietet Ihnen die Werkzeuge, um einfache Webanwendungen zu erstellen, mit Routing basierend auf Pfaden, usw.
|
||||
* Wenn Sie Starlette vergleichen, vergleichen Sie es mit Webframeworks (oder Mikroframeworks) wie Sanic, Flask, Django, usw.
|
||||
* **FastAPI**:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# FastAPI bei Cloudanbietern deployen { #deploy-fastapi-on-cloud-providers }
|
||||
|
||||
Sie können praktisch **jeden Cloudanbieter** verwenden, um Ihre FastAPI-Anwendung bereitzustellen.
|
||||
Sie können praktisch **jeden Cloudanbieter** verwenden, um Ihre FastAPI-Anwendung zu deployen.
|
||||
|
||||
In den meisten Fällen bieten die großen Cloudanbieter Anleitungen zum Deployment von FastAPI an.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Sie haben es eilig und kennen sich bereits aus? Springen Sie zum [`Dockerfile` u
|
|||
<summary>Dockerfile-Vorschau 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ Erstellen Sie nun im selben Projektverzeichnis eine Datei `Dockerfile` mit:
|
|||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)!
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
|
|
@ -390,7 +390,7 @@ Wenn Ihr FastAPI eine einzelne Datei ist, zum Beispiel `main.py` ohne ein `./app
|
|||
Dann müssten Sie nur noch die entsprechenden Pfade ändern, um die Datei im `Dockerfile` zu kopieren:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
@ -454,7 +454,7 @@ Ohne die Verwendung von Containern kann es umständlich und schwierig sein, Anwe
|
|||
|
||||
## Replikation – Anzahl der Prozesse { #replication-number-of-processes }
|
||||
|
||||
Wenn Sie einen <abbr title="Eine Gruppe von Maschinen, die so konfiguriert sind, dass sie verbunden sind und auf irgendeine Weise zusammenarbeiten.">Cluster</abbr> von Maschinen mit **Kubernetes**, Docker Swarm Mode, Nomad verwenden, oder einem anderen, ähnlich komplexen System zur Verwaltung verteilter Container auf mehreren Maschinen, möchten Sie wahrscheinlich die **Replikation auf Cluster-Ebene abwickeln**, anstatt in jedem Container einen **Prozessmanager** (wie Uvicorn mit Workern) zu verwenden.
|
||||
Wenn Sie einen <dfn title="Eine Gruppe von Maschinen, die so konfiguriert sind, dass sie verbunden sind und auf irgendeine Weise zusammenarbeiten.">Cluster</dfn> von Maschinen mit **Kubernetes**, Docker Swarm Mode, Nomad verwenden, oder einem anderen, ähnlich komplexen System zur Verwaltung verteilter Container auf mehreren Maschinen, möchten Sie wahrscheinlich die **Replikation auf Cluster-Ebene abwickeln**, anstatt in jedem Container einen **Prozessmanager** (wie Uvicorn mit Workern) zu verwenden.
|
||||
|
||||
Diese verteilten Containerverwaltungssysteme wie Kubernetes verfügen normalerweise über eine integrierte Möglichkeit, die **Replikation von Containern** zu handhaben und gleichzeitig **Load Balancing** für die eingehenden <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> zu unterstützen. Alles auf **Cluster-Ebene**.
|
||||
|
||||
|
|
@ -499,7 +499,7 @@ Natürlich gibt es **Sonderfälle**, in denen Sie **einen Container** mit mehrer
|
|||
In diesen Fällen können Sie die `--workers` Befehlszeilenoption verwenden, um die Anzahl der zu startenden Worker festzulegen:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
|
@ -570,7 +570,7 @@ Wenn Sie ein einfaches Setup mit einem **einzelnen Container** haben, welcher da
|
|||
|
||||
### Docker-Basisimage { #base-docker-image }
|
||||
|
||||
Es gab ein offizielles FastAPI-Docker-Image: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Dieses ist jedoch jetzt veraltet. ⛔️
|
||||
Es gab ein offizielles FastAPI-Docker-Image: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Dieses ist jedoch jetzt deprecatet. ⛔️
|
||||
|
||||
Sie sollten wahrscheinlich **nicht** dieses Basis-Docker-Image (oder ein anderes ähnliches) verwenden.
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ Hier ist ein Beispiel, wie eine HTTPS-API aussehen könnte, Schritt für Schritt
|
|||
|
||||
Alles beginnt wahrscheinlich damit, dass Sie einen **Domainnamen erwerben**. Anschließend konfigurieren Sie ihn in einem DNS-Server (wahrscheinlich beim selben Cloudanbieter).
|
||||
|
||||
Sie würden wahrscheinlich einen Cloud-Server (eine virtuelle Maschine) oder etwas Ähnliches bekommen, und dieser hätte eine <abbr title="Sie ändert sich nicht">feste</abbr> **öffentliche IP-Adresse**.
|
||||
Sie würden wahrscheinlich einen Cloud-Server (eine virtuelle Maschine) oder etwas Ähnliches bekommen, und dieser hätte eine <dfn title="Ändert sich im Laufe der Zeit nicht. Nicht dynamisch.">feste</dfn> **öffentliche IP-Adresse**.
|
||||
|
||||
In dem oder den DNS-Server(n) würden Sie einen Eintrag (einen „`A record`“) konfigurieren, um mit **Ihrer Domain** auf die öffentliche **IP-Adresse Ihres Servers** zu verweisen.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Das Deployment einer **FastAPI**-Anwendung ist relativ einfach.
|
|||
|
||||
## Was bedeutet Deployment { #what-does-deployment-mean }
|
||||
|
||||
<abbr title="Bereitstellen der Anwendung">**Deployment**</abbr> bedeutet, die notwendigen Schritte durchzuführen, um die Anwendung **für die Endbenutzer verfügbar** zu machen.
|
||||
<abbr title="Bereitstellen der Anwendung">**Deployment**</abbr> bedeutet, die notwendigen Schritte durchzuführen, um die Anwendung **für die Benutzer verfügbar** zu machen.
|
||||
|
||||
Bei einer **Web-API** bedeutet das normalerweise, diese auf einem **entfernten Rechner** zu platzieren, mit einem **Serverprogramm**, welches gute Leistung, Stabilität, usw. bietet, damit Ihre **Benutzer** auf die Anwendung effizient und ohne Unterbrechungen oder Probleme **zugreifen** können.
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ In der Liste der Deployment-Konzepte von oben würde die Verwendung von Workern
|
|||
|
||||
* **Sicherheit – HTTPS**
|
||||
* **Beim Hochfahren ausführen**
|
||||
* **Neustarts**
|
||||
* ***Neustarts***
|
||||
* Replikation (die Anzahl der laufenden Prozesse)
|
||||
* **Arbeitsspeicher**
|
||||
* **Schritte vor dem Start**
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
### Basiert auf offenen Standards { #based-on-open-standards }
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> für die Erstellung von APIs, inklusive Deklarationen von <abbr title="auch bekannt als: Endpunkte, Routen">Pfad</abbr>-<abbr title="auch bekannt als HTTP-Methoden, wie POST, GET, PUT, DELETE">Operationen</abbr>, Parametern, <abbr title="Anfragekörper">Requestbodys</abbr>, Sicherheit, usw.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> für die Erstellung von APIs, inklusive Deklarationen von <dfn title="auch bekannt als: Endpunkte, Routen">Pfad</dfn>-<dfn title="auch bekannt als HTTP-Methoden, wie POST, GET, PUT, DELETE">Operationen</dfn>, Parametern, <abbr title="Anfragekörper">Requestbodys</abbr>, Sicherheit, usw.
|
||||
* Automatische Dokumentation der Datenmodelle mit <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (da OpenAPI selbst auf JSON Schema basiert).
|
||||
* Um diese Standards herum entworfen, nach sorgfältigem Studium. Statt einer nachträglichen Schicht darüber.
|
||||
* Dies ermöglicht auch automatische **Client-Code-Generierung** in vielen Sprachen.
|
||||
|
|
@ -136,7 +136,7 @@ Alles als wiederverwendbare Tools und Komponenten gebaut, die einfach in Ihre Sy
|
|||
|
||||
### Dependency Injection { #dependency-injection }
|
||||
|
||||
FastAPI enthält ein extrem einfach zu verwendendes, aber extrem mächtiges <abbr title='auch bekannt als „Komponenten“, „Resourcen“, „Dienste“, „Dienstanbieter“'><strong>Dependency Injection</strong></abbr> System.
|
||||
FastAPI enthält ein extrem einfach zu verwendendes, aber extrem mächtiges <dfn title='auch bekannt als: "Komponenten", "Resourcen", "Dienste", "Dienstanbieter"'><strong>Dependency Injection</strong></dfn> System.
|
||||
|
||||
* Selbst Abhängigkeiten können Abhängigkeiten haben, woraus eine Hierarchie oder ein **„Graph“ von Abhängigkeiten** entsteht.
|
||||
* Alles **automatisch gehandhabt** durch das Framework.
|
||||
|
|
@ -153,8 +153,8 @@ Jede Integration wurde so entworfen, dass sie so einfach zu nutzen ist (mit Abh
|
|||
|
||||
### Getestet { #tested }
|
||||
|
||||
* 100 % <abbr title="Der Prozentsatz an Code, der automatisch getestet wird">Testabdeckung</abbr>.
|
||||
* 100 % <abbr title="Python-Typannotationen, mit denen Ihr Editor und andere externe Werkzeuge Sie besser unterstützen können">Typen annotiert</abbr>.
|
||||
* 100 % <dfn title="Der Prozentsatz an Code, der automatisch getestet wird">Testabdeckung</dfn>.
|
||||
* 100 % <dfn title="Python-Typannotationen, mit denen Ihr Editor und andere externe Werkzeuge Sie besser unterstützen können">Typen annotiert</dfn>.
|
||||
* Verwendet in Produktionsanwendungen.
|
||||
|
||||
## Starlette Merkmale { #starlette-features }
|
||||
|
|
@ -179,7 +179,7 @@ Mit **FastAPI** bekommen Sie alles von **Starlette** (da FastAPI nur Starlette a
|
|||
|
||||
**FastAPI** ist vollkommen kompatibel (und basiert auf) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Das bedeutet, wenn Sie eigenen Pydantic Quellcode haben, funktioniert der.
|
||||
|
||||
Inklusive externer Bibliotheken, die auf Pydantic basieren, wie <abbr title="Object-Relational Mapper – Objektrelationaler Abbilder">ORM</abbr>s, <abbr title="Object-Document Mapper – Objekt-Dokument-Abbilder">ODM</abbr>s für Datenbanken.
|
||||
Inklusive externer Bibliotheken, die auf Pydantic basieren, wie <abbr title="Object-Relational Mapper - Objektrelationaler Mapper">ORM</abbr>s, <abbr title="Object-Document Mapper - Objekt-Dokument-Mapper">ODM</abbr>s für Datenbanken.
|
||||
|
||||
Daher können Sie in vielen Fällen das Objekt eines Requests **direkt zur Datenbank** schicken, weil alles automatisch validiert wird.
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ Mit **FastAPI** bekommen Sie alle Funktionen von **Pydantic** (da FastAPI für d
|
|||
* **Kein Kopfzerbrechen**:
|
||||
* Keine neue Schemadefinition-Mikrosprache zu lernen.
|
||||
* Wenn Sie Pythons Typen kennen, wissen Sie, wie man Pydantic verwendet.
|
||||
* Gutes Zusammenspiel mit Ihrer/Ihrem **<abbr title="Integrated Development Environment – Integrierte Entwicklungsumgebung: Ähnlich einem Code-Editor">IDE</abbr>/<abbr title="Ein Programm, das Fehler im Quellcode sucht">Linter</abbr>/Gehirn**:
|
||||
* Gutes Zusammenspiel mit Ihrer/Ihrem **<abbr title="Integrated Development Environment - Integrierte Entwicklungsumgebung: Ähnlich einem Code-Editor">IDE</abbr>/<dfn title="Ein Programm, das Fehler im Quellcode sucht">Linter</dfn>/Gehirn**:
|
||||
* Weil Pydantics Datenstrukturen einfach nur Instanzen ihrer definierten Klassen sind; Autovervollständigung, Linting, mypy und Ihre Intuition sollten alle einwandfrei mit Ihren validierten Daten funktionieren.
|
||||
* Validierung von **komplexen Strukturen**:
|
||||
* Benutzung von hierarchischen Pydantic-Modellen, Python-`typing`s `List` und `Dict`, etc.
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ Nachdem ich mehrere Alternativen getestet hatte, entschied ich, dass ich <a href
|
|||
|
||||
Dann habe ich zu dessen Code beigetragen, um es vollständig mit JSON Schema kompatibel zu machen, und so verschiedene Möglichkeiten zum Definieren von einschränkenden Deklarationen (Constraints) zu unterstützen, und die Editorunterstützung (Typprüfungen, Codevervollständigung) zu verbessern, basierend auf den Tests in mehreren Editoren.
|
||||
|
||||
Während der Entwicklung habe ich auch zu <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a> beigetragen, der anderen Schlüsselanforderung.
|
||||
Während der Entwicklung habe ich auch zu <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a> beigetragen, die andere Schlüsselanforderung.
|
||||
|
||||
## Entwicklung { #development }
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Aber falls Ihre Clients aus irgendeinem Grund vom alten Verhalten abhängen, kö
|
|||
|
||||
Sie können beispielsweise eine Unterklasse von `HTTPBearer` erstellen, die einen Fehler `403 Forbidden` zurückgibt, statt des Default-`401 Unauthorized`-Fehlers:
|
||||
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Sie können problemlos dieselben Pydantic-Einstellungen verwenden, um Ihre gener
|
|||
|
||||
Zum Beispiel:
|
||||
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
|
||||
|
||||
Hier deklarieren wir die Einstellung `openapi_url` mit dem gleichen Defaultwert `"/openapi.json"`.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Ohne Änderung der Einstellungen ist die Syntaxhervorhebung standardmäßig akti
|
|||
|
||||
Sie können sie jedoch deaktivieren, indem Sie `syntaxHighlight` auf `False` setzen:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
|
||||
|
||||
... und dann zeigt die Swagger-Oberfläche die Syntaxhervorhebung nicht mehr an:
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ Sie können sie jedoch deaktivieren, indem Sie `syntaxHighlight` auf `False` set
|
|||
|
||||
Auf die gleiche Weise könnten Sie das Theme der Syntaxhervorhebung mit dem Schlüssel `"syntaxHighlight.theme"` festlegen (beachten Sie, dass er einen Punkt in der Mitte hat):
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Obige Konfiguration würde das Theme für die Farbe der Syntaxhervorhebung ändern:
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ Sie können jede davon überschreiben, indem Sie im Argument `swagger_ui_paramet
|
|||
|
||||
Um beispielsweise `deepLinking` zu deaktivieren, könnten Sie folgende Einstellungen an `swagger_ui_parameters` übergeben:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
|
||||
|
||||
## Andere Parameter der Swagger-Oberfläche { #other-swagger-ui-parameters }
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Der erste Schritt besteht darin, die automatischen Dokumentationen zu deaktivier
|
|||
|
||||
Um diese zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
|
||||
|
||||
### Die benutzerdefinierten Dokumentationen hinzufügen { #include-the-custom-docs }
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ Sie können die internen Funktionen von FastAPI wiederverwenden, um die HTML-Sei
|
|||
|
||||
Und ähnlich für ReDoc ...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ Swagger UI erledigt das hinter den Kulissen für Sie, benötigt aber diesen „U
|
|||
|
||||
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
|
||||
|
||||
### Es testen { #test-it }
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ Danach könnte Ihre Dateistruktur wie folgt aussehen:
|
|||
* Importieren Sie `StaticFiles`.
|
||||
* „Mounten“ Sie eine `StaticFiles()`-Instanz in einem bestimmten Pfad.
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
|
||||
|
||||
### Die statischen Dateien testen { #test-the-static-files }
|
||||
|
||||
|
|
@ -144,7 +144,7 @@ Wie bei der Verwendung eines benutzerdefinierten CDN besteht der erste Schritt d
|
|||
|
||||
Um sie zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
|
||||
|
||||
### Die benutzerdefinierten Dokumentationen für statische Dateien hinzufügen { #include-the-custom-docs-for-static-files }
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ Auch hier können Sie die internen Funktionen von FastAPI wiederverwenden, um di
|
|||
|
||||
Und ähnlich für ReDoc ...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ Swagger UI erledigt das hinter den Kulissen für Sie, benötigt aber diesen „U
|
|||
|
||||
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
|
||||
|
||||
### Benutzeroberfläche mit statischen Dateien testen { #test-static-files-ui }
|
||||
|
||||
|
|
|
|||
|
|
@ -43,19 +43,19 @@ Fügen wir beispielsweise <a href="https://github.com/Rebilly/ReDoc/blob/master/
|
|||
|
||||
Schreiben Sie zunächst wie gewohnt Ihre ganze **FastAPI**-Anwendung:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
|
||||
|
||||
### Das OpenAPI-Schema generieren { #generate-the-openapi-schema }
|
||||
|
||||
Verwenden Sie dann dieselbe Hilfsfunktion, um das OpenAPI-Schema innerhalb einer `custom_openapi()`-Funktion zu generieren:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
|
||||
|
||||
### Das OpenAPI-Schema ändern { #modify-the-openapi-schema }
|
||||
|
||||
Jetzt können Sie die ReDoc-Erweiterung hinzufügen und dem `info`-„Objekt“ im OpenAPI-Schema ein benutzerdefiniertes `x-logo` hinzufügen:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
|
||||
|
||||
### Zwischenspeichern des OpenAPI-Schemas { #cache-the-openapi-schema }
|
||||
|
||||
|
|
@ -65,13 +65,13 @@ Auf diese Weise muss Ihre Anwendung das Schema nicht jedes Mal generieren, wenn
|
|||
|
||||
Es wird nur einmal generiert und dann wird dasselbe zwischengespeicherte Schema für die nächsten <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> verwendet.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
|
||||
|
||||
### Die Methode überschreiben { #override-the-method }
|
||||
|
||||
Jetzt können Sie die Methode `.openapi()` durch Ihre neue Funktion ersetzen.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
|
||||
|
||||
### Es testen { #check-it }
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ Abhängig von Ihrem Anwendungsfall könnten Sie eine andere Bibliothek vorziehen
|
|||
|
||||
Hier ist eine kleine Vorschau, wie Sie Strawberry mit FastAPI integrieren können:
|
||||
|
||||
{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
|
||||
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
|
||||
|
||||
Weitere Informationen zu Strawberry finden Sie in der <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry-Dokumentation</a>.
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Seine Schlüssel-Merkmale sind:
|
|||
* **Schnell**: Sehr hohe Performanz, auf Augenhöhe mit **NodeJS** und **Go** (dank Starlette und Pydantic). [Eines der schnellsten verfügbaren Python-Frameworks](#performance).
|
||||
* **Schnell zu entwickeln**: Erhöhen Sie die Geschwindigkeit bei der Entwicklung von Features um etwa 200 % bis 300 %. *
|
||||
* **Weniger Bugs**: Verringern Sie die von Menschen (Entwicklern) verursachten Fehler um etwa 40 %. *
|
||||
* **Intuitiv**: Hervorragende Editor-Unterstützung. <abbr title="auch bekannt als Auto-Complete, Autovervollständigung, IntelliSense">Code-Vervollständigung</abbr> überall. Weniger Zeit mit Debuggen verbringen.
|
||||
* **Intuitiv**: Hervorragende Editor-Unterstützung. <dfn title="auch bekannt als Auto-Complete, Autovervollständigung, IntelliSense">Code-Vervollständigung</dfn> überall. Weniger Zeit mit Debuggen verbringen.
|
||||
* **Einfach**: So konzipiert, dass es einfach zu benutzen und zu erlernen ist. Weniger Zeit mit dem Lesen von Dokumentation verbringen.
|
||||
* **Kurz**: Minimieren Sie die Verdoppelung von Code. Mehrere Features aus jeder Parameterdeklaration. Weniger Bugs.
|
||||
* **Robust**: Erhalten Sie produktionsreifen Code. Mit automatischer, interaktiver Dokumentation.
|
||||
|
|
@ -363,12 +363,12 @@ item: Item
|
|||
... und mit dieser einen Deklaration erhalten Sie:
|
||||
|
||||
* Editor-Unterstützung, einschließlich:
|
||||
* Code-Vervollständigung.
|
||||
* Vervollständigung.
|
||||
* Typprüfungen.
|
||||
* Validierung von Daten:
|
||||
* Automatische und eindeutige Fehler, wenn die Daten ungültig sind.
|
||||
* Validierung sogar für tief verschachtelte JSON-Objekte.
|
||||
* <abbr title="auch bekannt als: Serialisierung, Parsen, Marshalling">Konvertierung</abbr> von Eingabedaten: Aus dem Netzwerk kommend, zu Python-Daten und -Typen. Lesen von:
|
||||
* <dfn title="auch bekannt als: Serialisierung, Parsen, Marshalling">Konvertierung</dfn> von Eingabedaten: Aus dem Netzwerk kommend, zu Python-Daten und -Typen. Lesen von:
|
||||
* JSON.
|
||||
* Pfad-Parametern.
|
||||
* Query-Parametern.
|
||||
|
|
@ -376,7 +376,7 @@ item: Item
|
|||
* Headern.
|
||||
* Formularen.
|
||||
* Dateien.
|
||||
* <abbr title="auch bekannt als: Serialisierung, Parsen, Marshalling">Konvertierung</abbr> von Ausgabedaten: Konvertierung von Python-Daten und -Typen zu Netzwerkdaten (als JSON):
|
||||
* <dfn title="auch bekannt als: Serialisierung, Parsen, Marshalling">Konvertierung</dfn> von Ausgabedaten: Konvertierung von Python-Daten und -Typen zu Netzwerkdaten (als JSON):
|
||||
* Konvertieren von Python-Typen (`str`, `int`, `float`, `bool`, `list`, usw.).
|
||||
* `datetime`-Objekte.
|
||||
* `UUID`-Objekte.
|
||||
|
|
@ -439,7 +439,7 @@ Für ein vollständigeres Beispiel, mit weiteren Funktionen, siehe das <a href="
|
|||
|
||||
* Deklaration von **Parametern** von anderen verschiedenen Stellen wie: **Header**, **Cookies**, **Formularfelder** und **Dateien**.
|
||||
* Wie man **Validierungs-Constraints** wie `maximum_length` oder `regex` setzt.
|
||||
* Ein sehr leistungsfähiges und einfach zu bedienendes System für **<abbr title="auch bekannt als Komponenten, Ressourcen, Provider, Services, Injectables">Dependency Injection</abbr>**.
|
||||
* Ein sehr leistungsfähiges und einfach zu bedienendes System für **<dfn title="auch bekannt als Komponenten, Ressourcen, Provider, Services, Injectables">Dependency Injection</dfn>**.
|
||||
* Sicherheit und Authentifizierung, einschließlich Unterstützung für **OAuth2** mit **JWT-Tokens** und **HTTP Basic** Authentifizierung.
|
||||
* Fortgeschrittenere (aber ebenso einfache) Techniken zur Deklaration **tief verschachtelter JSON-Modelle** (dank Pydantic).
|
||||
* **GraphQL**-Integration mit <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> und anderen Bibliotheken.
|
||||
|
|
@ -524,7 +524,7 @@ Verwendet von Starlette:
|
|||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> – erforderlich, wenn Sie den `TestClient` verwenden möchten.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> – erforderlich, wenn Sie die Default-Template-Konfiguration verwenden möchten.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> – erforderlich, wenn Sie Formulare mittels `request.form()` <abbr title="Konvertieren des Strings, der aus einem HTTP-Request stammt, nach Python-Daten">„parsen“</abbr> möchten.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> – erforderlich, wenn Sie Formulare mittels `request.form()` <dfn title="Konvertieren des Strings, der aus einem HTTP-Request stammt, nach Python-Daten">„parsen“</dfn> möchten.
|
||||
|
||||
Verwendet von FastAPI:
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ GitHub-Repository: <a href="https://github.com/tiangolo/full-stack-fastapi-templ
|
|||
- 🦇 „Dark-Mode“-Unterstützung.
|
||||
- 🐋 [Docker Compose](https://www.docker.com) für Entwicklung und Produktion.
|
||||
- 🔒 Sicheres Passwort-Hashing standardmäßig.
|
||||
- 🔑 JWT (JSON Web Token)-Token-Authentifizierung.
|
||||
- 🔑 JWT (JSON Web Token)-Authentifizierung.
|
||||
- 📫 E-Mail-basierte Passwortwiederherstellung.
|
||||
- ✅ Tests mit [Pytest](https://pytest.org).
|
||||
- 📞 [Traefik](https://traefik.io) als Reverse-Proxy / Load Balancer.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Einführung in Python-Typen { #python-types-intro }
|
||||
|
||||
Python hat Unterstützung für optionale <abbr title="englisch: Type hints">„Typhinweise“</abbr> (auch <abbr title="englisch: Type annotations">„Typannotationen“</abbr> genannt).
|
||||
Python hat Unterstützung für optionale „Typhinweise“ (auch „Typannotationen“ genannt).
|
||||
|
||||
Diese **„Typhinweise“** oder -Annotationen sind eine spezielle Syntax, die es erlaubt, den <abbr title="zum Beispiel: str, int, float, bool">Typ</abbr> einer Variablen zu deklarieren.
|
||||
Diese **„Typhinweise“** oder -Annotationen sind eine spezielle Syntax, die es erlaubt, den <dfn title="zum Beispiel: str, int, float, bool">Typ</dfn> einer Variablen zu deklarieren.
|
||||
|
||||
Durch das Deklarieren von Typen für Ihre Variablen können Editoren und Tools bessere Unterstützung bieten.
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ Wenn Sie ein Python-Experte sind und bereits alles über Typhinweise wissen, üb
|
|||
|
||||
Fangen wir mit einem einfachen Beispiel an:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py *}
|
||||
|
||||
Dieses Programm gibt aus:
|
||||
|
||||
|
|
@ -34,9 +34,9 @@ Die Funktion macht Folgendes:
|
|||
|
||||
* Nimmt einen `first_name` und `last_name`.
|
||||
* Schreibt den ersten Buchstaben eines jeden Wortes groß, mithilfe von `title()`.
|
||||
* <abbr title="Fügt sie zu einer Einheit zusammen. Mit dem Inhalt des einen nach dem anderen.">Verkettet</abbr> sie mit einem Leerzeichen in der Mitte.
|
||||
* <dfn title="Fügt sie zu einer Einheit zusammen. Mit dem Inhalt des einen nach dem anderen.">Verkettet</dfn> sie mit einem Leerzeichen in der Mitte.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
|
||||
|
||||
### Es bearbeiten { #edit-it }
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ Das war's.
|
|||
|
||||
Das sind die „Typhinweise“:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
|
||||
|
||||
Das ist nicht das gleiche wie das Deklarieren von Defaultwerten, wie es hier der Fall ist:
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ Hier können Sie durch die Optionen blättern, bis Sie diejenige finden, bei der
|
|||
|
||||
Sehen Sie sich diese Funktion an, sie hat bereits Typhinweise:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
|
||||
|
||||
Da der Editor die Typen der Variablen kennt, erhalten Sie nicht nur Code-Vervollständigung, sondern auch eine Fehlerprüfung:
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ Da der Editor die Typen der Variablen kennt, erhalten Sie nicht nur Code-Vervoll
|
|||
|
||||
Jetzt, da Sie wissen, dass Sie das reparieren müssen, konvertieren Sie `age` mittels `str(age)` in einen String:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
|
||||
|
||||
## Deklarieren von Typen { #declaring-types }
|
||||
|
||||
|
|
@ -133,29 +133,32 @@ Zum Beispiel diese:
|
|||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
|
||||
|
||||
### Generische Typen mit Typ-Parametern { #generic-types-with-type-parameters }
|
||||
### `typing`-Modul { #typing-module }
|
||||
|
||||
Es gibt Datenstrukturen, die andere Werte enthalten können, wie etwa `dict`, `list`, `set` und `tuple`. Die inneren Werte können auch ihren eigenen Typ haben.
|
||||
Für einige zusätzliche Anwendungsfälle müssen Sie möglicherweise Dinge aus dem Standardmodul `typing` importieren. Zum Beispiel, wenn Sie deklarieren möchten, dass etwas „jeden Typ“ haben kann, können Sie `Any` aus `typing` verwenden:
|
||||
|
||||
Diese Typen mit inneren Typen werden „**generische**“ Typen genannt. Es ist möglich, sie mit ihren inneren Typen zu deklarieren.
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
Um diese Typen und die inneren Typen zu deklarieren, können Sie Pythons Standardmodul `typing` verwenden. Es existiert speziell für die Unterstützung dieser Typhinweise.
|
||||
|
||||
#### Neuere Python-Versionen { #newer-versions-of-python }
|
||||
def some_function(data: Any):
|
||||
print(data)
|
||||
```
|
||||
|
||||
Die Syntax, welche `typing` verwendet, ist **kompatibel** mit allen Versionen, von Python 3.6 aufwärts zu den neuesten, inklusive Python 3.9, Python 3.10, usw.
|
||||
### Generische Typen { #generic-types }
|
||||
|
||||
Mit der Weiterentwicklung von Python kommen **neuere Versionen** heraus, mit verbesserter Unterstützung für Typannotationen, und in vielen Fällen müssen Sie gar nicht mehr das `typing`-Modul importieren, um Typannotationen zu schreiben.
|
||||
Einige Typen können „Typ-Parameter“ in eckigen Klammern annehmen, um ihre inneren Typen zu definieren, z. B. eine „Liste von Strings“ würde als `list[str]` deklariert.
|
||||
|
||||
Wenn Sie eine neuere Python-Version für Ihr Projekt wählen können, werden Sie aus dieser zusätzlichen Vereinfachung Nutzen ziehen können.
|
||||
Diese Typen, die Typ-Parameter annehmen können, werden **generische Typen** oder **Generics** genannt.
|
||||
|
||||
In der gesamten Dokumentation gibt es Beispiele, welche kompatibel mit unterschiedlichen Python-Versionen sind (wenn es Unterschiede gibt).
|
||||
Sie können dieselben eingebauten Typen als Generics verwenden (mit eckigen Klammern und Typen darin):
|
||||
|
||||
Zum Beispiel bedeutet „**Python 3.6+**“, dass das Beispiel kompatibel mit Python 3.6 oder höher ist (inklusive 3.7, 3.8, 3.9, 3.10, usw.). Und „**Python 3.9+**“ bedeutet, es ist kompatibel mit Python 3.9 oder höher (inklusive 3.10, usw.).
|
||||
|
||||
Wenn Sie über die **neueste Version von Python** verfügen, verwenden Sie die Beispiele für die neueste Version, diese werden die **beste und einfachste Syntax** haben, zum Beispiel, „**Python 3.10+**“.
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
#### Liste { #list }
|
||||
|
||||
|
|
@ -167,7 +170,7 @@ Als Typ nehmen Sie `list`.
|
|||
|
||||
Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckigen Klammern umfasst:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
|
|
@ -193,7 +196,7 @@ Und trotzdem weiß der Editor, dass es sich um ein `str` handelt, und bietet ent
|
|||
|
||||
Das Gleiche gilt für die Deklaration eines Tupels – `tuple` – und einer Menge – `set`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
|
||||
|
||||
Das bedeutet:
|
||||
|
||||
|
|
@ -208,7 +211,7 @@ Der erste Typ-Parameter ist für die Schlüssel des `dict`.
|
|||
|
||||
Der zweite Typ-Parameter ist für die Werte des `dict`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
|
||||
|
||||
Das bedeutet:
|
||||
|
||||
|
|
@ -216,47 +219,23 @@ Das bedeutet:
|
|||
* Die Schlüssel dieses `dict` sind vom Typ `str` (z. B. die Namen der einzelnen Artikel).
|
||||
* Die Werte dieses `dict` sind vom Typ `float` (z. B. der Preis jedes Artikels).
|
||||
|
||||
#### <abbr title="Union – Verbund, Einheit‚ Vereinigung: Eines von Mehreren">Union</abbr> { #union }
|
||||
#### Union { #union }
|
||||
|
||||
Sie können deklarieren, dass eine Variable einer von **verschiedenen Typen** sein kann, zum Beispiel ein `int` oder ein `str`.
|
||||
|
||||
In Python 3.6 und höher (inklusive Python 3.10) können Sie den `Union`-Typ von `typing` verwenden und die möglichen Typen innerhalb der eckigen Klammern auflisten.
|
||||
Um das zu definieren, verwenden Sie den <dfn title="auch „bitweiser Oder-Operator“ genannt, aber diese Bedeutung ist hier nicht relevant">vertikalen Balken (`|`)</dfn>, um beide Typen zu trennen.
|
||||
|
||||
In Python 3.10 gibt es zusätzlich eine **neue Syntax**, die es erlaubt, die möglichen Typen getrennt von einem <abbr title='auch „bitweiser Oder-Operator“ genannt, aber diese Bedeutung ist hier nicht relevant'>vertikalen Balken (`|`)</abbr> aufzulisten.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
Das wird „Union“ genannt, weil die Variable etwas aus der Vereinigung dieser beiden Typmengen sein kann.
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
In beiden Fällen bedeutet das, dass `item` ein `int` oder ein `str` sein kann.
|
||||
Das bedeutet, dass `item` ein `int` oder ein `str` sein könnte.
|
||||
|
||||
#### Vielleicht `None` { #possibly-none }
|
||||
|
||||
Sie können deklarieren, dass ein Wert ein `str`, aber vielleicht auch `None` sein kann.
|
||||
|
||||
In Python 3.6 und darüber (inklusive Python 3.10) können Sie das deklarieren, indem Sie `Optional` vom `typing` Modul importieren und verwenden.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
Wenn Sie `Optional[str]` anstelle von nur `str` verwenden, wird Ihr Editor Ihnen dabei helfen, Fehler zu erkennen, bei denen Sie annehmen könnten, dass ein Wert immer eine String (`str`) ist, obwohl er auch `None` sein könnte.
|
||||
|
||||
`Optional[Something]` ist tatsächlich eine Abkürzung für `Union[Something, None]`, diese beiden sind äquivalent.
|
||||
|
||||
Das bedeutet auch, dass Sie in Python 3.10 `Something | None` verwenden können:
|
||||
Sie können deklarieren, dass ein Wert einen Typ haben könnte, wie `str`, dass er aber auch `None` sein könnte.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
|
|
@ -266,96 +245,7 @@ Das bedeutet auch, dass Sie in Python 3.10 `Something | None` verwenden können:
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ Alternative
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### `Union` oder `Optional` verwenden? { #using-union-or-optional }
|
||||
|
||||
Wenn Sie eine Python-Version unterhalb 3.10 verwenden, hier ist mein sehr **subjektiver** Standpunkt dazu:
|
||||
|
||||
* 🚨 Vermeiden Sie `Optional[SomeType]`
|
||||
* Stattdessen ✨ **verwenden Sie `Union[SomeType, None]`** ✨.
|
||||
|
||||
Beide sind äquivalent und im Hintergrund dasselbe, aber ich empfehle `Union` statt `Optional`, weil das Wort „**optional**“ impliziert, dass dieser Wert, zum Beispiel als Funktionsparameter, optional ist. Tatsächlich bedeutet es aber nur „Der Wert kann `None` sein“, selbst wenn der Wert nicht optional ist und benötigt wird.
|
||||
|
||||
Ich denke, `Union[SomeType, None]` ist expliziter bezüglich seiner Bedeutung.
|
||||
|
||||
Es geht nur um Worte und Namen. Aber diese Worte können beeinflussen, wie Sie und Ihre Teamkollegen über den Code denken.
|
||||
|
||||
Nehmen wir zum Beispiel diese Funktion:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
|
||||
|
||||
Der Parameter `name` ist definiert als `Optional[str]`, aber er ist **nicht optional**, Sie können die Funktion nicht ohne diesen Parameter aufrufen:
|
||||
|
||||
```Python
|
||||
say_hi() # Oh, nein, das löst einen Fehler aus! 😱
|
||||
```
|
||||
|
||||
Der `name` Parameter wird **immer noch benötigt** (nicht *optional*), weil er keinen Default-Wert hat. `name` akzeptiert aber dennoch `None` als Wert:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Das funktioniert, None ist gültig 🎉
|
||||
```
|
||||
|
||||
Die gute Nachricht ist, dass Sie sich darüber keine Sorgen mehr machen müssen, wenn Sie Python 3.10 verwenden, da Sie einfach `|` verwenden können, um Vereinigungen von Typen zu definieren:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
|
||||
|
||||
Und dann müssen Sie sich nicht mehr um Namen wie `Optional` und `Union` kümmern. 😎
|
||||
|
||||
#### Generische Typen { #generic-types }
|
||||
|
||||
Diese Typen, die Typ-Parameter in eckigen Klammern akzeptieren, werden **generische Typen** oder **Generics** genannt.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
Sie können die eingebauten Typen als Generics verwenden (mit eckigen Klammern und Typen darin):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Und ebenso wie bei früheren Python-Versionen, aus dem `typing`-Modul:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ... und andere.
|
||||
|
||||
In Python 3.10 können Sie als Alternative zu den Generics `Union` und `Optional` den <abbr title='auch „bitweiser Oder-Operator“ genannt, aber diese Bedeutung ist hier nicht relevant'>vertikalen Balken (`|`)</abbr> verwenden, um Vereinigungen von Typen zu deklarieren, das ist besser und einfacher.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
Sie können die eingebauten Typen als Generics verwenden (mit eckigen Klammern und Typen darin):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Und Generics aus dem `typing`-Modul:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ... und andere.
|
||||
|
||||
////
|
||||
Wenn Sie `str | None` anstelle von nur `str` verwenden, wird Ihr Editor Ihnen dabei helfen, Fehler zu erkennen, bei denen Sie annehmen könnten, dass ein Wert immer ein `str` ist, obwohl er auch `None` sein könnte.
|
||||
|
||||
### Klassen als Typen { #classes-as-types }
|
||||
|
||||
|
|
@ -363,11 +253,11 @@ Sie können auch eine Klasse als Typ einer Variablen deklarieren.
|
|||
|
||||
Nehmen wir an, Sie haben eine Klasse `Person`, mit einem Namen:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
|
||||
|
||||
Dann können Sie eine Variable vom Typ `Person` deklarieren:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
|
||||
|
||||
Und wiederum bekommen Sie die volle Editor-Unterstützung:
|
||||
|
||||
|
|
@ -403,19 +293,13 @@ Um mehr über <a href="https://docs.pydantic.dev/" class="external-link" target=
|
|||
|
||||
Viel mehr von all dem werden Sie in praktischer Anwendung im [Tutorial – Benutzerhandbuch](tutorial/index.md){.internal-link target=_blank} sehen.
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
Pydantic verhält sich speziell, wenn Sie `Optional` oder `Union[Something, None]` ohne einen Defaultwert verwenden. Sie können darüber in der Pydantic Dokumentation unter <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Erforderliche optionale Felder</a> mehr erfahren.
|
||||
|
||||
///
|
||||
|
||||
## Typhinweise mit Metadaten-Annotationen { #type-hints-with-metadata-annotations }
|
||||
|
||||
Python bietet auch die Möglichkeit, **zusätzliche <abbr title="Daten über die Daten, in diesem Fall Informationen über den Typ, z. B. eine Beschreibung.">Metadaten</abbr>** in Typhinweisen unterzubringen, mittels `Annotated`.
|
||||
Python bietet auch die Möglichkeit, **zusätzliche <dfn title="Daten über die Daten, in diesem Fall Informationen über den Typ, z. B. eine Beschreibung.">Metadaten</dfn>** in Typhinweisen unterzubringen, mittels `Annotated`.
|
||||
|
||||
Seit Python 3.9 ist `Annotated` ein Teil der Standardbibliothek, Sie können es von `typing` importieren.
|
||||
Sie können `Annotated` von `typing` importieren.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
|
||||
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
|
||||
|
||||
Python selbst macht nichts mit `Annotated`. Für Editoren und andere Tools ist der Typ immer noch `str`.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
/// details | 🌐 Übersetzung durch KI und Menschen
|
||||
|
||||
Diese Übersetzung wurde von KI erstellt, angeleitet von Menschen. 🤝
|
||||
|
||||
Sie könnte Fehler enthalten, etwa Missverständnisse des ursprünglichen Sinns oder unnatürliche Formulierungen, usw. 🤖
|
||||
|
||||
Sie können diese Übersetzung verbessern, indem Sie [uns helfen, die KI-LLM besser anzuleiten](https://fastapi.tiangolo.com/de/contributing/#translations).
|
||||
|
||||
[Englische Version](ENGLISH_VERSION_URL)
|
||||
|
||||
///
|
||||
|
|
@ -15,7 +15,7 @@ Hierzu zählen beispielsweise:
|
|||
|
||||
Importieren Sie zunächst `BackgroundTasks` und definieren Sie einen Parameter in Ihrer *Pfadoperation-Funktion* mit der Typdeklaration `BackgroundTasks`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
|
||||
|
||||
**FastAPI** erstellt für Sie das Objekt vom Typ `BackgroundTasks` und übergibt es als diesen Parameter.
|
||||
|
||||
|
|
@ -31,13 +31,13 @@ In diesem Fall schreibt die Taskfunktion in eine Datei (den Versand einer E-Mail
|
|||
|
||||
Und da der Schreibvorgang nicht `async` und `await` verwendet, definieren wir die Funktion mit normalem `def`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
|
||||
|
||||
## Den Hintergrundtask hinzufügen { #add-the-background-task }
|
||||
|
||||
Übergeben Sie innerhalb Ihrer *Pfadoperation-Funktion* Ihre Taskfunktion mit der Methode `.add_task()` an das *Hintergrundtasks*-Objekt:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
|
||||
|
||||
`.add_task()` erhält als Argumente:
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ Sie können die *Pfadoperationen* für dieses Modul mit `APIRouter` erstellen.
|
|||
|
||||
Sie importieren ihn und erstellen eine „Instanz“ auf die gleiche Weise wie mit der Klasse `FastAPI`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
|
||||
### *Pfadoperationen* mit `APIRouter` { #path-operations-with-apirouter }
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ Und dann verwenden Sie ihn, um Ihre *Pfadoperationen* zu deklarieren.
|
|||
|
||||
Verwenden Sie ihn auf die gleiche Weise wie die Klasse `FastAPI`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
|
||||
Sie können sich `APIRouter` als eine „Mini-`FastAPI`“-Klasse vorstellen.
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ Also fügen wir sie in ihr eigenes `dependencies`-Modul (`app/dependencies.py`)
|
|||
|
||||
Wir werden nun eine einfache Abhängigkeit verwenden, um einen benutzerdefinierten `X-Token`-Header zu lesen:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ Wir wissen, dass alle *Pfadoperationen* in diesem Modul folgendes haben:
|
|||
|
||||
Anstatt also alles zu jeder *Pfadoperation* hinzuzufügen, können wir es dem `APIRouter` hinzufügen.
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
|
||||
Da der Pfad jeder *Pfadoperation* mit `/` beginnen muss, wie in:
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ Und wir müssen die Abhängigkeitsfunktion aus dem Modul `app.dependencies` impo
|
|||
|
||||
Daher verwenden wir einen relativen Import mit `..` für die Abhängigkeiten:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
|
||||
#### Wie relative Importe funktionieren { #how-relative-imports-work }
|
||||
|
||||
|
|
@ -279,7 +279,7 @@ Wir fügen weder das Präfix `/items` noch `tags=["items"]` zu jeder *Pfadoperat
|
|||
|
||||
Aber wir können immer noch _mehr_ `tags` hinzufügen, die auf eine bestimmte *Pfadoperation* angewendet werden, sowie einige zusätzliche `responses`, die speziell für diese *Pfadoperation* gelten:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -305,13 +305,13 @@ Sie importieren und erstellen wie gewohnt eine `FastAPI`-Klasse.
|
|||
|
||||
Und wir können sogar [globale Abhängigkeiten](dependencies/global-dependencies.md){.internal-link target=_blank} deklarieren, die mit den Abhängigkeiten für jeden `APIRouter` kombiniert werden:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
|
||||
### Den `APIRouter` importieren { #import-the-apirouter }
|
||||
|
||||
Jetzt importieren wir die anderen Submodule, die `APIRouter` haben:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
|
||||
|
||||
Da es sich bei den Dateien `app/routers/users.py` und `app/routers/items.py` um Submodule handelt, die Teil desselben Python-Packages `app` sind, können wir einen einzelnen Punkt `.` verwenden, um sie mit „relativen Imports“ zu importieren.
|
||||
|
||||
|
|
@ -374,13 +374,13 @@ würde der `router` von `users` den von `items` überschreiben und wir könnten
|
|||
|
||||
Um also beide in derselben Datei verwenden zu können, importieren wir die Submodule direkt:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
|
||||
|
||||
### Die `APIRouter` für `users` und `items` inkludieren { #include-the-apirouters-for-users-and-items }
|
||||
|
||||
Inkludieren wir nun die `router` aus diesen Submodulen `users` und `items`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
|
|
@ -420,13 +420,13 @@ Sie enthält einen `APIRouter` mit einigen administrativen *Pfadoperationen*, di
|
|||
|
||||
In diesem Beispiel wird es ganz einfach sein. Nehmen wir jedoch an, dass wir, da sie mit anderen Projekten in der Organisation geteilt wird, sie nicht ändern und kein `prefix`, `dependencies`, `tags`, usw. direkt zum `APIRouter` hinzufügen können:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
Aber wir möchten immer noch ein benutzerdefiniertes `prefix` festlegen, wenn wir den `APIRouter` einbinden, sodass alle seine *Pfadoperationen* mit `/admin` beginnen, wir möchten es mit den `dependencies` sichern, die wir bereits für dieses Projekt haben, und wir möchten `tags` und `responses` hinzufügen.
|
||||
|
||||
Wir können das alles deklarieren, ohne den ursprünglichen `APIRouter` ändern zu müssen, indem wir diese Parameter an `app.include_router()` übergeben:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
|
||||
|
||||
Auf diese Weise bleibt der ursprüngliche `APIRouter` unverändert, sodass wir dieselbe `app/internal/admin.py`-Datei weiterhin mit anderen Projekten in der Organisation teilen können.
|
||||
|
||||
|
|
@ -447,7 +447,7 @@ Wir können *Pfadoperationen* auch direkt zur `FastAPI`-App hinzufügen.
|
|||
|
||||
Hier machen wir es ... nur um zu zeigen, dass wir es können 🤷:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
|
||||
|
||||
und es wird korrekt funktionieren, zusammen mit allen anderen *Pfadoperationen*, die mit `app.include_router()` hinzugefügt wurden.
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ Beachten Sie, wie jedes Attribut eines Modells mit einem Typ, Defaultwert und `F
|
|||
|
||||
Sie können zusätzliche Information in `Field`, `Query`, `Body`, usw. deklarieren. Und es wird im generierten JSON-Schema untergebracht.
|
||||
|
||||
Sie werden später mehr darüber lernen, wie man zusätzliche Information unterbringt, wenn Sie lernen, Beispiele zu deklarieren.
|
||||
Sie werden später in der Dokumentation mehr darüber lernen, wie man zusätzliche Information unterbringt, wenn Sie lernen, Beispiele zu deklarieren.
|
||||
|
||||
/// warning | Achtung
|
||||
|
||||
|
|
|
|||
|
|
@ -104,12 +104,6 @@ Da einfache Werte standardmäßig als Query-Parameter interpretiert werden, müs
|
|||
q: str | None = None
|
||||
```
|
||||
|
||||
Oder in Python 3.9:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Zum Beispiel:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ images: list[Image]
|
|||
|
||||
so wie in:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
|
||||
|
||||
## Editor-Unterstützung überall { #editor-support-everywhere }
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ Das schauen wir uns mal an.
|
|||
|
||||
Im folgenden Beispiel akzeptieren Sie irgendein `dict`, solange es `int`-Schlüssel und `float`-Werte hat:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ Die Funktionsparameter werden wie folgt erkannt:
|
|||
|
||||
FastAPI weiß, dass der Wert von `q` nicht erforderlich ist, aufgrund des definierten Defaultwertes `= None`.
|
||||
|
||||
Das `str | None` (Python 3.10+) oder `Union` in `Union[str, None]` (Python 3.9+) wird von FastAPI nicht verwendet, um zu bestimmen, dass der Wert nicht erforderlich ist. FastAPI weiß, dass er nicht erforderlich ist, weil er einen Defaultwert von `= None` hat.
|
||||
Das `str | None` wird von FastAPI nicht verwendet, um zu bestimmen, dass der Wert nicht erforderlich ist. FastAPI weiß, dass er nicht erforderlich ist, weil er einen Defaultwert von `= None` hat.
|
||||
|
||||
Das Hinzufügen der Typannotationen ermöglicht jedoch Ihrem Editor, Ihnen eine bessere Unterstützung zu bieten und Fehler zu erkennen.
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Aber selbst wenn Sie die **Daten ausfüllen** und auf „Ausführen“ klicken,
|
|||
|
||||
In einigen speziellen Anwendungsfällen (wahrscheinlich nicht sehr häufig) möchten Sie möglicherweise die Cookies, die Sie empfangen möchten, **einschränken**.
|
||||
|
||||
Ihre API hat jetzt die Macht, ihre eigene <abbr title="Das ist ein Scherz, nur für den Fall. Es hat nichts mit Cookie-Einwilligungen zu tun, aber es ist witzig, dass selbst die API jetzt die armen Cookies ablehnen kann. Haben Sie einen Keks. 🍪">Cookie-Einwilligung</abbr> zu kontrollieren. 🤪🍪
|
||||
Ihre API hat jetzt die Macht, ihre eigene <dfn title="Das ist ein Scherz, nur für den Fall. Es hat nichts mit Cookie-Einwilligungen zu tun, aber es ist witzig, dass selbst die API jetzt die armen Cookies ablehnen kann. Haben Sie einen Keks. 🍪">Cookie-Einwilligung</dfn> zu kontrollieren. 🤪🍪
|
||||
|
||||
Sie können die Modellkonfiguration von Pydantic verwenden, um `extra` Felder zu verbieten (`forbid`):
|
||||
|
||||
|
|
@ -54,9 +54,9 @@ Sie können die Modellkonfiguration von Pydantic verwenden, um `extra` Felder zu
|
|||
|
||||
Wenn ein Client versucht, einige **zusätzliche Cookies** zu senden, erhält er eine **Error-<abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>**.
|
||||
|
||||
Arme Cookie-Banner, wie sie sich mühen, Ihre Einwilligung zu erhalten, dass die <abbr title="Das ist ein weiterer Scherz. Beachten Sie mich nicht. Trinken Sie einen Kaffee zu Ihrem Keks. ☕">API sie ablehnen darf</abbr>. 🍪
|
||||
Arme Cookie-Banner, wie sie sich mühen, Ihre Einwilligung zu erhalten, dass die <dfn title="Das ist ein weiterer Scherz. Beachten Sie mich nicht. Trinken Sie einen Kaffee zu Ihrem Keks. ☕">API sie ablehnen darf</dfn>. 🍪
|
||||
|
||||
Wenn der Client beispielsweise versucht, ein `santa_tracker`-Cookie mit einem Wert von `good-list-please` zu senden, erhält der Client eine **Error-Response**, die ihm mitteilt, dass das `santa_tracker` <abbr title="Santa beschwert sich über den Mangel an Cookies. 🎅 Okay, keine Cookie-Witze mehr.">Cookie nicht erlaubt ist</abbr>:
|
||||
Wenn der Client beispielsweise versucht, ein `santa_tracker`-Cookie mit einem Wert von `good-list-please` zu senden, erhält der Client eine **Error-Response**, die ihm mitteilt, dass das `santa_tracker` <dfn title="Santa missbilligt den Mangel an Cookies. 🎅 Okay, keine Cookie-Witze mehr.">Cookie nicht erlaubt ist</dfn>:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -73,4 +73,4 @@ Wenn der Client beispielsweise versucht, ein `santa_tracker`-Cookie mit einem We
|
|||
|
||||
## Zusammenfassung { #summary }
|
||||
|
||||
Sie können **Pydantic-Modelle** verwenden, um <abbr title="Nehmen Sie einen letzten Keks, bevor Sie gehen. 🍪">**Cookies**</abbr> in **FastAPI** zu deklarieren. 😎
|
||||
Sie können **Pydantic-Modelle** verwenden, um <dfn title="Nehmen Sie einen letzten Keks, bevor Sie gehen. 🍪">**Cookies**</dfn> in **FastAPI** zu deklarieren. 😎
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ Sie können auch angeben, ob Ihr Backend erlaubt:
|
|||
* Bestimmte HTTP-Methoden (`POST`, `PUT`) oder alle mit der Wildcard `"*"`.
|
||||
* Bestimmte HTTP-Header oder alle mit der Wildcard `"*"`.
|
||||
|
||||
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
|
||||
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
|
||||
|
||||
Die von der `CORSMiddleware`-Implementierung verwendeten Defaultparameter sind standardmäßig restriktiv, daher müssen Sie bestimmte Origins, Methoden oder Header ausdrücklich aktivieren, damit Browser sie in einem Cross-Domain-Kontext verwenden dürfen.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Sie können den Debugger in Ihrem Editor verbinden, zum Beispiel mit Visual Stud
|
|||
|
||||
Importieren und führen Sie `uvicorn` direkt in Ihrer FastAPI-Anwendung aus:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
|
||||
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
|
||||
|
||||
### Über `__name__ == "__main__"` { #about-name-main }
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ Jetzt können Sie Ihre Abhängigkeit mithilfe dieser Klasse deklarieren.
|
|||
|
||||
Beachten Sie, wie wir `CommonQueryParams` im obigen Code zweimal schreiben:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ nicht annotiert
|
||||
//// tab | Python 3.10+ nicht annotiert
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ Aus diesem extrahiert FastAPI die deklarierten Parameter, und dieses ist es, was
|
|||
|
||||
In diesem Fall hat das erste `CommonQueryParams` in:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, ...
|
||||
|
|
@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ nicht annotiert
|
||||
//// tab | Python 3.10+ nicht annotiert
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ commons: CommonQueryParams ...
|
|||
|
||||
Sie könnten tatsächlich einfach schreiben:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
|
|
@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ nicht annotiert
|
||||
//// tab | Python 3.10+ nicht annotiert
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ Es wird jedoch empfohlen, den Typ zu deklarieren, da Ihr Editor so weiß, was al
|
|||
|
||||
Aber Sie sehen, dass wir hier etwas Codeduplizierung haben, indem wir `CommonQueryParams` zweimal schreiben:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ nicht annotiert
|
||||
//// tab | Python 3.10+ nicht annotiert
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ In diesem speziellen Fall können Sie Folgendes tun:
|
|||
|
||||
Anstatt zu schreiben:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
|
@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ nicht annotiert
|
||||
//// tab | Python 3.10+ nicht annotiert
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
|||
|
||||
... schreiben Sie:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends()]
|
||||
|
|
@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ nicht annotiert
|
||||
//// tab | Python 3.10+ nicht annotiert
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ Oder die Abhängigkeit gibt keinen Wert zurück.
|
|||
|
||||
Aber Sie müssen sie trotzdem ausführen/auflösen.
|
||||
|
||||
In diesen Fällen können Sie, anstatt einen Parameter der *Pfadoperation-Funktion* mit `Depends` zu deklarieren, eine `list`e von `dependencies` zum *Pfadoperation-Dekorator* hinzufügen.
|
||||
In diesen Fällen können Sie, anstatt einen Parameter der *Pfadoperation-Funktion* mit `Depends` zu deklarieren, eine `list` von `dependencies` zum *Pfadoperation-Dekorator* hinzufügen.
|
||||
|
||||
## `dependencies` zum *Pfadoperation-Dekorator* hinzufügen { #add-dependencies-to-the-path-operation-decorator }
|
||||
|
||||
Der *Pfadoperation-Dekorator* erhält ein optionales Argument `dependencies`.
|
||||
|
||||
Es sollte eine `list`e von `Depends()` sein:
|
||||
Es sollte eine `list` von `Depends()` sein:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
|
||||
|
||||
Diese Abhängigkeiten werden auf die gleiche Weise wie normale Abhängigkeiten ausgeführt/aufgelöst. Aber ihr Wert (falls sie einen zurückgeben) wird nicht an Ihre *Pfadoperation-Funktion* übergeben.
|
||||
|
||||
|
|
@ -44,13 +44,13 @@ Sie können dieselben Abhängigkeits-*Funktionen* verwenden, die Sie normalerwei
|
|||
|
||||
Sie können Anforderungen für einen <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> (wie Header) oder andere Unterabhängigkeiten deklarieren:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
|
||||
|
||||
### Exceptions auslösen { #raise-exceptions }
|
||||
|
||||
Die Abhängigkeiten können Exceptions `raise`n, genau wie normale Abhängigkeiten:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
|
||||
|
||||
### Rückgabewerte { #return-values }
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ Und sie können Werte zurückgeben oder nicht, die Werte werden nicht verwendet.
|
|||
|
||||
Sie können also eine normale Abhängigkeit (die einen Wert zurückgibt), die Sie bereits an anderer Stelle verwenden, wiederverwenden, und auch wenn der Wert nicht verwendet wird, wird die Abhängigkeit ausgeführt:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
|
||||
|
||||
## Abhängigkeiten für eine Gruppe von *Pfadoperationen* { #dependencies-for-a-group-of-path-operations }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Abhängigkeiten mit `yield` { #dependencies-with-yield }
|
||||
|
||||
FastAPI unterstützt Abhängigkeiten, die nach Abschluss einige <abbr title="Manchmal auch genannt „Exit Code“, „Cleanup Code“, „Teardown Code“, „Closing Code“, „Kontextmanager Exit Code“, usw.">zusätzliche Schritte ausführen</abbr>.
|
||||
FastAPI unterstützt Abhängigkeiten, die einige <dfn title="manchmal auch genannt: „Exit Code“, „Cleanup Code“, „Teardown Code“, „Closing Code“, „Kontextmanager Exit Code“, usw.">zusätzliche Schritte nach Abschluss</dfn> ausführen.
|
||||
|
||||
Verwenden Sie dazu `yield` statt `return` und schreiben Sie die zusätzlichen Schritte / den zusätzlichen Code danach.
|
||||
|
||||
|
|
@ -29,15 +29,15 @@ Sie könnten damit beispielsweise eine Datenbanksession erstellen und diese nach
|
|||
|
||||
Nur der Code vor und einschließlich der `yield`-Anweisung wird ausgeführt, bevor eine <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> erzeugt wird:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
|
||||
|
||||
Der ge`yield`ete Wert ist das, was in *Pfadoperationen* und andere Abhängigkeiten eingefügt wird:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
|
||||
|
||||
Der auf die `yield`-Anweisung folgende Code wird nach der Response ausgeführt:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ Sie können also mit `except SomeException` diese bestimmte Exception innerhalb
|
|||
|
||||
Auf die gleiche Weise können Sie `finally` verwenden, um sicherzustellen, dass die Exit-Schritte ausgeführt werden, unabhängig davon, ob eine Exception geworfen wurde oder nicht.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
|
||||
|
||||
## Unterabhängigkeiten mit `yield` { #sub-dependencies-with-yield }
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ Sie können Unterabhängigkeiten und „Bäume“ von Unterabhängigkeiten belie
|
|||
|
||||
Beispielsweise kann `dependency_c` von `dependency_b` und `dependency_b` von `dependency_a` abhängen:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
|
||||
|
||||
Und alle können `yield` verwenden.
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ In diesem Fall benötigt `dependency_c` zum Ausführen seines Exit-Codes, dass d
|
|||
|
||||
Und wiederum benötigt `dependency_b` den Wert von `dependency_a` (hier `dep_a` genannt) für seinen Exit-Code.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
|
||||
|
||||
Auf die gleiche Weise könnten Sie einige Abhängigkeiten mit `yield` und einige andere Abhängigkeiten mit `return` haben, und alle können beliebig voneinander abhängen.
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ Aber es ist für Sie da, wenn Sie es brauchen. 🤓
|
|||
|
||||
///
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
|
||||
|
||||
Wenn Sie Exceptions abfangen und darauf basierend eine benutzerdefinierte Response erstellen möchten, erstellen Sie einen [benutzerdefinierten Exceptionhandler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ Wenn Sie Exceptions abfangen und darauf basierend eine benutzerdefinierte Respon
|
|||
|
||||
Wenn Sie eine Exception mit `except` in einer Abhängigkeit mit `yield` abfangen und sie nicht erneut auslösen (oder eine neue Exception auslösen), kann FastAPI nicht feststellen, dass es eine Exception gab, genau so wie es bei normalem Python der Fall wäre:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
|
||||
|
||||
In diesem Fall sieht der Client eine *HTTP 500 Internal Server Error*-Response, wie es sein sollte, da wir keine `HTTPException` oder Ähnliches auslösen, aber der Server hat **keine Logs** oder einen anderen Hinweis darauf, was der Fehler war. 😱
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ Wenn Sie eine Exception in einer Abhängigkeit mit `yield` abfangen, sollten Sie
|
|||
|
||||
Sie können dieselbe Exception mit `raise` erneut auslösen:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
|
||||
|
||||
Jetzt erhält der Client dieselbe *HTTP 500 Internal Server Error*-Response, aber der Server enthält unseren benutzerdefinierten `InternalError` in den Logs. 😎
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ Normalerweise wird der Exit-Code von Abhängigkeiten mit `yield` ausgeführt **n
|
|||
|
||||
Wenn Sie aber wissen, dass Sie die Abhängigkeit nach der Rückkehr aus der *Pfadoperation-Funktion* nicht mehr benötigen, können Sie `Depends(scope="function")` verwenden, um FastAPI mitzuteilen, dass es die Abhängigkeit nach der Rückkehr aus der *Pfadoperation-Funktion* schließen soll, jedoch **bevor** die **Response gesendet wird**.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
|
||||
|
||||
`Depends()` erhält einen `scope`-Parameter, der sein kann:
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ In Python können Sie Kontextmanager erstellen, indem Sie <a href="https://docs.
|
|||
|
||||
Sie können solche auch innerhalb von **FastAPI**-Abhängigkeiten mit `yield` verwenden, indem Sie `with`- oder `async with`-Anweisungen innerhalb der Abhängigkeits-Funktion verwenden:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ Bei einigen Anwendungstypen möchten Sie möglicherweise Abhängigkeiten zur ges
|
|||
|
||||
In diesem Fall werden sie auf alle *Pfadoperationen* in der Anwendung angewendet:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
|
||||
|
||||
|
||||
Und alle Ideen aus dem Abschnitt über das [Hinzufügen von `dependencies` zu den *Pfadoperation-Dekoratoren*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} gelten weiterhin, aber in diesem Fall für alle *Pfadoperationen* in der App.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Abhängigkeiten { #dependencies }
|
||||
|
||||
**FastAPI** hat ein sehr mächtiges, aber intuitives **<abbr title="auch bekannt als: Komponenten, Ressourcen, Provider, Services, Injectables">Dependency Injection</abbr>** System.
|
||||
**FastAPI** hat ein sehr mächtiges, aber intuitives **<dfn title="auch bekannt als Komponenten, Ressourcen, Provider, Services, Injectables">Abhängigkeitsinjektion</dfn>** System.
|
||||
|
||||
Es ist so konzipiert, sehr einfach zu verwenden zu sein und es jedem Entwickler sehr leicht zu machen, andere Komponenten mit **FastAPI** zu integrieren.
|
||||
|
||||
|
|
|
|||
|
|
@ -58,11 +58,11 @@ query_extractor --> query_or_cookie_extractor --> read_query
|
|||
|
||||
Wenn eine Ihrer Abhängigkeiten mehrmals für dieselbe *Pfadoperation* deklariert wird, beispielsweise wenn mehrere Abhängigkeiten eine gemeinsame Unterabhängigkeit haben, wird **FastAPI** diese Unterabhängigkeit nur einmal pro <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> aufrufen.
|
||||
|
||||
Und es speichert den zurückgegebenen Wert in einem <abbr title="Mechanismus, der bereits berechnete/generierte Werte zwischenspeichert, um sie später wiederzuverwenden, anstatt sie erneut zu berechnen.">„Cache“</abbr> und übergibt diesen gecachten Wert an alle „Dependanten“, die ihn in diesem spezifischen Request benötigen, anstatt die Abhängigkeit mehrmals für denselben Request aufzurufen.
|
||||
Und es speichert den zurückgegebenen Wert in einem <dfn title="Hilfsprogramm/System zum Speichern berechneter/erzeugter Werte, um sie wiederzuverwenden, anstatt sie erneut zu berechnen.">„Cache“</dfn> und übergibt diesen gecachten Wert an alle „Dependanten“, die ihn in diesem spezifischen Request benötigen, anstatt die Abhängigkeit mehrmals für denselben Request aufzurufen.
|
||||
|
||||
In einem fortgeschrittenen Szenario, bei dem Sie wissen, dass die Abhängigkeit bei jedem Schritt (möglicherweise mehrmals) in demselben Request aufgerufen werden muss, anstatt den zwischengespeicherten Wert zu verwenden, können Sie den Parameter `use_cache=False` festlegen, wenn Sie `Depends` verwenden:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
|
|
@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
|
|||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ nicht annotiert
|
||||
//// tab | Python 3.10+ nicht annotiert
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -190,9 +190,9 @@ Aber wenn wir das in der Zuweisung `response_model=PlaneItem | CarItem` machen,
|
|||
|
||||
Auf die gleiche Weise können Sie Responses von Listen von Objekten deklarieren.
|
||||
|
||||
Dafür verwenden Sie Pythons Standard-`typing.List` (oder nur `list` in Python 3.9 und höher):
|
||||
Dafür verwenden Sie Pythons Standard-`list`:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
|
||||
|
||||
## Response mit beliebigem `dict` { #response-with-arbitrary-dict }
|
||||
|
||||
|
|
@ -200,9 +200,9 @@ Sie können auch eine Response deklarieren, die ein beliebiges `dict` zurückgib
|
|||
|
||||
Dies ist nützlich, wenn Sie die gültigen Feld-/Attributnamen nicht im Voraus kennen (die für ein Pydantic-Modell benötigt werden würden).
|
||||
|
||||
In diesem Fall können Sie `typing.Dict` verwenden (oder nur `dict` in Python 3.9 und höher):
|
||||
In diesem Fall können Sie `dict` verwenden:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
|
||||
|
||||
## Zusammenfassung { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Die einfachste FastAPI-Datei könnte wie folgt aussehen:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py *}
|
||||
|
||||
Kopieren Sie das in eine Datei `main.py`.
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ Das war's! Jetzt können Sie Ihre App unter dieser URL aufrufen. ✨
|
|||
|
||||
### Schritt 1: `FastAPI` importieren { #step-1-import-fastapi }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
|
||||
|
||||
`FastAPI` ist eine Python-Klasse, die die gesamte Funktionalität für Ihre API bereitstellt.
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ Sie können alle <a href="https://www.starlette.dev/" class="external-link" targ
|
|||
|
||||
### Schritt 2: Erzeugen einer `FastAPI`-„Instanz“ { #step-2-create-a-fastapi-instance }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
|
||||
|
||||
In diesem Beispiel ist die Variable `app` eine „Instanz“ der Klasse `FastAPI`.
|
||||
|
||||
|
|
@ -266,12 +266,12 @@ Wir werden sie auch „**Operationen**“ nennen.
|
|||
|
||||
#### Definieren eines *Pfadoperation-Dekorators* { #define-a-path-operation-decorator }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
|
||||
|
||||
Das `@app.get("/")` sagt **FastAPI**, dass die Funktion direkt darunter für die Bearbeitung von <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> zuständig ist, die an:
|
||||
|
||||
* den Pfad `/`
|
||||
* unter der Verwendung der <abbr title="eine HTTP-GET-Methode"><code>get</code>-Operation</abbr> gehen
|
||||
* unter der Verwendung der <dfn title="eine HTTP-GET-Methode"><code>get</code>-Operation</dfn> gehen
|
||||
|
||||
/// info | `@decorator` Info
|
||||
|
||||
|
|
@ -320,7 +320,7 @@ Das ist unsere „**Pfadoperation-Funktion**“:
|
|||
* **Operation**: ist `get`.
|
||||
* **Funktion**: ist die Funktion direkt unter dem „Dekorator“ (unter `@app.get("/")`).
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
|
||||
|
||||
Dies ist eine Python-Funktion.
|
||||
|
||||
|
|
@ -332,7 +332,7 @@ In diesem Fall handelt es sich um eine `async`-Funktion.
|
|||
|
||||
Sie könnten sie auch als normale Funktion anstelle von `async def` definieren:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
@ -342,7 +342,7 @@ Wenn Sie den Unterschied nicht kennen, lesen Sie [Async: *„In Eile?“*](../as
|
|||
|
||||
### Schritt 5: den Inhalt zurückgeben { #step-5-return-the-content }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Sie können ein `dict`, eine `list`, einzelne Werte wie `str`, `int`, usw. zurückgeben.
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Um HTTP-<abbr title="Response – Antwort: Daten, die der Server zum anfragenden
|
|||
|
||||
### `HTTPException` importieren { #import-httpexception }
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
|
||||
|
||||
### Eine `HTTPException` in Ihrem Code auslösen { #raise-an-httpexception-in-your-code }
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ Der Vorteil des Auslösens einer Exception gegenüber dem Zurückgeben eines Wer
|
|||
|
||||
In diesem Beispiel lösen wir eine Exception mit einem Statuscode von `404` aus, wenn der Client einen Artikel mit einer nicht existierenden ID anfordert:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
|
||||
|
||||
### Die resultierende Response { #the-resulting-response }
|
||||
|
||||
|
|
@ -77,11 +77,11 @@ Sie werden es wahrscheinlich nicht direkt in Ihrem Code verwenden müssen.
|
|||
|
||||
Aber falls Sie es für ein fortgeschrittenes Szenario benötigen, können Sie benutzerdefinierte Header hinzufügen:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
|
||||
|
||||
## Benutzerdefinierte Exceptionhandler installieren { #install-custom-exception-handlers }
|
||||
|
||||
Sie können benutzerdefinierte <abbr title="Ausnahmebehandler: Funktion, die sich um die Bearbeitung einer Exception kümmert">Exceptionhandler</abbr> mit <a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">den gleichen Exception-Werkzeugen von Starlette</a> hinzufügen.
|
||||
Sie können benutzerdefinierte <a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">Exceptionhandler mit den gleichen Exception-Werkzeugen von Starlette</a> hinzufügen.
|
||||
|
||||
Angenommen, Sie haben eine benutzerdefinierte Exception `UnicornException`, die Sie (oder eine Bibliothek, die Sie verwenden) `raise`n könnten.
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ Und Sie möchten diese Exception global mit FastAPI handhaben.
|
|||
|
||||
Sie könnten einen benutzerdefinierten Exceptionhandler mit `@app.exception_handler()` hinzufügen:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
|
||||
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
|
||||
|
||||
Hier, wenn Sie `/unicorns/yolo` anfordern, wird die *Pfadoperation* eine `UnicornException` `raise`n.
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ Um diesen zu überschreiben, importieren Sie den `RequestValidationError` und ve
|
|||
|
||||
Der Exceptionhandler erhält einen `Request` und die Exception.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
|
||||
|
||||
Wenn Sie nun zu `/items/foo` gehen, erhalten Sie anstelle des standardmäßigen JSON-Fehlers mit:
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ Auf die gleiche Weise können Sie den `HTTPException`-Handler überschreiben.
|
|||
|
||||
Zum Beispiel könnten Sie eine Klartext-Response statt JSON für diese Fehler zurückgeben wollen:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
|
||||
|
||||
/// note | Technische Details
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ Der `RequestValidationError` enthält den empfangenen `body` mit den ungültigen
|
|||
|
||||
Sie könnten diesen während der Entwicklung Ihrer Anwendung verwenden, um den Body zu loggen und zu debuggen, ihn an den Benutzer zurückzugeben usw.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
|
||||
|
||||
Versuchen Sie nun, einen ungültigen Artikel zu senden:
|
||||
|
||||
|
|
@ -239,6 +239,6 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
|||
|
||||
Wenn Sie die Exception zusammen mit den gleichen Default-Exceptionhandlern von **FastAPI** verwenden möchten, können Sie die Default-Exceptionhandler aus `fastapi.exception_handlers` importieren und wiederverwenden:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
|
||||
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
|
||||
|
||||
In diesem Beispiel geben Sie nur den Fehler mit einer sehr ausdrucksstarken Nachricht aus, aber Sie verstehen das Prinzip. Sie können die Exception verwenden und dann einfach die Default-Exceptionhandler wiederverwenden.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Sie können die folgenden Felder festlegen, die in der OpenAPI-Spezifikation und
|
|||
|
||||
Sie können diese wie folgt setzen:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
|
||||
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Seit OpenAPI 3.1.0 und FastAPI 0.99.0 können Sie die `license_info` auch mit ei
|
|||
|
||||
Zum Beispiel:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
|
||||
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
|
||||
|
||||
## Metadaten für Tags { #metadata-for-tags }
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ Versuchen wir es mit einem Beispiel mit Tags für `users` und `items`.
|
|||
|
||||
Erstellen Sie Metadaten für Ihre Tags und übergeben Sie diese an den Parameter `openapi_tags`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
|
||||
|
||||
Beachten Sie, dass Sie Markdown innerhalb der Beschreibungen verwenden können. Zum Beispiel wird „login“ in Fettschrift (**login**) und „fancy“ in Kursivschrift (_fancy_) angezeigt.
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ Sie müssen nicht für alle von Ihnen verwendeten Tags Metadaten hinzufügen.
|
|||
|
||||
Verwenden Sie den Parameter `tags` mit Ihren *Pfadoperationen* (und `APIRouter`n), um diese verschiedenen Tags zuzuweisen:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ Sie können das aber mit dem Parameter `openapi_url` konfigurieren.
|
|||
|
||||
Um beispielsweise festzulegen, dass es unter `/api/v1/openapi.json` bereitgestellt wird:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Wenn Sie das OpenAPI-Schema vollständig deaktivieren möchten, können Sie `openapi_url=None` festlegen, wodurch auch die Dokumentationsbenutzeroberflächen deaktiviert werden, die es verwenden.
|
||||
|
||||
|
|
@ -117,4 +117,4 @@ Sie können die beiden enthaltenen Dokumentationsbenutzeroberflächen konfigurie
|
|||
|
||||
Um beispielsweise Swagger UI so einzustellen, dass sie unter `/documentation` bereitgestellt wird, und ReDoc zu deaktivieren:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ Die Middleware-Funktion erhält:
|
|||
* Dann gibt es die von der entsprechenden *Pfadoperation* generierte `response` zurück.
|
||||
* Sie können die `response` dann weiter modifizieren, bevor Sie sie zurückgeben.
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ Und auch nachdem die `response` generiert wurde, bevor sie zurückgegeben wird.
|
|||
|
||||
Sie könnten beispielsweise einen benutzerdefinierten Header `X-Process-Time` hinzufügen, der die Zeit in Sekunden enthält, die benötigt wurde, um den Request zu verarbeiten und eine Response zu generieren:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ In diesem Fall macht es Sinn, die Tags in einem `Enum` zu speichern.
|
|||
|
||||
**FastAPI** unterstützt das auf die gleiche Weise wie einfache Strings:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
|
||||
|
||||
## Zusammenfassung und Beschreibung { #summary-and-description }
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ Sie können eine <abbr title="Zusammenfassung">`summary`</abbr> und eine <abbr t
|
|||
|
||||
## Beschreibung mittels Docstring { #description-from-docstring }
|
||||
|
||||
Da Beschreibungen oft mehrere Zeilen lang sind, können Sie die Beschreibung der *Pfadoperation* im <abbr title="Ein mehrzeiliger String (keiner Variable zugewiesen) als erster Ausdruck in einer Funktion, wird für die Dokumentation derselben verwendet">Docstring</abbr> der Funktion deklarieren, und **FastAPI** wird sie daraus auslesen.
|
||||
Da Beschreibungen oft mehrere Zeilen lang sind, können Sie die Beschreibung der *Pfadoperation* im <dfn title="Ein mehrzeiliger String (keiner Variable zugewiesen) als erster Ausdruck in einer Funktion, wird für die Dokumentation derselben verwendet">Docstring</dfn> der Funktion deklarieren, und **FastAPI** wird sie daraus auslesen.
|
||||
|
||||
Sie können <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> im Docstring schreiben, es wird korrekt interpretiert und angezeigt (unter Berücksichtigung der Einrückung des Docstring).
|
||||
|
||||
|
|
@ -90,9 +90,9 @@ Daher, wenn Sie keine vergeben, wird **FastAPI** automatisch eine für „Erfolg
|
|||
|
||||
## Eine *Pfadoperation* deprecaten { #deprecate-a-path-operation }
|
||||
|
||||
Wenn Sie eine *Pfadoperation* als <abbr title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</abbr> kennzeichnen möchten, ohne sie zu entfernen, fügen Sie den Parameter `deprecated` hinzu:
|
||||
Wenn Sie eine *Pfadoperation* als <dfn title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</dfn> kennzeichnen möchten, ohne sie zu entfernen, fügen Sie den Parameter `deprecated` hinzu:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
|
||||
|
||||
Sie wird in der interaktiven Dokumentation gut sichtbar als deprecatet markiert werden:
|
||||
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ Für **FastAPI** spielt es keine Rolle. Es erkennt die Parameter anhand ihrer Na
|
|||
|
||||
Sie können Ihre Funktion also so deklarieren:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Aber bedenken Sie, dass Sie dieses Problem nicht haben, wenn Sie `Annotated` verwenden, da es nicht darauf ankommt, dass Sie keine Funktionsparameter-Defaultwerte für `Query()` oder `Path()` verwenden.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
|
||||
|
||||
## Die Parameter sortieren, wie Sie möchten: Tricks { #order-the-parameters-as-you-need-tricks }
|
||||
|
||||
|
|
@ -83,13 +83,13 @@ Wenn Sie:
|
|||
|
||||
Python wird nichts mit diesem `*` machen, aber es wird wissen, dass alle folgenden Parameter als Schlüsselwortargumente (Schlüssel-Wert-Paare) verwendet werden sollen, auch bekannt als <abbr title="Von: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Selbst wenn diese keinen Defaultwert haben.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
|
||||
|
||||
### Besser mit `Annotated` { #better-with-annotated }
|
||||
|
||||
Bedenken Sie, dass Sie, wenn Sie `Annotated` verwenden, da Sie keine Funktionsparameter-Defaultwerte verwenden, dieses Problem nicht haben werden und wahrscheinlich nicht `*` verwenden müssen.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## Validierung von Zahlen: Größer oder gleich { #number-validations-greater-than-or-equal }
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ Mit `Query` und `Path` (und anderen, die Sie später sehen werden) können Sie Z
|
|||
|
||||
Hier, mit `ge=1`, muss `item_id` eine ganze Zahl sein, die „`g`reater than or `e`qual to“ (größer oder gleich) `1` ist.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
|
||||
|
||||
## Validierung von Zahlen: Größer und kleiner oder gleich { #number-validations-greater-than-and-less-than-or-equal }
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ Das Gleiche gilt für:
|
|||
* `gt`: `g`reater `t`han (größer als)
|
||||
* `le`: `l`ess than or `e`qual (kleiner oder gleich)
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
|
||||
|
||||
## Validierung von Zahlen: Floats, größer und kleiner { #number-validations-floats-greater-than-and-less-than }
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ Also wäre `0.5` ein gültiger Wert. Aber `0.0` oder `0` nicht.
|
|||
|
||||
Und das Gleiche gilt für <abbr title="less than – kleiner als"><code>lt</code></abbr>.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
|
||||
|
||||
## Zusammenfassung { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Sie können Pfad-„Parameter“ oder -„Variablen“ mit der gleichen Syntax deklarieren, welche in Python-<abbr title="Formatstring – Formatierter String: Der String enthält Ausdrücke, die mit geschweiften Klammern umschlossen sind. Solche Stellen werden durch den Wert des Ausdrucks ersetzt">Formatstrings</abbr> verwendet wird:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
|
||||
|
||||
Der Wert des Pfad-Parameters `item_id` wird Ihrer Funktion als das Argument `item_id` übergeben.
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ Wenn Sie dieses Beispiel ausführen und auf <a href="http://127.0.0.1:8000/items
|
|||
|
||||
Sie können den Typ eines Pfad-Parameters in der Argumentliste der Funktion deklarieren, mit Standard-Python-Typannotationen:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
In diesem Fall wird `item_id` als `int` deklariert, also als Ganzzahl.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Dadurch erhalten Sie Editor-Unterstützung innerhalb Ihrer Funktion, mit Fehlerp
|
|||
|
||||
///
|
||||
|
||||
## Daten-<abbr title="Auch bekannt als: Serialisierung, Parsen, Marshalling">Konversion</abbr> { #data-conversion }
|
||||
## Daten-<dfn title="auch bekannt als: Serialisierung, Parsen, Marshalling">Konversion</dfn> { #data-conversion }
|
||||
|
||||
Wenn Sie dieses Beispiel ausführen und Ihren Browser unter <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a> öffnen, sehen Sie als Response:
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ Wenn Sie dieses Beispiel ausführen und Ihren Browser unter <a href="http://127.
|
|||
|
||||
Beachten Sie, dass der Wert, den Ihre Funktion erhält und zurückgibt, die Zahl `3` ist, also ein `int`. Nicht der String `"3"`, also ein `str`.
|
||||
|
||||
Sprich, mit dieser Typdeklaration wird **FastAPI** den <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> automatisch <abbr title="Den String, der von einem HTTP-Request kommt, in Python-Objekte konvertieren">„parsen“</abbr>.
|
||||
Sprich, mit dieser Typdeklaration wird **FastAPI** den <dfn title="Den String, der von einem HTTP-Request kommt, in Python-Daten konvertieren">„parsen“</dfn>.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -118,13 +118,13 @@ Und Sie haben auch einen Pfad `/users/{user_id}`, um Daten über einen spezifisc
|
|||
|
||||
Weil *Pfadoperationen* in ihrer Reihenfolge ausgewertet werden, müssen Sie sicherstellen, dass der Pfad `/users/me` vor `/users/{user_id}` deklariert wurde:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
|
||||
|
||||
Ansonsten würde der Pfad für `/users/{user_id}` auch `/users/me` auswerten, und annehmen, dass ein Parameter `user_id` mit dem Wert `"me"` übergeben wurde.
|
||||
|
||||
Sie können eine Pfadoperation auch nicht erneut definieren:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
|
||||
|
||||
Die erste Definition wird immer verwendet werden, da ihr Pfad zuerst übereinstimmt.
|
||||
|
||||
|
|
@ -140,12 +140,11 @@ Indem Sie von `str` erben, weiß die API-Dokumentation, dass die Werte vom Typ `
|
|||
|
||||
Erstellen Sie dann Klassen-Attribute mit festgelegten Werten, welches die erlaubten Werte sein werden:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
Falls Sie sich fragen, was „AlexNet“, „ResNet“ und „LeNet“ ist, das sind Namen von <abbr title="Genau genommen, Deep-Learning-Modellarchitekturen">Modellen</abbr> für maschinelles Lernen.
|
||||
Falls Sie sich fragen, was „AlexNet“, „ResNet“ und „LeNet“ ist, das sind Namen von <dfn title="Genauer gesagt: Deep-Learning-Modellarchitekturen">Modellen</dfn> für maschinelles Lernen.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -153,7 +152,7 @@ Falls Sie sich fragen, was „AlexNet“, „ResNet“ und „LeNet“ ist, das
|
|||
|
||||
Dann erstellen Sie einen *Pfad-Parameter*, der als Typ die gerade erstellte Enum-Klasse hat (`ModelName`):
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
|
||||
|
||||
### Die API-Dokumentation testen { #check-the-docs }
|
||||
|
||||
|
|
@ -169,13 +168,13 @@ Der *Pfad-Parameter* wird ein *<abbr title="Member – Mitglied: Einer der mögl
|
|||
|
||||
Sie können ihn mit einem Member Ihrer Enumeration `ModelName` vergleichen:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
|
||||
|
||||
#### *Enumerations-Wert* erhalten { #get-the-enumeration-value }
|
||||
|
||||
Den tatsächlichen Wert (in diesem Fall ein `str`) erhalten Sie via `model_name.value`, oder generell, `your_enum_member.value`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -189,7 +188,7 @@ Sie können *Enum-Member* in ihrer *Pfadoperation* zurückgeben, sogar verschach
|
|||
|
||||
Diese werden zu ihren entsprechenden Werten konvertiert (in diesem Fall Strings), bevor sie zum Client übertragen werden:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
|
||||
|
||||
In Ihrem Client erhalten Sie eine JSON-Response, wie etwa:
|
||||
|
||||
|
|
@ -228,7 +227,7 @@ In diesem Fall ist der Name des Parameters `file_path`. Der letzte Teil, `:path`
|
|||
|
||||
Sie verwenden das also wie folgt:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -243,7 +242,7 @@ In dem Fall wäre die URL: `/files//home/johndoe/myfile.txt`, mit einem doppelte
|
|||
In **FastAPI** erhalten Sie mittels kurzer, intuitiver Typdeklarationen:
|
||||
|
||||
* Editor-Unterstützung: Fehlerprüfungen, Codevervollständigung, usw.
|
||||
* Daten "<abbr title="Den String, der von einem HTTP-Request kommt, nach Python-Daten konvertieren">parsen</abbr>"
|
||||
* Daten „<dfn title="Den String, der von einem HTTP-Request kommt, in Python-Daten konvertieren">parsen</dfn>“
|
||||
* Datenvalidierung
|
||||
* API-Annotationen und automatische Dokumentation
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ Stellen Sie sicher, dass Sie [die FastAPI-Version aktualisieren](../deployment/v
|
|||
|
||||
///
|
||||
|
||||
## Verwenden von `Annotated` im Typ für den `q`-Parameter { #use-annotated-in-the-type-for-the-q-parameter }
|
||||
## `Annotated` im Typ für den `q`-Parameter verwenden { #use-annotated-in-the-type-for-the-q-parameter }
|
||||
|
||||
Erinnern Sie sich, dass ich Ihnen zuvor in [Python-Typen-Intro](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank} gesagt habe, dass `Annotated` verwendet werden kann, um Metadaten zu Ihren Parametern hinzuzufügen?
|
||||
|
||||
|
|
@ -47,40 +47,16 @@ Jetzt ist es soweit, dies mit FastAPI zu verwenden. 🚀
|
|||
|
||||
Wir hatten diese Typannotation:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Was wir tun werden, ist, dies mit `Annotated` zu wrappen, sodass es zu:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Beide dieser Versionen bedeuten dasselbe: `q` ist ein Parameter, der ein `str` oder `None` sein kann, und standardmäßig ist er `None`.
|
||||
|
||||
Jetzt springen wir zu den spannenden Dingen. 🎉
|
||||
|
|
@ -109,7 +85,7 @@ FastAPI wird nun:
|
|||
|
||||
## Alternative (alt): `Query` als Defaultwert { #alternative-old-query-as-the-default-value }
|
||||
|
||||
Frühere Versionen von FastAPI (vor <abbr title="vor 2023-03">0.95.0</abbr>) erforderten, dass Sie `Query` als den Defaultwert Ihres Parameters verwendeten, anstatt es innerhalb von `Annotated` zu platzieren. Es besteht eine hohe Wahrscheinlichkeit, dass Sie Code sehen, der es so verwendet, also werde ich es Ihnen erklären.
|
||||
Frühere Versionen von FastAPI (vor <dfn title="vor 2023-03">0.95.0</dfn>) erforderten, dass Sie `Query` als den Defaultwert Ihres Parameters verwendeten, anstatt es innerhalb von `Annotated` zu platzieren. Es besteht eine hohe Wahrscheinlichkeit, dass Sie Code sehen, der es so verwendet, also werde ich es Ihnen erklären.
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -191,7 +167,7 @@ Sie können auch einen `min_length`-Parameter hinzufügen:
|
|||
|
||||
## Reguläre Ausdrücke hinzufügen { #add-regular-expressions }
|
||||
|
||||
Sie können einen <abbr title="Ein regulärer Ausdruck, regex oder regexp genannt, ist eine Sequenz von Zeichen, die ein Suchmuster für Zeichenfolgen definiert.">regulären Ausdruck</abbr> `pattern` definieren, mit dem der Parameter übereinstimmen muss:
|
||||
Sie können einen <dfn title="Ein regulärer Ausdruck, regex oder regexp genannt, ist eine Sequenz von Zeichen, die ein Suchmuster für Strings definiert.">regulären Ausdruck</dfn> `pattern` definieren, mit dem der Parameter übereinstimmen muss:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
|
||||
|
||||
|
|
@ -211,7 +187,7 @@ Natürlich können Sie Defaultwerte verwenden, die nicht `None` sind.
|
|||
|
||||
Nehmen wir an, Sie möchten, dass der `q` Query-Parameter eine `min_length` von `3` hat und einen Defaultwert von `"fixedquery"`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
@ -241,7 +217,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
|
|||
|
||||
Wenn Sie einen Wert als erforderlich deklarieren müssen, während Sie `Query` verwenden, deklarieren Sie einfach keinen Defaultwert:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
|
||||
|
||||
### Erforderlich, kann `None` sein { #required-can-be-none }
|
||||
|
||||
|
|
@ -292,7 +268,7 @@ Die interaktive API-Dokumentation wird entsprechend aktualisiert, um mehrere Wer
|
|||
|
||||
Sie können auch eine Default-`list` von Werten definieren, wenn keine bereitgestellt werden:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
|
||||
|
||||
Wenn Sie zu:
|
||||
|
||||
|
|
@ -315,7 +291,7 @@ gehen, wird der Default für `q` sein: `["foo", "bar"]`, und Ihre Response wird
|
|||
|
||||
Sie können auch `list` direkt verwenden, anstelle von `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
@ -371,7 +347,7 @@ Dann können Sie ein `alias` deklarieren, und dieser Alias wird verwendet, um de
|
|||
|
||||
Nehmen wir an, Ihnen gefällt dieser Parameter nicht mehr.
|
||||
|
||||
Sie müssen ihn eine Weile dort belassen, da es Clients gibt, die ihn verwenden, aber Sie möchten, dass die Dokumentation ihn klar als <abbr title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</abbr> anzeigt.
|
||||
Sie müssen ihn eine Weile dort belassen, da es Clients gibt, die ihn verwenden, aber Sie möchten, dass die Dokumentation ihn klar als <dfn title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</dfn> anzeigt.
|
||||
|
||||
Dann übergeben Sie den Parameter `deprecated=True` an `Query`:
|
||||
|
||||
|
|
@ -393,7 +369,7 @@ Es kann Fälle geben, in denen Sie eine **benutzerdefinierte Validierung** durch
|
|||
|
||||
In diesen Fällen können Sie eine **benutzerdefinierte Validierungsfunktion** verwenden, die nach der normalen Validierung angewendet wird (z. B. nach der Validierung, dass der Wert ein `str` ist).
|
||||
|
||||
Sie können dies mit <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">Pydantic's `AfterValidator`</a> innerhalb von `Annotated` erreichen.
|
||||
Sie können dies mit <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">Pydantics `AfterValidator`</a> innerhalb von `Annotated` erreichen.
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -401,7 +377,7 @@ Pydantic unterstützt auch <a href="https://docs.pydantic.dev/latest/concepts/va
|
|||
|
||||
///
|
||||
|
||||
Zum Beispiel überprüft dieser benutzerdefinierte Validator, ob die Artikel-ID mit `isbn-` für eine <abbr title="ISBN bedeutet Internationale Standardbuchnummer">ISBN</abbr>-Buchnummer oder mit `imdb-` für eine <abbr title="IMDB (Internet Movie Database) ist eine Website mit Informationen über Filme">IMDB</abbr>-Film-URL-ID beginnt:
|
||||
Zum Beispiel überprüft dieser benutzerdefinierte Validator, ob die Artikel-ID mit `isbn-` für eine <abbr title="International Standard Book Number - Internationale Standardbuchnummer">ISBN</abbr>-Buchnummer oder mit `imdb-` für eine <abbr title="Internet Movie Database - Internet-Filmdatenbank: eine Website mit Informationen über Filme">IMDB</abbr>-Film-URL-ID beginnt:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
|
|
@ -435,7 +411,7 @@ Haben Sie bemerkt? Eine Zeichenkette mit `value.startswith()` kann ein Tuple üb
|
|||
|
||||
#### Ein zufälliges Item { #a-random-item }
|
||||
|
||||
Mit `data.items()` erhalten wir ein <abbr title="Etwas, das man mit einer for-Schleife durchlaufen kann, wie eine Liste, Set, usw.">iterierbares Objekt</abbr> mit Tupeln, die Schlüssel und Wert für jedes <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr>-Element enthalten.
|
||||
Mit `data.items()` erhalten wir ein <dfn title="Etwas, das man mit einer for-Schleife durchlaufen kann, wie eine Liste, Set, usw.">iterierbares Objekt</dfn> mit Tupeln, die Schlüssel und Wert für jedes <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr>-Element enthalten.
|
||||
|
||||
Wir konvertieren dieses iterierbare Objekt mit `list(data.items())` in eine richtige `list`.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Wenn Sie in Ihrer Funktion andere Parameter deklarieren, die nicht Teil der Pfad-Parameter sind, dann werden diese automatisch als „Query“-Parameter interpretiert.
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
|
||||
|
||||
Die <abbr title="Abfrage">Query</abbr> ist die Menge von Schlüssel-Wert-Paaren, die nach dem `?` in einer URL folgen und durch `&`-Zeichen getrennt sind.
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ Aber wenn Sie sie mit Python-Typen deklarieren (im obigen Beispiel als `int`), w
|
|||
Die gleichen Prozesse, die für Pfad-Parameter gelten, werden auch auf Query-Parameter angewendet:
|
||||
|
||||
* Editor Unterstützung (natürlich)
|
||||
* Daten-<abbr title="Konvertieren des Strings, der von einem HTTP-Request kommt, in Python-Daten">„Parsen“</abbr>
|
||||
* Daten-<dfn title="Konvertieren des Strings, der von einem HTTP-Request kommt, in Python-Daten">„Parsen“</dfn>
|
||||
* Datenvalidierung
|
||||
* Automatische Dokumentation
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ Wenn Sie keinen spezifischen Wert haben wollen, sondern der Parameter einfach op
|
|||
|
||||
Aber wenn Sie wollen, dass ein Query-Parameter erforderlich ist, vergeben Sie einfach keinen Defaultwert:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
|
||||
|
||||
Hier ist `needy` ein erforderlicher Query-Parameter vom Typ `str`.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ Das liegt daran, dass hochgeladene Dateien als „Formulardaten“ gesendet werd
|
|||
|
||||
Importieren Sie `File` und `UploadFile` von `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## `File`-Parameter definieren { #define-file-parameters }
|
||||
|
||||
Erstellen Sie Datei-Parameter, so wie Sie es auch mit `Body` und `Form` machen würden:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ Aber es gibt viele Fälle, in denen Sie davon profitieren, `UploadFile` zu verwe
|
|||
|
||||
Definieren Sie einen Datei-Parameter mit dem Typ `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
|
||||
|
||||
`UploadFile` zu verwenden, hat mehrere Vorzüge gegenüber `bytes`:
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ Sie können eine Datei optional machen, indem Sie Standard-Typannotationen verwe
|
|||
|
||||
Sie können auch `File()` mit `UploadFile` verwenden, um zum Beispiel zusätzliche Metadaten zu setzen:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
|
||||
|
||||
## Mehrere Datei-Uploads { #multiple-file-uploads }
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ Diese werden demselben Formularfeld zugeordnet, welches mit den Formulardaten ge
|
|||
|
||||
Um das zu machen, deklarieren Sie eine Liste von `bytes` oder `UploadFile`s:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
|
||||
|
||||
Sie erhalten, wie deklariert, eine `list` von `bytes` oder `UploadFile`s.
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ Sie können auch `from starlette.responses import HTMLResponse` verwenden.
|
|||
|
||||
Und so wie zuvor können Sie `File()` verwenden, um zusätzliche Parameter zu setzen, sogar für `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
|
||||
|
||||
## Zusammenfassung { #recap }
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Dies wird seit FastAPI Version `0.113.0` unterstützt. 🤓
|
|||
|
||||
Sie müssen nur ein **Pydantic-Modell** mit den Feldern deklarieren, die Sie als **Formularfelder** erhalten möchten, und dann den Parameter als `Form` deklarieren:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
|
||||
|
||||
**FastAPI** wird die Daten für **jedes Feld** aus den **Formulardaten** im <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> **extrahieren** und Ihnen das von Ihnen definierte Pydantic-Modell übergeben.
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ Dies wird seit FastAPI Version `0.114.0` unterstützt. 🤓
|
|||
|
||||
Sie können die Modellkonfiguration von Pydantic verwenden, um jegliche `extra` Felder zu `verbieten`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
|
||||
|
||||
Wenn ein Client versucht, einige zusätzliche Daten zu senden, erhält er eine **Error-<abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>**.
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ $ pip install python-multipart
|
|||
|
||||
## `File` und `Form` importieren { #import-file-and-form }
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## `File` und `Form`-Parameter definieren { #define-file-and-form-parameters }
|
||||
|
||||
Erstellen Sie Datei- und Formularparameter, so wie Sie es auch mit `Body` oder `Query` machen würden:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
|
||||
|
||||
Die Datei- und Formularfelder werden als Formulardaten hochgeladen, und Sie erhalten diese Dateien und Formularfelder.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,17 +18,17 @@ $ pip install python-multipart
|
|||
|
||||
Importieren Sie `Form` von `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## `Form`-Parameter definieren { #define-form-parameters }
|
||||
|
||||
Erstellen Sie Formular-Parameter, so wie Sie es auch mit `Body` und `Query` machen würden:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
Zum Beispiel stellt eine der Möglichkeiten, die OAuth2-Spezifikation zu verwenden (genannt „password flow“), die Bedingung, einen `username` und ein `password` als Formularfelder zu senden.
|
||||
|
||||
Die <abbr title="Specification – Spezifikation">Spec</abbr> erfordert, dass die Felder exakt `username` und `password` genannt werden und als Formularfelder, nicht JSON, gesendet werden.
|
||||
Die <dfn title="Spezifikation">Spezifikation</dfn> erfordert, dass die Felder exakt `username` und `password` genannt werden und als Formularfelder, nicht JSON, gesendet werden.
|
||||
|
||||
Mit `Form` haben Sie die gleichen Konfigurationsmöglichkeiten wie mit `Body` (und `Query`, `Path`, `Cookie`), inklusive Validierung, Beispielen, einem Alias (z. B. `user-name` statt `username`), usw.
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ Es kann Fälle geben, bei denen Sie etwas zurückgeben, das kein gültiges Pydan
|
|||
|
||||
Der häufigste Anwendungsfall ist, wenn Sie [eine Response direkt zurückgeben, wie es später im Handbuch für fortgeschrittene Benutzer erläutert wird](../advanced/response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
|
||||
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
|
||||
|
||||
Dieser einfache Anwendungsfall wird automatisch von FastAPI gehandhabt, weil die Annotation des Rückgabetyps die Klasse (oder eine Unterklasse von) `Response` ist.
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ Und Tools werden auch glücklich sein, weil sowohl `RedirectResponse` als auch `
|
|||
|
||||
Sie können auch eine Unterklasse von `Response` in der Typannotation verwenden.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
|
||||
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
|
||||
|
||||
Das wird ebenfalls funktionieren, weil `RedirectResponse` eine Unterklasse von `Response` ist, und FastAPI sich um diesen einfachen Anwendungsfall automatisch kümmert.
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ Das wird ebenfalls funktionieren, weil `RedirectResponse` eine Unterklasse von `
|
|||
|
||||
Aber wenn Sie ein beliebiges anderes Objekt zurückgeben, das kein gültiger Pydantic-Typ ist (z. B. ein Datenbank-Objekt), und Sie annotieren es so in der Funktion, wird FastAPI versuchen, ein Pydantic-Responsemodell von dieser Typannotation zu erstellen, und scheitern.
|
||||
|
||||
Das gleiche wird passieren, wenn Sie eine <abbr title="Eine Union mehrerer Typen bedeutet: „Irgendeiner dieser Typen“">Union</abbr> mehrerer Typen haben, und einer oder mehrere sind nicht gültige Pydantic-Typen. Zum Beispiel funktioniert folgendes nicht 💥:
|
||||
Das gleiche wird passieren, wenn Sie eine <dfn title="Eine Union mehrerer Typen bedeutet: „Irgendeiner dieser Typen“">Union</dfn> mehrerer Typen haben, und einer oder mehrere sind nicht gültige Pydantic-Typen. Zum Beispiel funktioniert folgendes nicht 💥:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Genauso wie Sie ein Responsemodell angeben können, können Sie auch den HTTP-St
|
|||
* `@app.delete()`
|
||||
* usw.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ Kurz gefasst:
|
|||
|
||||
/// tip | Tipp
|
||||
|
||||
Um mehr über die einzelnen Statuscodes zu erfahren und welcher wofür verwendet wird, sehen Sie sich die <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Mozilla-Entwicklernetzwerk">MDN</abbr> Dokumentation über HTTP-Statuscodes</a> an.
|
||||
Um mehr über die einzelnen Statuscodes zu erfahren und welcher wofür verwendet wird, sehen Sie sich die <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Mozilla-Entwicklernetzwerk">MDN</abbr> Dokumentation über HTTP-Statuscodes</a> an.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ Um mehr über die einzelnen Statuscodes zu erfahren und welcher wofür verwendet
|
|||
|
||||
Lassen Sie uns das vorherige Beispiel noch einmal anschauen:
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
`201` ist der Statuscode für „Created“ („Erzeugt“).
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ Aber Sie müssen sich nicht merken, was jeder dieser Codes bedeutet.
|
|||
|
||||
Sie können die Annehmlichkeit von Variablen aus `fastapi.status` nutzen.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
|
||||
|
||||
Diese sind nur eine Annehmlichkeit, sie enthalten dieselbe Zahl, aber so können Sie die Autovervollständigung Ihres Editors verwenden, um sie zu finden:
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ Sie können natürlich auch mehrere `examples` übergeben:
|
|||
|
||||
Wenn Sie das tun, werden die Beispiele Teil des internen **JSON-Schemas** für diese Body-Daten.
|
||||
|
||||
<abbr title="2023-08-26">Während dies geschrieben wird</abbr>, unterstützt Swagger UI, das für die Anzeige der Dokumentations-Benutzeroberfläche zuständige Tool, jedoch nicht die Anzeige mehrerer Beispiele für die Daten in **JSON Schema**. Aber lesen Sie unten für einen Workaround weiter.
|
||||
Nichtsdestotrotz unterstützt Swagger UI, das für die Anzeige der Dokumentations-Benutzeroberfläche zuständige Tool, zum <dfn title="2023-08-26">Zeitpunkt der Erstellung</dfn> nicht die Anzeige mehrerer Beispiele für die Daten in **JSON Schema**. Aber lesen Sie unten für einen Workaround weiter.
|
||||
|
||||
### OpenAPI-spezifische `examples` { #openapi-specific-examples }
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Lassen Sie uns zunächst einfach den Code verwenden und sehen, wie er funktionie
|
|||
|
||||
Kopieren Sie das Beispiel in eine Datei `main.py`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py *}
|
||||
|
||||
## Ausführen { #run-it }
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ Betrachten wir es also aus dieser vereinfachten Sicht:
|
|||
* Der Benutzer klickt im Frontend, um zu einem anderen Abschnitt der Frontend-Web-Anwendung zu gelangen.
|
||||
* Das Frontend muss weitere Daten von der API abrufen.
|
||||
* Es benötigt jedoch eine Authentifizierung für diesen bestimmten Endpunkt.
|
||||
* Um sich also bei unserer API zu authentifizieren, sendet es einen Header `Authorization` mit dem Wert `Bearer ` plus dem Token.
|
||||
* Um sich also bei unserer API zu authentifizieren, sendet es einen `Authorization`-Header mit dem Wert `Bearer ` plus dem Token.
|
||||
* Wenn der Token `foobar` enthielte, wäre der Inhalt des `Authorization`-Headers: `Bearer foobar`.
|
||||
|
||||
## **FastAPI**s `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer }
|
||||
|
|
@ -134,7 +134,7 @@ In dem Fall gibt Ihnen **FastAPI** ebenfalls die Tools, die Sie zum Erstellen br
|
|||
|
||||
Wenn wir eine Instanz der Klasse `OAuth2PasswordBearer` erstellen, übergeben wir den Parameter `tokenUrl`. Dieser Parameter enthält die URL, die der Client (das Frontend, das im Browser des Benutzers ausgeführt wird) verwendet, wenn er den `username` und das `password` sendet, um einen Token zu erhalten.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ Es kann also mit `Depends` verwendet werden.
|
|||
|
||||
Jetzt können Sie dieses `oauth2_scheme` als Abhängigkeit `Depends` übergeben.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
Diese Abhängigkeit stellt einen `str` bereit, der dem Parameter `token` der *Pfadoperation-Funktion* zugewiesen wird.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Im vorherigen Kapitel hat das Sicherheitssystem (das auf dem Dependency Injection System basiert) der *Pfadoperation-Funktion* einen `token` vom Typ `str` überreicht:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
Aber das ist immer noch nicht so nützlich.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ OAuth2 ist eine Spezifikation, die verschiedene Möglichkeiten zur Handhabung vo
|
|||
|
||||
Es handelt sich um eine recht umfangreiche Spezifikation, und sie deckt mehrere komplexe Anwendungsfälle ab.
|
||||
|
||||
Sie umfasst Möglichkeiten zur Authentifizierung mithilfe eines „Dritten“ („third party“).
|
||||
Sie umfasst Möglichkeiten zur Authentifizierung mithilfe eines „Dritten“.
|
||||
|
||||
Das ist es, was alle diese „Login mit Facebook, Google, X (Twitter), GitHub“-Systeme unter der Haube verwenden.
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,11 @@ Und eine weitere, um zu überprüfen, ob ein empfangenes Passwort mit dem gespei
|
|||
|
||||
Und noch eine, um einen Benutzer zu authentifizieren und zurückzugeben.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
|
||||
|
||||
Wenn `authenticate_user` mit einem Benutzernamen aufgerufen wird, der in der Datenbank nicht existiert, führen wir dennoch `verify_password` gegen einen Dummy-Hash aus.
|
||||
|
||||
So stellt man sicher, dass der Endpunkt ungefähr gleich viel Zeit für die Antwort benötigt, unabhängig davon, ob der Benutzername gültig ist oder nicht. Dadurch werden Timing-Angriffe verhindert, mit denen vorhandene Benutzernamen ermittelt werden könnten.
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
|
|
@ -152,7 +156,7 @@ Definieren Sie ein Pydantic-Modell, das im Token-Endpunkt für die <abbr title="
|
|||
|
||||
Erstellen Sie eine Hilfsfunktion, um einen neuen Zugriffstoken zu generieren.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
|
||||
|
||||
## Die Abhängigkeiten aktualisieren { #update-the-dependencies }
|
||||
|
||||
|
|
@ -162,7 +166,7 @@ Dekodieren Sie den empfangenen Token, validieren Sie ihn und geben Sie den aktue
|
|||
|
||||
Wenn der Token ungültig ist, geben Sie sofort einen HTTP-Fehler zurück.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
|
||||
|
||||
## Die *Pfadoperation* `/token` aktualisieren { #update-the-token-path-operation }
|
||||
|
||||
|
|
@ -170,7 +174,7 @@ Erstellen Sie ein <abbr title="Zeitdifferenz">`timedelta`</abbr> mit der Ablaufz
|
|||
|
||||
Erstellen Sie einen echten JWT-Zugriffstoken und geben Sie ihn zurück.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
|
||||
|
||||
### Technische Details zum JWT-„Subjekt“ `sub` { #technical-details-about-the-jwt-subject-sub }
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Aber für die Login-*Pfadoperation* müssen wir diese Namen verwenden, um mit de
|
|||
|
||||
Die Spezifikation besagt auch, dass `username` und `password` als Formulardaten gesendet werden müssen (hier also kein JSON).
|
||||
|
||||
### <abbr title="Geltungsbereich">`scope`</abbr> { #scope }
|
||||
### `scope` { #scope }
|
||||
|
||||
Ferner sagt die Spezifikation, dass der Client ein weiteres Formularfeld "`scope`" („Geltungsbereich“) senden kann.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Mit `StaticFiles` können Sie statische Dateien aus einem Verzeichnis automatisc
|
|||
* Importieren Sie `StaticFiles`.
|
||||
* „Mounten“ Sie eine `StaticFiles()`-Instanz in einem bestimmten Pfad.
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
/// note | Technische Details
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Verwenden Sie das `TestClient`-Objekt auf die gleiche Weise wie `httpx`.
|
|||
|
||||
Schreiben Sie einfache `assert`-Anweisungen mit den Standard-Python-Ausdrücken, die Sie überprüfen müssen (wiederum, Standard-`pytest`).
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
|
||||
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ Nehmen wir an, Sie haben eine Dateistruktur wie in [Größere Anwendungen](bigge
|
|||
In der Datei `main.py` haben Sie Ihre **FastAPI**-Anwendung:
|
||||
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/main.py *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/main.py *}
|
||||
|
||||
|
||||
### Testdatei { #testing-file }
|
||||
|
|
@ -93,7 +93,7 @@ Dann könnten Sie eine Datei `test_main.py` mit Ihren Tests haben. Sie könnte s
|
|||
|
||||
Da sich diese Datei im selben Package befindet, können Sie relative Importe verwenden, um das Objekt `app` aus dem `main`-Modul (`main.py`) zu importieren:
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
|
||||
|
||||
|
||||
... und haben den Code für die Tests wie zuvor.
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ $ cd awesome-project
|
|||
|
||||
## Eine virtuelle Umgebung erstellen { #create-a-virtual-environment }
|
||||
|
||||
Wenn Sie zum **ersten Mal** an einem Python-Projekt arbeiten, erstellen Sie eine virtuelle Umgebung **<abbr title="es gibt andere Optionen, dies ist eine einfache Richtlinie">innerhalb Ihres Projekts</abbr>**.
|
||||
Wenn Sie zum **ersten Mal** an einem Python-Projekt arbeiten, erstellen Sie eine virtuelle Umgebung **<dfn title="es gibt andere Optionen, dies ist eine einfache Richtlinie">innerhalb Ihres Projekts</dfn>**.
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ $ source .venv/Scripts/activate
|
|||
|
||||
Jedes Mal, wenn Sie ein **neues Paket** in dieser Umgebung installieren, aktivieren Sie die Umgebung erneut.
|
||||
|
||||
So stellen Sie sicher, dass, wenn Sie ein **Terminalprogramm (<abbr title="command line interface – Kommandozeileninterface">CLI</abbr>)** verwenden, das durch dieses Paket installiert wurde, Sie das aus Ihrer virtuellen Umgebung verwenden und nicht eines, das global installiert ist, wahrscheinlich mit einer anderen Version als der, die Sie benötigen.
|
||||
So stellen Sie sicher, dass, wenn Sie ein **Terminalprogramm (<abbr title="command line interface - Kommandozeileninterface">CLI</abbr>)** verwenden, das durch dieses Paket installiert wurde, Sie das aus Ihrer virtuellen Umgebung verwenden und nicht eines, das global installiert ist, wahrscheinlich mit einer anderen Version als der, die Sie benötigen.
|
||||
|
||||
///
|
||||
|
||||
|
|
@ -639,7 +639,7 @@ $ source .venv/Scripts/activate
|
|||
|
||||
Dieser Befehl erstellt oder ändert einige [Umgebungsvariablen](environment-variables.md){.internal-link target=_blank}, die für die nächsten Befehle verfügbar sein werden.
|
||||
|
||||
Eine dieser Variablen ist die `PATH`-Variable.
|
||||
Eine dieser Variablen ist die `PATH`-Umgebungsvariable.
|
||||
|
||||
/// tip | Tipp
|
||||
|
||||
|
|
@ -649,7 +649,7 @@ Sie können mehr über die `PATH`-Umgebungsvariable im Abschnitt [Umgebungsvaria
|
|||
|
||||
Das Aktivieren einer virtuellen Umgebung fügt deren Pfad `.venv/bin` (auf Linux und macOS) oder `.venv\Scripts` (auf Windows) zur `PATH`-Umgebungsvariable hinzu.
|
||||
|
||||
Angenommen, die `PATH`-Variable sah vor dem Aktivieren der Umgebung so aus:
|
||||
Angenommen, die `PATH`-Umgebungsvariable sah vor dem Aktivieren der Umgebung so aus:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
|
|
@ -678,7 +678,7 @@ Das bedeutet, dass das System nach Programmen sucht in:
|
|||
|
||||
////
|
||||
|
||||
Nach dem Aktivieren der virtuellen Umgebung würde die `PATH`-Variable folgendermaßen aussehen:
|
||||
Nach dem Aktivieren der virtuellen Umgebung würde die `PATH`-Umgebungsvariable folgendermaßen aussehen:
|
||||
|
||||
//// tab | Linux, macOS
|
||||
|
||||
|
|
@ -728,7 +728,7 @@ finden und dieses verwenden.
|
|||
|
||||
////
|
||||
|
||||
Ein wichtiger Punkt ist, dass es den Pfad der virtuellen Umgebung am **Anfang** der `PATH`-Variable platziert. Das System wird es **vor** allen anderen verfügbaren Pythons finden. Auf diese Weise, wenn Sie `python` ausführen, wird das Python **aus der virtuellen Umgebung** verwendet anstelle eines anderen `python` (zum Beispiel, einem `python` aus einer globalen Umgebung).
|
||||
Ein wichtiger Punkt ist, dass es den Pfad der virtuellen Umgebung am **Anfang** der `PATH`-Umgebungsvariable platziert. Das System wird es **vor** allen anderen verfügbaren Pythons finden. Auf diese Weise, wenn Sie `python` ausführen, wird das Python **aus der virtuellen Umgebung** verwendet anstelle eines anderen `python` (zum Beispiel, einem `python` aus einer globalen Umgebung).
|
||||
|
||||
Das Aktivieren einer virtuellen Umgebung ändert auch ein paar andere Dinge, aber dies ist eines der wichtigsten Dinge, die es tut.
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ a.internal-link::after {
|
|||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.md-footer-meta .md-social {
|
||||
padding-right: 4rem;
|
||||
}
|
||||
|
||||
.user-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var script = document.createElement("script");
|
||||
script.src = "https://widget.kapa.ai/kapa-widget.bundle.js";
|
||||
script.setAttribute("data-website-id", "91f47f27-b405-4299-bf5f-a1c0ec07b3cc");
|
||||
script.setAttribute("data-project-name", "FastAPI");
|
||||
script.setAttribute("data-project-color", "#009485");
|
||||
script.setAttribute("data-project-logo", "https://fastapi.tiangolo.com/img/favicon.png");
|
||||
script.setAttribute("data-bot-protection-mechanism", "hcaptcha");
|
||||
script.setAttribute("data-button-height", "3rem");
|
||||
script.setAttribute("data-button-width", "3rem");
|
||||
script.setAttribute("data-button-border-radius", "50%");
|
||||
script.setAttribute("data-button-padding", "0");
|
||||
script.setAttribute("data-button-image", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 8V4H8'/%3E%3Crect width='16' height='12' x='4' y='8' rx='2'/%3E%3Cpath d='M2 14h2'/%3E%3Cpath d='M20 14h2'/%3E%3Cpath d='M15 13v2'/%3E%3Cpath d='M9 13v2'/%3E%3C/svg%3E");
|
||||
script.setAttribute("data-button-image-height", "20px");
|
||||
script.setAttribute("data-button-image-width", "20px");
|
||||
script.setAttribute("data-button-text", "Ask AI");
|
||||
script.setAttribute("data-button-text-font-size", "0.5rem");
|
||||
script.setAttribute("data-button-text-font-family", "Roboto, sans-serif");
|
||||
script.setAttribute("data-button-text-color", "#FFFFFF");
|
||||
script.setAttribute("data-modal-border-radius", "0.5rem");
|
||||
script.setAttribute("data-modal-header-bg-color", "#009485");
|
||||
script.setAttribute("data-modal-title", "FastAPI AI Assistant");
|
||||
script.setAttribute("data-modal-title-color", "#FFFFFF");
|
||||
script.setAttribute("data-modal-title-font-family", "Roboto, sans-serif");
|
||||
script.setAttribute("data-modal-example-questions", "How to define a route?,How to validate models?,How to handle responses?,How to deploy FastAPI?");
|
||||
script.setAttribute("data-modal-disclaimer", "AI-generated answers based on FastAPI [documentation](https://fastapi.tiangolo.com/) and [community discussions](https://github.com/fastapi/fastapi/discussions). Always verify important information.");
|
||||
script.async = true;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
|
@ -9,10 +9,16 @@ hide:
|
|||
|
||||
### Docs
|
||||
|
||||
* 🔥 Remove Python 3.9 specific files, no longer needed after updating translations. PR [#14931](https://github.com/fastapi/fastapi/pull/14931) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Update docs for JWT to prevent timing attacks. PR [#14908](https://github.com/fastapi/fastapi/pull/14908) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Update translations for ko (update-all and add-missing). PR [#14923](https://github.com/fastapi/fastapi/pull/14923) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for uk (add-missing). PR [#14922](https://github.com/fastapi/fastapi/pull/14922) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for zh-hant (update-all and add-missing). PR [#14921](https://github.com/fastapi/fastapi/pull/14921) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for fr (update-all and add-missing). PR [#14920](https://github.com/fastapi/fastapi/pull/14920) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for de (update-all) . PR [#14910](https://github.com/fastapi/fastapi/pull/14910) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for ja (update-all). PR [#14916](https://github.com/fastapi/fastapi/pull/14916) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for pt (update-all). PR [#14912](https://github.com/fastapi/fastapi/pull/14912) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for es (update-all and add-missing). PR [#14911](https://github.com/fastapi/fastapi/pull/14911) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
|
@ -23,6 +29,7 @@ hide:
|
|||
|
||||
### Internal
|
||||
|
||||
* 🎨 Upgrade typing syntax for Python 3.10. PR [#14932](https://github.com/fastapi/fastapi/pull/14932) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Bump cryptography from 46.0.4 to 46.0.5. PR [#14892](https://github.com/fastapi/fastapi/pull/14892) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump pillow from 12.1.0 to 12.1.1. PR [#14899](https://github.com/fastapi/fastapi/pull/14899) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
|
||||
|
|
|
|||
|
|
@ -342,5 +342,6 @@ extra_css:
|
|||
extra_javascript:
|
||||
- js/termynal.js
|
||||
- js/custom.js
|
||||
- js/init_kapa_widget.js
|
||||
hooks:
|
||||
- ../../scripts/mkdocs_hooks.py
|
||||
|
|
|
|||
|
|
@ -0,0 +1,503 @@
|
|||
# Fichier de test LLM { #llm-test-file }
|
||||
|
||||
Ce document teste si le <abbr title="Large Language Model - Grand modèle de langage">LLM</abbr>, qui traduit la documentation, comprend le `general_prompt` dans `scripts/translate.py` et l’invite spécifique à la langue dans `docs/{language code}/llm-prompt.md`. L’invite spécifique à la langue est ajoutée à la fin de `general_prompt`.
|
||||
|
||||
Les tests ajoutés ici seront visibles par tous les concepteurs d’invites spécifiques à chaque langue.
|
||||
|
||||
Utiliser comme suit :
|
||||
|
||||
* Avoir une invite spécifique à la langue - `docs/{language code}/llm-prompt.md`.
|
||||
* Effectuer une nouvelle traduction de ce document dans votre langue cible souhaitée (voir par exemple la commande `translate-page` de `translate.py`). Cela créera la traduction sous `docs/{language code}/docs/_llm-test.md`.
|
||||
* Vérifier si tout est correct dans la traduction.
|
||||
* Si nécessaire, améliorer votre invite spécifique à la langue, l’invite générale, ou le document anglais.
|
||||
* Corriger ensuite manuellement les problèmes restants dans la traduction, afin que ce soit une bonne traduction.
|
||||
* Retraduire, en ayant la bonne traduction en place. Le résultat idéal serait que le LLM ne fasse plus aucun changement à la traduction. Cela signifie que l’invite générale et votre invite spécifique à la langue sont aussi bonnes que possible (il fera parfois quelques changements apparemment aléatoires, la raison étant que <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">les LLM ne sont pas des algorithmes déterministes</a>).
|
||||
|
||||
Les tests :
|
||||
|
||||
## Extraits de code { #code-snippets }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Ceci est un extrait de code : `foo`. Et ceci est un autre extrait de code : `bar`. Et encore un autre : `baz quux`.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Le contenu des extraits de code doit être laissé tel quel.
|
||||
|
||||
Voir la section `### Content of code snippets` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Guillemets { #quotes }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Hier, mon ami a écrit : « Si vous écrivez « incorrectly » correctement, vous l’avez écrit de façon incorrecte ». À quoi j’ai répondu : « Correct, mais ‘incorrectly’ est incorrectement non pas ‘« incorrectly »’ ».
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Le LLM traduira probablement ceci de manière erronée. Il est seulement intéressant de voir s’il conserve la traduction corrigée lors d’une retraduction.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Le concepteur de l’invite peut choisir s’il souhaite convertir les guillemets neutres en guillemets typographiques. Il est acceptable de les laisser tels quels.
|
||||
|
||||
Voir par exemple la section `### Quotes` dans `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
||||
## Guillemets dans les extraits de code { #quotes-in-code-snippets }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
`pip install "foo[bar]"`
|
||||
|
||||
Exemples de littéraux de chaîne dans des extraits de code : `"this"`, `'that'`.
|
||||
|
||||
Un exemple difficile de littéraux de chaîne dans des extraits de code : `f"I like {'oranges' if orange else "apples"}"`
|
||||
|
||||
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
... Cependant, les guillemets à l’intérieur des extraits de code doivent rester tels quels.
|
||||
|
||||
////
|
||||
|
||||
## Blocs de code { #code-blocks }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Un exemple de code Bash ...
|
||||
|
||||
```bash
|
||||
# Afficher un message de bienvenue à l'univers
|
||||
echo "Hello universe"
|
||||
```
|
||||
|
||||
... et un exemple de code console ...
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
|
||||
Searching for package file structure
|
||||
```
|
||||
|
||||
... et un autre exemple de code console ...
|
||||
|
||||
```console
|
||||
// Créer un répertoire "Code"
|
||||
$ mkdir code
|
||||
// Aller dans ce répertoire
|
||||
$ cd code
|
||||
```
|
||||
|
||||
... et un exemple de code Python ...
|
||||
|
||||
```Python
|
||||
wont_work() # Cela ne fonctionnera pas 😱
|
||||
works(foo="bar") # Cela fonctionne 🎉
|
||||
```
|
||||
|
||||
... et c’est tout.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Le code dans les blocs de code ne doit pas être modifié, à l’exception des commentaires.
|
||||
|
||||
Voir la section `### Content of code blocks` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Onglets et encadrés colorés { #tabs-and-colored-boxes }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
/// info | Info
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// note | Remarque
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// note | Détails techniques
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// check | Vérifications
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// tip | Astuce
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// warning | Alertes
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// danger | Danger
|
||||
Du texte
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Les onglets et les blocs « Info »/« Note »/« Warning »/etc. doivent avoir la traduction de leur titre ajoutée après une barre verticale (« | »).
|
||||
|
||||
Voir les sections `### Special blocks` et `### Tab blocks` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Liens Web et internes { #web-and-internal-links }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Le texte du lien doit être traduit, l’adresse du lien doit rester inchangée :
|
||||
|
||||
* [Lien vers le titre ci-dessus](#code-snippets)
|
||||
* [Lien interne](index.md#installation){.internal-link target=_blank}
|
||||
* <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">Lien externe</a>
|
||||
* <a href="https://fastapi.tiangolo.com/css/styles.css" class="external-link" target="_blank">Lien vers une feuille de style</a>
|
||||
* <a href="https://fastapi.tiangolo.com/js/logic.js" class="external-link" target="_blank">Lien vers un script</a>
|
||||
* <a href="https://fastapi.tiangolo.com/img/foo.jpg" class="external-link" target="_blank">Lien vers une image</a>
|
||||
|
||||
Le texte du lien doit être traduit, l’adresse du lien doit pointer vers la traduction :
|
||||
|
||||
* <a href="https://fastapi.tiangolo.com/fr/" class="external-link" target="_blank">Lien FastAPI</a>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Les liens doivent être traduits, mais leur adresse doit rester inchangée. Exception faite des liens absolus vers des pages de la documentation FastAPI. Dans ce cas, il faut pointer vers la traduction.
|
||||
|
||||
Voir la section `### Links` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Éléments HTML « abbr » { #html-abbr-elements }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Voici quelques éléments entourés d’un élément HTML « abbr » (certains sont inventés) :
|
||||
|
||||
### L’abbr fournit une expression complète { #the-abbr-gives-a-full-phrase }
|
||||
|
||||
* <abbr title="Getting Things Done - S'organiser pour réussir">GTD</abbr>
|
||||
* <abbr title="less than - inférieur à"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token - Jeton Web XML">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - Interface passerelle serveur parallèle">PSGI</abbr>
|
||||
|
||||
### L’abbr donne une expression complète et une explication { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network - Réseau des développeurs Mozilla: documentation pour les développeurs, écrite par l’équipe Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output - Entrée/Sortie: lecture ou écriture sur le disque, communications réseau.">I/O</abbr>.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Les attributs « title » des éléments « abbr » sont traduits en suivant des consignes spécifiques.
|
||||
|
||||
Les traductions peuvent ajouter leurs propres éléments « abbr » que le LLM ne doit pas supprimer. Par exemple pour expliquer des mots anglais.
|
||||
|
||||
Voir la section `### HTML abbr elements` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Éléments HTML « dfn » { #html-dfn-elements }
|
||||
|
||||
* <dfn title="Un groupe de machines configurées pour être connectées et travailler ensemble d’une certaine manière.">grappe</dfn>
|
||||
* <dfn title="Une méthode d’apprentissage automatique qui utilise des réseaux de neurones artificiels avec de nombreuses couches cachées entre les couches d’entrée et de sortie, développant ainsi une structure interne complète">Apprentissage profond</dfn>
|
||||
|
||||
## Titres { #headings }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
### Créer une application Web - un tutoriel { #develop-a-webapp-a-tutorial }
|
||||
|
||||
Bonjour.
|
||||
|
||||
### Annotations de type et indications de type { #type-hints-and-annotations }
|
||||
|
||||
Rebonjour.
|
||||
|
||||
### Superclasses et sous-classes { #super-and-subclasses }
|
||||
|
||||
Rebonjour.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
La seule règle stricte pour les titres est que le LLM laisse la partie hachage entre accolades inchangée, ce qui garantit que les liens ne se rompent pas.
|
||||
|
||||
Voir la section `### Headings` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
Pour certaines consignes spécifiques à la langue, voir par exemple la section `### Headings` dans `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
||||
## Termes utilisés dans les documents { #terms-used-in-the-docs }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
* vous
|
||||
* votre
|
||||
|
||||
* p. ex.
|
||||
* etc.
|
||||
|
||||
* `foo` en tant que `int`
|
||||
* `bar` en tant que `str`
|
||||
* `baz` en tant que `list`
|
||||
|
||||
* le Tutoriel - Guide utilisateur
|
||||
* le Guide utilisateur avancé
|
||||
* la documentation SQLModel
|
||||
* la documentation de l’API
|
||||
* la documentation automatique
|
||||
|
||||
* Data Science
|
||||
* Apprentissage profond
|
||||
* Apprentissage automatique
|
||||
* Injection de dépendances
|
||||
* authentification HTTP Basic
|
||||
* HTTP Digest
|
||||
* format ISO
|
||||
* la norme JSON Schema
|
||||
* le schéma JSON
|
||||
* la définition de schéma
|
||||
* Flux Password
|
||||
* Mobile
|
||||
|
||||
* déprécié
|
||||
* conçu
|
||||
* invalide
|
||||
* à la volée
|
||||
* standard
|
||||
* par défaut
|
||||
* sensible à la casse
|
||||
* insensible à la casse
|
||||
|
||||
* servir l’application
|
||||
* servir la page
|
||||
|
||||
* l’app
|
||||
* l’application
|
||||
|
||||
* la requête
|
||||
* la réponse
|
||||
* la réponse d’erreur
|
||||
|
||||
* le chemin d’accès
|
||||
* le décorateur de chemin d’accès
|
||||
* la fonction de chemin d’accès
|
||||
|
||||
* le corps
|
||||
* le corps de la requête
|
||||
* le corps de la réponse
|
||||
* le corps JSON
|
||||
* le corps de formulaire
|
||||
* le corps de fichier
|
||||
* le corps de la fonction
|
||||
|
||||
* le paramètre
|
||||
* le paramètre de corps
|
||||
* le paramètre de chemin
|
||||
* le paramètre de requête
|
||||
* le paramètre de cookie
|
||||
* le paramètre d’en-tête
|
||||
* le paramètre de formulaire
|
||||
* le paramètre de fonction
|
||||
|
||||
* l’événement
|
||||
* l’événement de démarrage
|
||||
* le démarrage du serveur
|
||||
* l’événement d’arrêt
|
||||
* l’événement de cycle de vie
|
||||
|
||||
* le gestionnaire
|
||||
* le gestionnaire d’événements
|
||||
* le gestionnaire d’exceptions
|
||||
* gérer
|
||||
|
||||
* le modèle
|
||||
* le modèle Pydantic
|
||||
* le modèle de données
|
||||
* le modèle de base de données
|
||||
* le modèle de formulaire
|
||||
* l’objet modèle
|
||||
|
||||
* la classe
|
||||
* la classe de base
|
||||
* la classe parente
|
||||
* la sous-classe
|
||||
* la classe enfant
|
||||
* la classe sœur
|
||||
* la méthode de classe
|
||||
|
||||
* l’en-tête
|
||||
* les en-têtes
|
||||
* l’en-tête d’autorisation
|
||||
* l’en-tête `Authorization`
|
||||
* l’en-tête transféré
|
||||
|
||||
* le système d’injection de dépendances
|
||||
* la dépendance
|
||||
* l’élément dépendable
|
||||
* le dépendant
|
||||
|
||||
* lié aux E/S
|
||||
* lié au processeur
|
||||
* concurrence
|
||||
* parallélisme
|
||||
* multi-traitement
|
||||
|
||||
* la variable d’env
|
||||
* la variable d’environnement
|
||||
* le `PATH`
|
||||
* la variable `PATH`
|
||||
|
||||
* l’authentification
|
||||
* le fournisseur d’authentification
|
||||
* l’autorisation
|
||||
* le formulaire d’autorisation
|
||||
* le fournisseur d’autorisation
|
||||
* l’utilisateur s’authentifie
|
||||
* le système authentifie l’utilisateur
|
||||
|
||||
* la CLI
|
||||
* l’interface en ligne de commande
|
||||
|
||||
* le serveur
|
||||
* le client
|
||||
|
||||
* le fournisseur cloud
|
||||
* le service cloud
|
||||
|
||||
* le développement
|
||||
* les étapes de développement
|
||||
|
||||
* le dict
|
||||
* le dictionnaire
|
||||
* l’énumération
|
||||
* l’enum
|
||||
* le membre d’enum
|
||||
|
||||
* l’encodeur
|
||||
* le décodeur
|
||||
* encoder
|
||||
* décoder
|
||||
|
||||
* l’exception
|
||||
* lever
|
||||
|
||||
* l’expression
|
||||
* l’instruction
|
||||
|
||||
* le frontend
|
||||
* le backend
|
||||
|
||||
* la discussion GitHub
|
||||
* le ticket GitHub
|
||||
|
||||
* la performance
|
||||
* l’optimisation des performances
|
||||
|
||||
* le type de retour
|
||||
* la valeur de retour
|
||||
|
||||
* la sécurité
|
||||
* le schéma de sécurité
|
||||
|
||||
* la tâche
|
||||
* la tâche d’arrière-plan
|
||||
* la fonction de tâche
|
||||
|
||||
* le template
|
||||
* le moteur de templates
|
||||
|
||||
* l’annotation de type
|
||||
* l’annotation de type
|
||||
|
||||
* le worker du serveur
|
||||
* le worker Uvicorn
|
||||
* le Worker Gunicorn
|
||||
* le processus worker
|
||||
* la classe de worker
|
||||
* la charge de travail
|
||||
|
||||
* le déploiement
|
||||
* déployer
|
||||
|
||||
* le SDK
|
||||
* le kit de développement logiciel
|
||||
|
||||
* le `APIRouter`
|
||||
* le `requirements.txt`
|
||||
* le jeton Bearer
|
||||
* le changement majeur incompatible
|
||||
* le bogue
|
||||
* le bouton
|
||||
* l’appelable
|
||||
* le code
|
||||
* le commit
|
||||
* le gestionnaire de contexte
|
||||
* la coroutine
|
||||
* la session de base de données
|
||||
* le disque
|
||||
* le domaine
|
||||
* le moteur
|
||||
* le faux X
|
||||
* la méthode HTTP GET
|
||||
* l’élément
|
||||
* la bibliothèque
|
||||
* le cycle de vie
|
||||
* le verrou
|
||||
* le middleware
|
||||
* l’application mobile
|
||||
* le module
|
||||
* le montage
|
||||
* le réseau
|
||||
* l’origine
|
||||
* la surcharge
|
||||
* le payload
|
||||
* le processeur
|
||||
* la propriété
|
||||
* le proxy
|
||||
* la pull request
|
||||
* la requête
|
||||
* la RAM
|
||||
* la machine distante
|
||||
* le code d’état
|
||||
* la chaîne
|
||||
* l’étiquette
|
||||
* le framework Web
|
||||
* le joker
|
||||
* retourner
|
||||
* valider
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Il s’agit d’une liste non exhaustive et non normative de termes (principalement) techniques présents dans les documents. Elle peut aider le concepteur de l’invite à déterminer pour quels termes le LLM a besoin d’un coup de main. Par exemple, lorsqu’il continue de remplacer une bonne traduction par une traduction sous-optimale. Ou lorsqu’il a des difficultés à conjuguer/décliner un terme dans votre langue.
|
||||
|
||||
Voir par exemple la section `### List of English terms and their preferred German translations` dans `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# À propos { #about }
|
||||
|
||||
À propos de FastAPI, de sa conception, de ses sources d'inspiration et plus encore. 🤓
|
||||
|
|
@ -8,7 +8,7 @@ Si vous débutez avec **FastAPI**, vous n'en aurez peut-être pas besoin.
|
|||
|
||||
///
|
||||
|
||||
Vous pouvez déclarer des réponses supplémentaires, avec des codes HTTP, des types de médias, des descriptions, etc.
|
||||
Vous pouvez déclarer des réponses supplémentaires, avec des codes d'état supplémentaires, des types de médias, des descriptions, etc.
|
||||
|
||||
Ces réponses supplémentaires seront incluses dans le schéma OpenAPI, elles apparaîtront donc également dans la documentation de l'API.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Chacun de ces `dict` de réponse peut avoir une clé `model`, contenant un modè
|
|||
|
||||
Par exemple, pour déclarer une autre réponse avec un code HTTP `404` et un modèle Pydantic `Message`, vous pouvez écrire :
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ Par exemple, vous pouvez déclarer une réponse avec un code HTTP `404` qui util
|
|||
|
||||
Et une réponse avec un code HTTP `200` qui utilise votre `response_model`, mais inclut un `example` personnalisé :
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
|
||||
|
||||
Tout sera combiné et inclus dans votre OpenAPI, et affiché dans la documentation de l'API :
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,6 @@ Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` s
|
|||
|
||||
## Documents OpenAPI et API { #openapi-and-api-docs }
|
||||
|
||||
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (la documentation de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
|
||||
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (les documents de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
|
||||
|
||||
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires](additional-responses.md){.internal-link target=_blank}.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
# Dépendances avancées { #advanced-dependencies }
|
||||
|
||||
## Dépendances paramétrées { #parameterized-dependencies }
|
||||
|
||||
Toutes les dépendances que nous avons vues étaient des fonctions ou des classes fixes.
|
||||
|
||||
Mais il peut y avoir des cas où vous souhaitez pouvoir définir des paramètres sur la dépendance, sans devoir déclarer de nombreuses fonctions ou classes différentes.
|
||||
|
||||
Imaginons que nous voulions avoir une dépendance qui vérifie si le paramètre de requête `q` contient un contenu fixe.
|
||||
|
||||
Mais nous voulons pouvoir paramétrer ce contenu fixe.
|
||||
|
||||
## Une instance « callable » { #a-callable-instance }
|
||||
|
||||
En Python, il existe un moyen de rendre une instance de classe « callable ».
|
||||
|
||||
Pas la classe elle‑même (qui est déjà un callable), mais une instance de cette classe.
|
||||
|
||||
Pour cela, nous déclarons une méthode `__call__` :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
Dans ce cas, ce `__call__` est ce que **FastAPI** utilisera pour détecter des paramètres supplémentaires et des sous‑dépendances, et c’est ce qui sera appelé pour transmettre ensuite une valeur au paramètre dans votre *fonction de chemin d'accès*.
|
||||
|
||||
## Paramétrer l'instance { #parameterize-the-instance }
|
||||
|
||||
Et maintenant, nous pouvons utiliser `__init__` pour déclarer les paramètres de l’instance, que nous utiliserons pour « paramétrer » la dépendance :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
Dans ce cas, **FastAPI** n’accèdera pas à `__init__` et ne s’en souciera pas ; nous l’utiliserons directement dans notre code.
|
||||
|
||||
## Créer une instance { #create-an-instance }
|
||||
|
||||
Nous pouvons créer une instance de cette classe avec :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
Et de cette façon, nous pouvons « paramétrer » notre dépendance, qui contient maintenant « bar », en tant qu’attribut `checker.fixed_content`.
|
||||
|
||||
## Utiliser l'instance comme dépendance { #use-the-instance-as-a-dependency }
|
||||
|
||||
Ensuite, nous pourrions utiliser ce `checker` dans un `Depends(checker)`, au lieu de `Depends(FixedContentQueryChecker)`, car la dépendance est l’instance, `checker`, et non la classe elle‑même.
|
||||
|
||||
Et lors de la résolution de la dépendance, **FastAPI** appellera ce `checker` comme ceci :
|
||||
|
||||
```Python
|
||||
checker(q="somequery")
|
||||
```
|
||||
|
||||
... et passera ce que cela renvoie comme valeur de la dépendance à notre *fonction de chemin d'accès*, en tant que paramètre `fixed_content_included` :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Tout cela peut sembler artificiel. Et il n’est peut‑être pas encore très clair en quoi c’est utile.
|
||||
|
||||
Ces exemples sont volontairement simples, mais ils montrent comment tout cela fonctionne.
|
||||
|
||||
Dans les chapitres sur la sécurité, il existe des fonctions utilitaires implémentées de la même manière.
|
||||
|
||||
Si vous avez compris tout cela, vous savez déjà comment ces outils utilitaires pour la sécurité fonctionnent en interne.
|
||||
|
||||
///
|
||||
|
||||
## Dépendances avec `yield`, `HTTPException`, `except` et tâches d'arrière‑plan { #dependencies-with-yield-httpexception-except-and-background-tasks }
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Vous n’avez très probablement pas besoin de ces détails techniques.
|
||||
|
||||
Ces détails sont utiles principalement si vous aviez une application FastAPI antérieure à la version 0.121.0 et que vous rencontrez des problèmes avec des dépendances utilisant `yield`.
|
||||
|
||||
///
|
||||
|
||||
Les dépendances avec `yield` ont évolué au fil du temps pour couvrir différents cas d’utilisation et corriger certains problèmes ; voici un résumé de ce qui a changé.
|
||||
|
||||
### Dépendances avec `yield` et `scope` { #dependencies-with-yield-and-scope }
|
||||
|
||||
Dans la version 0.121.0, **FastAPI** a ajouté la prise en charge de `Depends(scope="function")` pour les dépendances avec `yield`.
|
||||
|
||||
Avec `Depends(scope="function")`, le code d’arrêt après `yield` s’exécute immédiatement après la fin de la *fonction de chemin d'accès*, avant que la réponse ne soit renvoyée au client.
|
||||
|
||||
Et lorsque vous utilisez `Depends(scope="request")` (valeur par défaut), le code d’arrêt après `yield` s’exécute après l’envoi de la réponse.
|
||||
|
||||
Vous pouvez en lire davantage dans les documents pour [Dépendances avec `yield` - Sortie anticipée et `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
|
||||
|
||||
### Dépendances avec `yield` et `StreamingResponse`, Détails techniques { #dependencies-with-yield-and-streamingresponse-technical-details }
|
||||
|
||||
Avant FastAPI 0.118.0, si vous utilisiez une dépendance avec `yield`, elle exécutait le code d’arrêt après que la *fonction de chemin d'accès* a retourné, mais juste avant d’envoyer la réponse.
|
||||
|
||||
L’objectif était d’éviter de conserver des ressources plus longtemps que nécessaire pendant que la réponse transitait sur le réseau.
|
||||
|
||||
Ce changement impliquait aussi que si vous retourniez une `StreamingResponse`, le code d’arrêt de la dépendance avec `yield` aurait déjà été exécuté.
|
||||
|
||||
Par exemple, si vous aviez une session de base de données dans une dépendance avec `yield`, la `StreamingResponse` ne pourrait pas utiliser cette session pendant le streaming des données, car la session aurait déjà été fermée dans le code d’arrêt après `yield`.
|
||||
|
||||
Ce comportement a été annulé en 0.118.0, afin que le code d’arrêt après `yield` s’exécute après l’envoi de la réponse.
|
||||
|
||||
/// info
|
||||
|
||||
Comme vous le verrez ci‑dessous, c’est très similaire au comportement avant la version 0.106.0, mais avec plusieurs améliorations et corrections de bogues pour des cas limites.
|
||||
|
||||
///
|
||||
|
||||
#### Cas d’utilisation avec sortie anticipée du code { #use-cases-with-early-exit-code }
|
||||
|
||||
Il existe certains cas d’utilisation avec des conditions spécifiques qui pourraient bénéficier de l’ancien comportement, où le code d’arrêt des dépendances avec `yield` s’exécute avant l’envoi de la réponse.
|
||||
|
||||
Par exemple, imaginez que vous ayez du code qui utilise une session de base de données dans une dépendance avec `yield` uniquement pour vérifier un utilisateur, mais que la session de base de données ne soit plus jamais utilisée dans la *fonction de chemin d'accès*, seulement dans la dépendance, et que la réponse mette longtemps à être envoyée, comme une `StreamingResponse` qui envoie les données lentement mais qui, pour une raison quelconque, n’utilise pas la base de données.
|
||||
|
||||
Dans ce cas, la session de base de données serait conservée jusqu’à la fin de l’envoi de la réponse, mais si vous ne l’utilisez pas, il ne serait pas nécessaire de la conserver.
|
||||
|
||||
Voici à quoi cela pourrait ressembler :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
|
||||
|
||||
Le code d’arrêt, la fermeture automatique de la `Session` dans :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
|
||||
|
||||
... serait exécuté après que la réponse a fini d’envoyer les données lentes :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
|
||||
|
||||
Mais comme `generate_stream()` n’utilise pas la session de base de données, il n’est pas vraiment nécessaire de garder la session ouverte pendant l’envoi de la réponse.
|
||||
|
||||
Si vous avez ce cas d’utilisation spécifique avec SQLModel (ou SQLAlchemy), vous pouvez fermer explicitement la session dès que vous n’en avez plus besoin :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
|
||||
|
||||
De cette manière, la session libérera la connexion à la base de données, afin que d’autres requêtes puissent l’utiliser.
|
||||
|
||||
Si vous avez un autre cas d’utilisation qui nécessite une sortie anticipée depuis une dépendance avec `yield`, veuillez créer une <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">Question de discussion GitHub</a> avec votre cas spécifique et pourquoi vous bénéficieriez d’une fermeture anticipée pour les dépendances avec `yield`.
|
||||
|
||||
S’il existe des cas d’utilisation convaincants pour une fermeture anticipée dans les dépendances avec `yield`, j’envisagerai d’ajouter une nouvelle façon d’y opter.
|
||||
|
||||
### Dépendances avec `yield` et `except`, Détails techniques { #dependencies-with-yield-and-except-technical-details }
|
||||
|
||||
Avant FastAPI 0.110.0, si vous utilisiez une dépendance avec `yield`, puis capturiez une exception avec `except` dans cette dépendance, et que vous ne relanciez pas l’exception, l’exception était automatiquement levée/transmise à tout gestionnaire d’exceptions ou au gestionnaire d’erreur interne du serveur.
|
||||
|
||||
Cela a été modifié dans la version 0.110.0 pour corriger une consommation de mémoire non gérée due aux exceptions transmises sans gestionnaire (erreurs internes du serveur), et pour rendre le comportement cohérent avec celui du code Python classique.
|
||||
|
||||
### Tâches d'arrière‑plan et dépendances avec `yield`, Détails techniques { #background-tasks-and-dependencies-with-yield-technical-details }
|
||||
|
||||
Avant FastAPI 0.106.0, lever des exceptions après `yield` n’était pas possible, le code d’arrêt dans les dépendances avec `yield` s’exécutait après l’envoi de la réponse, donc les [Gestionnaires d'exceptions](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} avaient déjà été exécutés.
|
||||
|
||||
Cela avait été conçu ainsi principalement pour permettre d’utiliser les mêmes objets « générés par yield » par les dépendances à l’intérieur de tâches d’arrière‑plan, car le code d’arrêt s’exécutait après la fin des tâches d’arrière‑plan.
|
||||
|
||||
Cela a été modifié dans FastAPI 0.106.0 afin de ne pas conserver des ressources pendant l’attente de la transmission de la réponse sur le réseau.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
De plus, une tâche d’arrière‑plan est normalement un ensemble de logique indépendant qui devrait être géré séparément, avec ses propres ressources (par ex. sa propre connexion à la base de données).
|
||||
|
||||
Ainsi, vous aurez probablement un code plus propre.
|
||||
|
||||
///
|
||||
|
||||
Si vous comptiez sur ce comportement, vous devez désormais créer les ressources pour les tâches d’arrière‑plan à l’intérieur de la tâche elle‑même, et n’utiliser en interne que des données qui ne dépendent pas des ressources des dépendances avec `yield`.
|
||||
|
||||
Par exemple, au lieu d’utiliser la même session de base de données, vous créeriez une nouvelle session de base de données à l’intérieur de la tâche d’arrière‑plan, et vous obtiendriez les objets depuis la base de données en utilisant cette nouvelle session. Puis, au lieu de passer l’objet obtenu depuis la base de données en paramètre à la fonction de tâche d’arrière‑plan, vous passeriez l’identifiant (ID) de cet objet et vous obtiendriez à nouveau l’objet à l’intérieur de la fonction de la tâche d’arrière‑plan.
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Types Python avancés { #advanced-python-types }
|
||||
|
||||
Voici quelques idées supplémentaires qui peuvent être utiles lorsque vous travaillez avec les types Python.
|
||||
|
||||
## Utiliser `Union` ou `Optional` { #using-union-or-optional }
|
||||
|
||||
Si votre code ne peut pas utiliser `|` pour une raison quelconque, par exemple si ce n'est pas dans une annotation de type mais dans quelque chose comme `response_model=`, au lieu d'utiliser la barre verticale (`|`) vous pouvez utiliser `Union` de `typing`.
|
||||
|
||||
Par exemple, vous pourriez déclarer que quelque chose peut être un `str` ou `None` :
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` propose également un raccourci pour déclarer que quelque chose peut être `None`, avec `Optional`.
|
||||
|
||||
Voici un conseil issu de mon point de vue très subjectif :
|
||||
|
||||
- 🚨 Évitez d'utiliser `Optional[SomeType]`
|
||||
- À la place ✨ **utilisez `Union[SomeType, None]`** ✨.
|
||||
|
||||
Les deux sont équivalents et, en interne, identiques, mais je recommande `Union` plutôt que `Optional` parce que le mot « optional » semble impliquer que la valeur est facultative, alors qu'il signifie en réalité « elle peut être `None` », même si elle n'est pas facultative et reste requise.
|
||||
|
||||
Je pense que `Union[SomeType, None]` est plus explicite quant à sa signification.
|
||||
|
||||
Il ne s'agit que des mots et des noms. Mais ces mots peuvent influencer la manière dont vous et vos coéquipiers pensez au code.
|
||||
|
||||
À titre d'exemple, prenons cette fonction :
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Le paramètre `name` est défini comme `Optional[str]`, mais il n'est pas facultatif, vous ne pouvez pas appeler la fonction sans le paramètre :
|
||||
|
||||
```Python
|
||||
say_hi() # Oh non, cela lève une erreur ! 😱
|
||||
```
|
||||
|
||||
Le paramètre `name` est toujours requis (pas facultatif) car il n'a pas de valeur par défaut. En revanche, `name` accepte `None` comme valeur :
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Ceci fonctionne, None est valide 🎉
|
||||
```
|
||||
|
||||
La bonne nouvelle, c'est que, dans la plupart des cas, vous pourrez simplement utiliser `|` pour définir des unions de types :
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Ainsi, normalement, vous n'avez pas à vous préoccuper de noms comme `Optional` et `Union`. 😎
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# Tests asynchrones { #async-tests }
|
||||
|
||||
Vous avez déjà vu comment tester vos applications **FastAPI** en utilisant le `TestClient` fourni. Jusqu'à présent, vous n'avez vu que comment écrire des tests synchrones, sans utiliser de fonctions `async`.
|
||||
|
||||
Pouvoir utiliser des fonctions asynchrones dans vos tests peut être utile, par exemple lorsque vous interrogez votre base de données de manière asynchrone. Imaginez que vous vouliez tester l'envoi de requêtes à votre application FastAPI puis vérifier que votre backend a bien écrit les bonnes données dans la base, tout en utilisant une bibliothèque de base de données asynchrone.
|
||||
|
||||
Voyons comment procéder.
|
||||
|
||||
## pytest.mark.anyio { #pytest-mark-anyio }
|
||||
|
||||
Si nous voulons appeler des fonctions asynchrones dans nos tests, nos fonctions de test doivent être asynchrones. AnyIO fournit un plug-in pratique qui nous permet d'indiquer que certaines fonctions de test doivent être appelées de manière asynchrone.
|
||||
|
||||
## HTTPX { #httpx }
|
||||
|
||||
Même si votre application **FastAPI** utilise des fonctions `def` normales au lieu de `async def`, c'est toujours une application `async` en interne.
|
||||
|
||||
Le `TestClient` fait un peu de magie pour appeler l'application FastAPI asynchrone depuis vos fonctions de test `def` normales, en utilisant pytest standard. Mais cette magie ne fonctionne plus lorsque nous l'utilisons dans des fonctions asynchrones. En exécutant nos tests de manière asynchrone, nous ne pouvons plus utiliser le `TestClient` dans nos fonctions de test.
|
||||
|
||||
Le `TestClient` est basé sur <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> et, heureusement, nous pouvons l'utiliser directement pour tester l'API.
|
||||
|
||||
## Exemple { #example }
|
||||
|
||||
Pour un exemple simple, considérons une structure de fichiers similaire à celle décrite dans [Applications plus grandes](../tutorial/bigger-applications.md){.internal-link target=_blank} et [Tests](../tutorial/testing.md){.internal-link target=_blank} :
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Le fichier `main.py` contiendrait :
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
Le fichier `test_main.py` contiendrait les tests pour `main.py`, il pourrait maintenant ressembler à ceci :
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## Exécuter { #run-it }
|
||||
|
||||
Vous pouvez lancer vos tests comme d'habitude via :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pytest
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## En détail { #in-detail }
|
||||
|
||||
Le marqueur `@pytest.mark.anyio` indique à pytest que cette fonction de test doit être appelée de manière asynchrone :
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Notez que la fonction de test est maintenant `async def` au lieu de simplement `def` comme auparavant avec le `TestClient`.
|
||||
|
||||
///
|
||||
|
||||
Nous pouvons ensuite créer un `AsyncClient` avec l'application et lui envoyer des requêtes asynchrones en utilisant `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
C'est l'équivalent de :
|
||||
|
||||
```Python
|
||||
response = client.get('/')
|
||||
```
|
||||
|
||||
... que nous utilisions pour faire nos requêtes avec le `TestClient`.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Notez que nous utilisons async/await avec le nouveau `AsyncClient` — la requête est asynchrone.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Si votre application s'appuie sur des événements de cycle de vie (lifespan), le `AsyncClient` ne déclenchera pas ces événements. Pour vous assurer qu'ils sont déclenchés, utilisez `LifespanManager` depuis <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>.
|
||||
|
||||
///
|
||||
|
||||
## Autres appels de fonctions asynchrones { #other-asynchronous-function-calls }
|
||||
|
||||
Comme la fonction de test est désormais asynchrone, vous pouvez également appeler (et `await`) d'autres fonctions `async` en plus d'envoyer des requêtes à votre application FastAPI dans vos tests, exactement comme vous le feriez ailleurs dans votre code.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous rencontrez une erreur `RuntimeError: Task attached to a different loop` lors de l'intégration d'appels de fonctions asynchrones dans vos tests (par exemple en utilisant <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MotorClient de MongoDB</a>), n'oubliez pas d'instancier les objets qui ont besoin d'une boucle d'événements uniquement dans des fonctions async, par exemple dans un callback `@app.on_event("startup")`.
|
||||
|
||||
///
|
||||
|
|
@ -0,0 +1,466 @@
|
|||
# Être derrière un proxy { #behind-a-proxy }
|
||||
|
||||
Dans de nombreuses situations, vous utiliserez un **proxy** comme Traefik ou Nginx devant votre application FastAPI.
|
||||
|
||||
Ces proxies peuvent gérer les certificats HTTPS et d'autres aspects.
|
||||
|
||||
## En-têtes transférés par le proxy { #proxy-forwarded-headers }
|
||||
|
||||
Un **proxy** placé devant votre application définit normalement certains en-têtes à la volée avant d'envoyer les requêtes à votre **serveur**, afin d'indiquer au serveur que la requête a été **transférée** par le proxy, en lui donnant l'URL d'origine (publique), y compris le domaine, le fait qu'elle utilise HTTPS, etc.
|
||||
|
||||
Le programme **serveur** (par exemple **Uvicorn** via **FastAPI CLI**) est capable d'interpréter ces en‑têtes, puis de transmettre ces informations à votre application.
|
||||
|
||||
Mais, par sécurité, comme le serveur ne sait pas qu'il se trouve derrière un proxy de confiance, il n'interprétera pas ces en‑têtes.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
Les en-têtes du proxy sont :
|
||||
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
|
||||
|
||||
///
|
||||
|
||||
### Activer les en-têtes transférés par le proxy { #enable-proxy-forwarded-headers }
|
||||
|
||||
Vous pouvez démarrer FastAPI CLI avec l'option de CLI `--forwarded-allow-ips` et fournir les adresses IP à considérer comme fiables pour lire ces en‑têtes transférés.
|
||||
|
||||
Si vous la définissez à `--forwarded-allow-ips="*"`, elle fera confiance à toutes les IP entrantes.
|
||||
|
||||
Si votre **serveur** est derrière un **proxy** de confiance et que seul le proxy lui parle, cela fera accepter l'IP de ce **proxy**, quelle qu'elle soit.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Redirections avec HTTPS { #redirects-with-https }
|
||||
|
||||
Par exemple, disons que vous définissez un *chemin d'accès* `/items/` :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
Si le client essaie d'aller à `/items`, par défaut, il sera redirigé vers `/items/`.
|
||||
|
||||
Mais avant de définir l'option de CLI `--forwarded-allow-ips`, il pourrait rediriger vers `http://localhost:8000/items/`.
|
||||
|
||||
Mais peut‑être que votre application est hébergée à `https://mysuperapp.com`, et la redirection devrait être vers `https://mysuperapp.com/items/`.
|
||||
|
||||
En définissant `--proxy-headers`, FastAPI pourra désormais rediriger vers l'emplacement correct. 😎
|
||||
|
||||
```
|
||||
https://mysuperapp.com/items/
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous voulez en savoir plus sur HTTPS, consultez le guide [À propos de HTTPS](../deployment/https.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Comment fonctionnent les en‑têtes transférés par le proxy { #how-proxy-forwarded-headers-work }
|
||||
|
||||
Voici une représentation visuelle de la façon dont le **proxy** ajoute des en‑têtes transférés entre le client et le **serveur d'application** :
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Proxy as Proxy/Load Balancer
|
||||
participant Server as FastAPI Server
|
||||
|
||||
Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Proxy: Proxy adds forwarded headers
|
||||
|
||||
Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Server: Server interprets headers<br/>(if --forwarded-allow-ips is set)
|
||||
|
||||
Server->>Proxy: HTTP Response<br/>with correct HTTPS URLs
|
||||
|
||||
Proxy->>Client: HTTPS Response
|
||||
```
|
||||
|
||||
Le **proxy** intercepte la requête client d'origine et ajoute les en-têtes spéciaux *forwarded* (`X-Forwarded-*`) avant de transmettre la requête au **serveur d'application**.
|
||||
|
||||
Ces en‑têtes conservent des informations sur la requête d'origine qui seraient autrement perdues :
|
||||
|
||||
* **X-Forwarded-For** : l'adresse IP du client d'origine
|
||||
* **X-Forwarded-Proto** : le protocole d'origine (`https`)
|
||||
* **X-Forwarded-Host** : l'hôte d'origine (`mysuperapp.com`)
|
||||
|
||||
Lorsque **FastAPI CLI** est configurée avec `--forwarded-allow-ips`, elle fait confiance à ces en‑têtes et les utilise, par exemple pour générer les bonnes URL dans les redirections.
|
||||
|
||||
## Proxy avec un préfixe de chemin supprimé { #proxy-with-a-stripped-path-prefix }
|
||||
|
||||
Vous pouvez avoir un proxy qui ajoute un préfixe de chemin à votre application.
|
||||
|
||||
Dans ces cas, vous pouvez utiliser `root_path` pour configurer votre application.
|
||||
|
||||
Le `root_path` est un mécanisme fourni par la spécification ASGI (sur laquelle FastAPI est construit, via Starlette).
|
||||
|
||||
Le `root_path` est utilisé pour gérer ces cas spécifiques.
|
||||
|
||||
Et il est également utilisé en interne lors du montage de sous‑applications.
|
||||
|
||||
Avoir un proxy avec un préfixe de chemin supprimé, dans ce cas, signifie que vous pourriez déclarer un chemin à `/app` dans votre code, mais ensuite, vous ajoutez une couche au‑dessus (le proxy) qui place votre application **FastAPI** sous un chemin comme `/api/v1`.
|
||||
|
||||
Dans ce cas, le chemin original `/app` serait en réalité servi à `/api/v1/app`.
|
||||
|
||||
Même si tout votre code est écrit en supposant qu'il n'y a que `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
Et le proxy **« stripping »** le **préfixe de chemin** à la volée avant de transmettre la requête au serveur de l'application (probablement Uvicorn via FastAPI CLI), en gardant votre application convaincue qu'elle est servie à `/app`, afin que vous n'ayez pas à mettre à jour tout votre code pour inclure le préfixe `/api/v1`.
|
||||
|
||||
Jusqu'ici, tout fonctionnerait normalement.
|
||||
|
||||
Mais ensuite, lorsque vous ouvrez l'interface de documentation intégrée (le frontend), elle s'attendra à obtenir le schéma OpenAPI à `/openapi.json`, au lieu de `/api/v1/openapi.json`.
|
||||
|
||||
Ainsi, le frontend (qui s'exécute dans le navigateur) essaiera d'atteindre `/openapi.json` et ne pourra pas obtenir le schéma OpenAPI.
|
||||
|
||||
Parce que nous avons un proxy avec un préfixe de chemin `/api/v1` pour notre application, le frontend doit récupérer le schéma OpenAPI à `/api/v1/openapi.json`.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
|
||||
browser("Browser")
|
||||
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
|
||||
server["Server on http://127.0.0.1:8000/app"]
|
||||
|
||||
browser --> proxy
|
||||
proxy --> server
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
L'IP `0.0.0.0` est couramment utilisée pour signifier que le programme écoute sur toutes les IP disponibles de cette machine/serveur.
|
||||
|
||||
///
|
||||
|
||||
L'interface de documents doit également indiquer dans le schéma OpenAPI que ce `server` d'API se trouve à `/api/v1` (derrière le proxy). Par exemple :
|
||||
|
||||
```JSON hl_lines="4-8"
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
// Plus d'éléments ici
|
||||
"servers": [
|
||||
{
|
||||
"url": "/api/v1"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
// Plus d'éléments ici
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dans cet exemple, le « Proxy » pourrait être quelque chose comme **Traefik**. Et le serveur serait quelque chose comme FastAPI CLI avec **Uvicorn**, exécutant votre application FastAPI.
|
||||
|
||||
### Fournir le `root_path` { #providing-the-root-path }
|
||||
|
||||
Pour y parvenir, vous pouvez utiliser l'option de ligne de commande `--root-path` comme suit :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Si vous utilisez Hypercorn, il dispose également de l'option `--root-path`.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
La spécification ASGI définit un `root_path` pour ce cas d'usage.
|
||||
|
||||
Et l'option de ligne de commande `--root-path` fournit ce `root_path`.
|
||||
|
||||
///
|
||||
|
||||
### Vérifier le `root_path` actuel { #checking-the-current-root-path }
|
||||
|
||||
Vous pouvez obtenir le `root_path` actuel utilisé par votre application pour chaque requête, il fait partie du dictionnaire `scope` (qui fait partie de la spécification ASGI).
|
||||
|
||||
Ici, nous l'incluons dans le message uniquement à des fins de démonstration.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Ensuite, si vous démarrez Uvicorn avec :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
La réponse sera semblable à :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
### Définir le `root_path` dans l'application FastAPI { #setting-the-root-path-in-the-fastapi-app }
|
||||
|
||||
Autrement, si vous n'avez pas la possibilité de fournir une option de ligne de commande comme `--root-path` ou équivalent, vous pouvez définir le paramètre `root_path` lors de la création de votre application FastAPI :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Passer le `root_path` à `FastAPI` équivaut à passer l'option de ligne de commande `--root-path` à Uvicorn ou Hypercorn.
|
||||
|
||||
### À propos de `root_path` { #about-root-path }
|
||||
|
||||
Gardez à l'esprit que le serveur (Uvicorn) n'utilisera ce `root_path` que pour le transmettre à l'application.
|
||||
|
||||
Mais si vous allez avec votre navigateur sur <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, vous verrez la réponse normale :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
Donc, il ne s'attendra pas à être accessible à `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
Uvicorn s'attendra à ce que le proxy accède à Uvicorn sur `http://127.0.0.1:8000/app`, et ce sera ensuite la responsabilité du proxy d'ajouter le préfixe supplémentaire `/api/v1` au‑dessus.
|
||||
|
||||
## À propos des proxies avec un préfixe de chemin supprimé { #about-proxies-with-a-stripped-path-prefix }
|
||||
|
||||
Gardez à l'esprit qu'un proxy avec préfixe de chemin supprimé n'est qu'une des façons de le configurer.
|
||||
|
||||
Dans de nombreux cas, la valeur par défaut sera probablement que le proxy n'a pas de préfixe de chemin supprimé.
|
||||
|
||||
Dans un cas comme celui‑ci (sans préfixe de chemin supprimé), le proxy écoutera sur quelque chose comme `https://myawesomeapp.com`, puis si le navigateur va sur `https://myawesomeapp.com/api/v1/app` et que votre serveur (par ex. Uvicorn) écoute sur `http://127.0.0.1:8000`, le proxy (sans préfixe de chemin supprimé) accédera à Uvicorn au même chemin : `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
## Tester localement avec Traefik { #testing-locally-with-traefik }
|
||||
|
||||
Vous pouvez facilement faire l'expérience en local avec un préfixe de chemin supprimé en utilisant <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
|
||||
|
||||
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Téléchargez Traefik</a> ; c'est un binaire unique, vous pouvez extraire le fichier compressé et l'exécuter directement depuis le terminal.
|
||||
|
||||
Créez ensuite un fichier `traefik.toml` avec :
|
||||
|
||||
```TOML hl_lines="3"
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":9999"
|
||||
|
||||
[providers]
|
||||
[providers.file]
|
||||
filename = "routes.toml"
|
||||
```
|
||||
|
||||
Cela indique à Traefik d'écouter sur le port 9999 et d'utiliser un autre fichier `routes.toml`.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Nous utilisons le port 9999 au lieu du port HTTP standard 80 afin que vous n'ayez pas à l'exécuter avec des privilèges administrateur (`sudo`).
|
||||
|
||||
///
|
||||
|
||||
Créez maintenant cet autre fichier `routes.toml` :
|
||||
|
||||
```TOML hl_lines="5 12 20"
|
||||
[http]
|
||||
[http.middlewares]
|
||||
|
||||
[http.middlewares.api-stripprefix.stripPrefix]
|
||||
prefixes = ["/api/v1"]
|
||||
|
||||
[http.routers]
|
||||
|
||||
[http.routers.app-http]
|
||||
entryPoints = ["http"]
|
||||
service = "app"
|
||||
rule = "PathPrefix(`/api/v1`)"
|
||||
middlewares = ["api-stripprefix"]
|
||||
|
||||
[http.services]
|
||||
|
||||
[http.services.app]
|
||||
[http.services.app.loadBalancer]
|
||||
[[http.services.app.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:8000"
|
||||
```
|
||||
|
||||
Ce fichier configure Traefik pour utiliser le préfixe de chemin `/api/v1`.
|
||||
|
||||
Puis Traefik redirigera ses requêtes vers votre Uvicorn tournant sur `http://127.0.0.1:8000`.
|
||||
|
||||
Démarrez maintenant Traefik :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ ./traefik --configFile=traefik.toml
|
||||
|
||||
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Et démarrez maintenant votre application, en utilisant l'option `--root-path` :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Vérifier les réponses { #check-the-responses }
|
||||
|
||||
Maintenant, si vous allez à l'URL avec le port pour Uvicorn : <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, vous verrez la réponse normale :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez que même si vous y accédez via `http://127.0.0.1:8000/app`, il affiche le `root_path` de `/api/v1`, repris depuis l'option `--root-path`.
|
||||
|
||||
///
|
||||
|
||||
Et maintenant ouvrez l'URL avec le port pour Traefik, en incluant le préfixe de chemin : <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>.
|
||||
|
||||
Nous obtenons la même réponse :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
mais cette fois à l'URL avec le préfixe fourni par le proxy : `/api/v1`.
|
||||
|
||||
Bien sûr, l'idée ici est que tout le monde accède à l'application via le proxy ; la version avec le préfixe de chemin `/api/v1` est donc la « correcte ».
|
||||
|
||||
Et la version sans préfixe de chemin (`http://127.0.0.1:8000/app`), fournie directement par Uvicorn, serait exclusivement destinée au _proxy_ (Traefik) pour y accéder.
|
||||
|
||||
Cela montre comment le Proxy (Traefik) utilise le préfixe de chemin et comment le serveur (Uvicorn) utilise le `root_path` fourni par l'option `--root-path`.
|
||||
|
||||
### Vérifier l'interface de documentation { #check-the-docs-ui }
|
||||
|
||||
Mais voici la partie intéressante. ✨
|
||||
|
||||
La manière « officielle » d'accéder à l'application serait via le proxy avec le préfixe de chemin que nous avons défini. Donc, comme on s'y attend, si vous essayez l'interface de documentation servie directement par Uvicorn, sans le préfixe de chemin dans l'URL, cela ne fonctionne pas, car elle s'attend à être accédée via le proxy.
|
||||
|
||||
Vous pouvez le vérifier sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> :
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image01.png">
|
||||
|
||||
Mais si nous accédons à l'interface de documents à l'URL « officielle » en utilisant le proxy avec le port `9999`, à `/api/v1/docs`, cela fonctionne correctement ! 🎉
|
||||
|
||||
Vous pouvez le vérifier sur <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> :
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image02.png">
|
||||
|
||||
Exactement comme nous le voulions. ✔️
|
||||
|
||||
C'est parce que FastAPI utilise ce `root_path` pour créer le `server` par défaut dans OpenAPI avec l'URL fournie par `root_path`.
|
||||
|
||||
## Serveurs supplémentaires { #additional-servers }
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Ceci est un cas d'utilisation plus avancé. N'hésitez pas à l'ignorer.
|
||||
|
||||
///
|
||||
|
||||
Par défaut, **FastAPI** créera un `server` dans le schéma OpenAPI avec l'URL correspondant au `root_path`.
|
||||
|
||||
Mais vous pouvez aussi fournir d'autres `servers` alternatifs, par exemple si vous voulez que la même interface de documents interagisse avec un environnement de staging et un environnement de production.
|
||||
|
||||
Si vous passez une liste personnalisée de `servers` et qu'il y a un `root_path` (parce que votre API vit derrière un proxy), **FastAPI** insérera un « server » avec ce `root_path` au début de la liste.
|
||||
|
||||
Par exemple :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
Générera un schéma OpenAPI comme :
|
||||
|
||||
```JSON hl_lines="5-7"
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
// Plus d'éléments ici
|
||||
"servers": [
|
||||
{
|
||||
"url": "/api/v1"
|
||||
},
|
||||
{
|
||||
"url": "https://stag.example.com",
|
||||
"description": "Staging environment"
|
||||
},
|
||||
{
|
||||
"url": "https://prod.example.com",
|
||||
"description": "Production environment"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
// Plus d'éléments ici
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez le serveur généré automatiquement avec une valeur `url` de `/api/v1`, reprise depuis le `root_path`.
|
||||
|
||||
///
|
||||
|
||||
Dans l'interface de documents sur <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>, cela ressemblera à ceci :
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image03.png">
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
L'interface de documents interagit avec le serveur que vous sélectionnez.
|
||||
|
||||
///
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
La propriété `servers` dans la spécification OpenAPI est facultative.
|
||||
|
||||
Si vous ne spécifiez pas le paramètre `servers` et que `root_path` est égal à `/`, la propriété `servers` dans le schéma OpenAPI généré sera entièrement omise par défaut, ce qui équivaut à un seul serveur avec une valeur `url` de `/`.
|
||||
|
||||
///
|
||||
|
||||
### Désactiver le serveur automatique issu de `root_path` { #disable-automatic-server-from-root-path }
|
||||
|
||||
Si vous ne voulez pas que **FastAPI** inclue un serveur automatique utilisant le `root_path`, vous pouvez utiliser le paramètre `root_path_in_servers=False` :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
et il ne l'inclura alors pas dans le schéma OpenAPI.
|
||||
|
||||
## Monter une sous-application { #mounting-a-sub-application }
|
||||
|
||||
Si vous avez besoin de monter une sous‑application (comme décrit dans [Sous‑applications - montages](sub-applications.md){.internal-link target=_blank}) tout en utilisant un proxy avec `root_path`, vous pouvez le faire normalement, comme vous vous y attendez.
|
||||
|
||||
FastAPI utilisera intelligemment le `root_path` en interne, donc cela fonctionnera simplement. ✨
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
# Réponse personnalisée - HTML, flux, fichier, autres { #custom-response-html-stream-file-others }
|
||||
|
||||
Par défaut, **FastAPI** renverra les réponses en utilisant `JSONResponse`.
|
||||
|
||||
Vous pouvez le remplacer en renvoyant directement une `Response` comme expliqué dans [Renvoyer directement une Response](response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
Mais si vous renvoyez directement une `Response` (ou n'importe quelle sous-classe, comme `JSONResponse`), les données ne seront pas automatiquement converties (même si vous déclarez un `response_model`), et la documentation ne sera pas générée automatiquement (par exemple, l'inclusion du « media type » dans l'en-tête HTTP `Content-Type` comme partie de l'OpenAPI généré).
|
||||
|
||||
Vous pouvez aussi déclarer la `Response` que vous voulez utiliser (par ex. toute sous-classe de `Response`), dans le décorateur de chemin d'accès en utilisant le paramètre `response_class`.
|
||||
|
||||
Le contenu que vous renvoyez depuis votre fonction de chemin d'accès sera placé à l'intérieur de cette `Response`.
|
||||
|
||||
Et si cette `Response` a un « media type » JSON (`application/json`), comme c'est le cas avec `JSONResponse` et `UJSONResponse`, les données que vous renvoyez seront automatiquement converties (et filtrées) avec tout `response_model` Pydantic que vous avez déclaré dans le décorateur de chemin d'accès.
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Si vous utilisez une classe de réponse sans « media type », FastAPI s'attendra à ce que votre réponse n'ait pas de contenu ; il ne documentera donc pas le format de la réponse dans les documents OpenAPI générés.
|
||||
|
||||
///
|
||||
|
||||
## Utiliser `ORJSONResponse` { #use-orjsonresponse }
|
||||
|
||||
Par exemple, si vous cherchez à maximiser la performance, vous pouvez installer et utiliser <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> et définir la réponse sur `ORJSONResponse`.
|
||||
|
||||
Importez la classe (sous-classe) `Response` que vous voulez utiliser et déclarez-la dans le décorateur de chemin d'accès.
|
||||
|
||||
Pour de grandes réponses, renvoyer directement une `Response` est bien plus rapide que de renvoyer un dictionnaire.
|
||||
|
||||
Cela vient du fait que, par défaut, FastAPI inspectera chaque élément et s'assurera qu'il est sérialisable en JSON, en utilisant le même [Encodeur compatible JSON](../tutorial/encoder.md){.internal-link target=_blank} expliqué dans le didacticiel. C'est ce qui vous permet de renvoyer des objets arbitraires, par exemple des modèles de base de données.
|
||||
|
||||
Mais si vous êtes certain que le contenu que vous renvoyez est sérialisable en JSON, vous pouvez le passer directement à la classe de réponse et éviter le surcoût supplémentaire qu'aurait FastAPI en faisant passer votre contenu de retour par le `jsonable_encoder` avant de le transmettre à la classe de réponse.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info
|
||||
|
||||
Le paramètre `response_class` sera aussi utilisé pour définir le « media type » de la réponse.
|
||||
|
||||
Dans ce cas, l'en-tête HTTP `Content-Type` sera défini à `application/json`.
|
||||
|
||||
Et il sera documenté comme tel dans OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
`ORJSONResponse` est disponible uniquement dans FastAPI, pas dans Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Réponse HTML { #html-response }
|
||||
|
||||
Pour renvoyer une réponse avec du HTML directement depuis **FastAPI**, utilisez `HTMLResponse`.
|
||||
|
||||
- Importez `HTMLResponse`.
|
||||
- Passez `HTMLResponse` comme paramètre `response_class` de votre décorateur de chemin d'accès.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info
|
||||
|
||||
Le paramètre `response_class` sera aussi utilisé pour définir le « media type » de la réponse.
|
||||
|
||||
Dans ce cas, l'en-tête HTTP `Content-Type` sera défini à `text/html`.
|
||||
|
||||
Et il sera documenté comme tel dans OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
### Renvoyer une `Response` { #return-a-response }
|
||||
|
||||
Comme vu dans [Renvoyer directement une Response](response-directly.md){.internal-link target=_blank}, vous pouvez aussi remplacer la réponse directement dans votre chemin d'accès, en la renvoyant.
|
||||
|
||||
Le même exemple ci-dessus, renvoyant une `HTMLResponse`, pourrait ressembler à :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Une `Response` renvoyée directement par votre fonction de chemin d'accès ne sera pas documentée dans OpenAPI (par exemple, le `Content-Type` ne sera pas documenté) et ne sera pas visible dans les documents interactifs automatiques.
|
||||
|
||||
///
|
||||
|
||||
/// info
|
||||
|
||||
Bien sûr, l'en-tête `Content-Type` réel, le code d'état, etc., proviendront de l'objet `Response` que vous avez renvoyé.
|
||||
|
||||
///
|
||||
|
||||
### Documenter dans OpenAPI et remplacer `Response` { #document-in-openapi-and-override-response }
|
||||
|
||||
Si vous voulez remplacer la réponse depuis l'intérieur de la fonction mais en même temps documenter le « media type » dans OpenAPI, vous pouvez utiliser le paramètre `response_class` ET renvoyer un objet `Response`.
|
||||
|
||||
`response_class` sera alors utilisé uniquement pour documenter l'opération de chemin d'accès OpenAPI, mais votre `Response` sera utilisée telle quelle.
|
||||
|
||||
#### Renvoyer directement une `HTMLResponse` { #return-an-htmlresponse-directly }
|
||||
|
||||
Par exemple, cela pourrait être quelque chose comme :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
Dans cet exemple, la fonction `generate_html_response()` génère déjà et renvoie une `Response` au lieu de renvoyer le HTML dans une `str`.
|
||||
|
||||
En renvoyant le résultat de l'appel à `generate_html_response()`, vous renvoyez déjà une `Response` qui remplacera le comportement par défaut de **FastAPI**.
|
||||
|
||||
Mais comme vous avez aussi passé `HTMLResponse` dans `response_class`, **FastAPI** saura comment la documenter dans OpenAPI et les documents interactifs comme HTML avec `text/html` :
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## Réponses disponibles { #available-responses }
|
||||
|
||||
Voici certaines des réponses disponibles.
|
||||
|
||||
Gardez à l'esprit que vous pouvez utiliser `Response` pour renvoyer autre chose, ou même créer une sous-classe personnalisée.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
Vous pourriez aussi utiliser `from starlette.responses import HTMLResponse`.
|
||||
|
||||
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` simplement pour votre confort de développement. Mais la plupart des réponses disponibles viennent directement de Starlette.
|
||||
|
||||
///
|
||||
|
||||
### `Response` { #response }
|
||||
|
||||
La classe principale `Response`, toutes les autres réponses en héritent.
|
||||
|
||||
Vous pouvez la renvoyer directement.
|
||||
|
||||
Elle accepte les paramètres suivants :
|
||||
|
||||
- `content` - Une `str` ou des `bytes`.
|
||||
- `status_code` - Un code d'état HTTP de type `int`.
|
||||
- `headers` - Un `dict` de chaînes.
|
||||
- `media_type` - Une `str` donnant le media type. Par exemple « text/html ».
|
||||
|
||||
FastAPI (en fait Starlette) inclura automatiquement un en-tête Content-Length. Il inclura aussi un en-tête Content-Type, basé sur `media_type` et en ajoutant un charset pour les types textuels.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
Prend du texte ou des octets et renvoie une réponse HTML, comme vous l'avez lu ci-dessus.
|
||||
|
||||
### `PlainTextResponse` { #plaintextresponse }
|
||||
|
||||
Prend du texte ou des octets et renvoie une réponse en texte brut.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
Prend des données et renvoie une réponse encodée en `application/json`.
|
||||
|
||||
C'est la réponse par défaut utilisée dans **FastAPI**, comme vous l'avez lu ci-dessus.
|
||||
|
||||
### `ORJSONResponse` { #orjsonresponse }
|
||||
|
||||
Une réponse JSON alternative rapide utilisant <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, comme vous l'avez lu ci-dessus.
|
||||
|
||||
/// info
|
||||
|
||||
Cela nécessite l'installation de `orjson`, par exemple avec `pip install orjson`.
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse` { #ujsonresponse }
|
||||
|
||||
Une réponse JSON alternative utilisant <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
|
||||
|
||||
/// info
|
||||
|
||||
Cela nécessite l'installation de `ujson`, par exemple avec `pip install ujson`.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
`ujson` est moins rigoureux que l'implémentation intégrée de Python dans sa gestion de certains cas limites.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Il est possible que `ORJSONResponse` soit une alternative plus rapide.
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse` { #redirectresponse }
|
||||
|
||||
Renvoie une redirection HTTP. Utilise par défaut un code d'état 307 (Temporary Redirect).
|
||||
|
||||
Vous pouvez renvoyer directement une `RedirectResponse` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
Ou vous pouvez l'utiliser dans le paramètre `response_class` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
Si vous faites cela, vous pouvez alors renvoyer directement l'URL depuis votre fonction de chemin d'accès.
|
||||
|
||||
Dans ce cas, le `status_code` utilisé sera celui par défaut pour `RedirectResponse`, c'est-à-dire `307`.
|
||||
|
||||
---
|
||||
|
||||
Vous pouvez aussi utiliser le paramètre `status_code` combiné avec le paramètre `response_class` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Prend un générateur async ou un générateur/itérateur normal et diffuse le corps de la réponse.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### Utiliser `StreamingResponse` avec des objets de type fichier { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
Si vous avez un objet <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">de type fichier</a> (par ex. l'objet renvoyé par `open()`), vous pouvez créer une fonction génératrice pour itérer sur cet objet de type fichier.
|
||||
|
||||
De cette façon, vous n'avez pas à tout lire en mémoire au préalable, et vous pouvez passer cette fonction génératrice à `StreamingResponse`, puis la renvoyer.
|
||||
|
||||
Cela inclut de nombreuses bibliothèques pour interagir avec du stockage cloud, du traitement vidéo, et autres.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. C'est la fonction génératrice. C'est une « fonction génératrice » parce qu'elle contient des instructions `yield` à l'intérieur.
|
||||
2. En utilisant un bloc `with`, nous nous assurons que l'objet de type fichier est fermé après l'exécution de la fonction génératrice. Donc, après qu'elle a fini d'envoyer la réponse.
|
||||
3. Ce `yield from` indique à la fonction d'itérer sur l'objet nommé `file_like`. Puis, pour chaque partie itérée, de produire cette partie comme provenant de cette fonction génératrice (`iterfile`).
|
||||
|
||||
Ainsi, c'est une fonction génératrice qui transfère le travail de « génération » à autre chose en interne.
|
||||
|
||||
En procédant ainsi, nous pouvons la placer dans un bloc `with` et, de cette façon, garantir que l'objet de type fichier est fermé après la fin.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez qu'ici, comme nous utilisons le `open()` standard qui ne prend pas en charge `async` et `await`, nous déclarons le chemin d'accès avec un `def` normal.
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse` { #fileresponse }
|
||||
|
||||
Diffuse de façon asynchrone un fichier comme réponse.
|
||||
|
||||
Prend un ensemble de paramètres différent à l'instanciation par rapport aux autres types de réponse :
|
||||
|
||||
- `path` - Le chemin du fichier à diffuser.
|
||||
- `headers` - D'éventuels en-têtes personnalisés à inclure, sous forme de dictionnaire.
|
||||
- `media_type` - Une chaîne donnant le media type. Si non défini, le nom du fichier ou le chemin sera utilisé pour en déduire un media type.
|
||||
- `filename` - Si défini, sera inclus dans l'en-tête `Content-Disposition` de la réponse.
|
||||
|
||||
Les réponses de type fichier incluront les en-têtes appropriés `Content-Length`, `Last-Modified` et `ETag`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
Vous pouvez aussi utiliser le paramètre `response_class` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
Dans ce cas, vous pouvez renvoyer directement le chemin du fichier depuis votre fonction de chemin d'accès.
|
||||
|
||||
## Classe de réponse personnalisée { #custom-response-class }
|
||||
|
||||
Vous pouvez créer votre propre classe de réponse personnalisée, héritant de `Response`, et l'utiliser.
|
||||
|
||||
Par exemple, disons que vous voulez utiliser <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, mais avec certains réglages personnalisés non utilisés dans la classe `ORJSONResponse` incluse.
|
||||
|
||||
Disons que vous voulez renvoyer du JSON indenté et formaté, donc vous voulez utiliser l'option orjson `orjson.OPT_INDENT_2`.
|
||||
|
||||
Vous pourriez créer une `CustomORJSONResponse`. L'essentiel est de créer une méthode `Response.render(content)` qui renvoie le contenu en `bytes` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
Maintenant, au lieu de renvoyer :
|
||||
|
||||
```json
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
... cette réponse renverra :
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello World"
|
||||
}
|
||||
```
|
||||
|
||||
Bien sûr, vous trouverez probablement des moyens bien meilleurs de tirer parti de cela que de formater du JSON. 😉
|
||||
|
||||
## Classe de réponse par défaut { #default-response-class }
|
||||
|
||||
Lors de la création d'une instance de classe **FastAPI** ou d'un `APIRouter`, vous pouvez spécifier quelle classe de réponse utiliser par défaut.
|
||||
|
||||
Le paramètre qui le définit est `default_response_class`.
|
||||
|
||||
Dans l'exemple ci-dessous, **FastAPI** utilisera `ORJSONResponse` par défaut, dans tous les chemins d'accès, au lieu de `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous pouvez toujours remplacer `response_class` dans les chemins d'accès comme auparavant.
|
||||
|
||||
///
|
||||
|
||||
## Documentation supplémentaire { #additional-documentation }
|
||||
|
||||
Vous pouvez aussi déclarer le media type et de nombreux autres détails dans OpenAPI en utilisant `responses` : [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
# Utiliser des dataclasses { #using-dataclasses }
|
||||
|
||||
FastAPI est construit au‑dessus de **Pydantic**, et je vous ai montré comment utiliser des modèles Pydantic pour déclarer les requêtes et les réponses.
|
||||
|
||||
Mais FastAPI prend aussi en charge l'utilisation de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> de la même manière :
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
|
||||
|
||||
Cela fonctionne grâce à **Pydantic**, qui offre une <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">prise en charge interne des `dataclasses`</a>.
|
||||
|
||||
Ainsi, même avec le code ci‑dessus qui n'emploie pas explicitement Pydantic, FastAPI utilise Pydantic pour convertir ces dataclasses standard en la variante de dataclasses de Pydantic.
|
||||
|
||||
Et bien sûr, cela prend en charge la même chose :
|
||||
|
||||
* validation des données
|
||||
* sérialisation des données
|
||||
* documentation des données, etc.
|
||||
|
||||
Cela fonctionne de la même manière qu'avec les modèles Pydantic. Et, en réalité, c'est mis en œuvre de la même façon en interne, en utilisant Pydantic.
|
||||
|
||||
/// info | Info
|
||||
|
||||
Gardez à l'esprit que les dataclasses ne peuvent pas tout ce que peuvent faire les modèles Pydantic.
|
||||
|
||||
Vous pourriez donc avoir encore besoin d'utiliser des modèles Pydantic.
|
||||
|
||||
Mais si vous avez déjà un ensemble de dataclasses sous la main, c'est une astuce pratique pour les utiliser afin d'alimenter une API Web avec FastAPI. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Utiliser des dataclasses dans `response_model` { #dataclasses-in-response-model }
|
||||
|
||||
Vous pouvez aussi utiliser `dataclasses` dans le paramètre `response_model` :
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
|
||||
|
||||
La dataclass sera automatiquement convertie en dataclass Pydantic.
|
||||
|
||||
Ainsi, son schéma apparaîtra dans l'interface utilisateur de la documentation de l'API :
|
||||
|
||||
<img src="/img/tutorial/dataclasses/image01.png">
|
||||
|
||||
## Utiliser des dataclasses dans des structures de données imbriquées { #dataclasses-in-nested-data-structures }
|
||||
|
||||
Vous pouvez aussi combiner `dataclasses` avec d'autres annotations de type pour créer des structures de données imbriquées.
|
||||
|
||||
Dans certains cas, vous devrez peut‑être encore utiliser la version `dataclasses` de Pydantic. Par exemple, si vous rencontrez des erreurs avec la documentation d'API générée automatiquement.
|
||||
|
||||
Dans ce cas, vous pouvez simplement remplacer les `dataclasses` standard par `pydantic.dataclasses`, qui est un remplacement drop‑in :
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
|
||||
|
||||
1. Nous continuons à importer `field` depuis les `dataclasses` standard.
|
||||
|
||||
2. `pydantic.dataclasses` est un remplacement drop‑in pour `dataclasses`.
|
||||
|
||||
3. La dataclass `Author` inclut une liste de dataclasses `Item`.
|
||||
|
||||
4. La dataclass `Author` est utilisée comme paramètre `response_model`.
|
||||
|
||||
5. Vous pouvez utiliser d'autres annotations de type standard avec des dataclasses comme corps de la requête.
|
||||
|
||||
Dans ce cas, il s'agit d'une liste de dataclasses `Item`.
|
||||
|
||||
6. Ici, nous renvoyons un dictionnaire qui contient `items`, qui est une liste de dataclasses.
|
||||
|
||||
FastAPI est toujours capable de <dfn title="convertir les données dans un format pouvant être transmis">sérialiser</dfn> les données en JSON.
|
||||
|
||||
7. Ici, `response_model` utilise une annotation de type correspondant à une liste de dataclasses `Author`.
|
||||
|
||||
Là encore, vous pouvez combiner `dataclasses` avec des annotations de type standard.
|
||||
|
||||
8. Notez que cette *fonction de chemin d'accès* utilise un `def` classique au lieu de `async def`.
|
||||
|
||||
Comme toujours, avec FastAPI vous pouvez combiner `def` et `async def` selon vos besoins.
|
||||
|
||||
Si vous avez besoin d'un rappel sur quand utiliser l'un ou l'autre, consultez la section _« In a hurry? »_ dans la documentation à propos de [`async` et `await`](../async.md#in-a-hurry){.internal-link target=_blank}.
|
||||
|
||||
9. Cette *fonction de chemin d'accès* ne renvoie pas des dataclasses (même si elle le pourrait), mais une liste de dictionnaires contenant des données internes.
|
||||
|
||||
FastAPI utilisera le paramètre `response_model` (qui inclut des dataclasses) pour convertir la réponse.
|
||||
|
||||
Vous pouvez combiner `dataclasses` avec d'autres annotations de type, selon de nombreuses combinaisons, pour former des structures de données complexes.
|
||||
|
||||
Reportez‑vous aux annotations dans le code ci‑dessus pour voir plus de détails spécifiques.
|
||||
|
||||
## En savoir plus { #learn-more }
|
||||
|
||||
Vous pouvez aussi combiner `dataclasses` avec d'autres modèles Pydantic, en hériter, les inclure dans vos propres modèles, etc.
|
||||
|
||||
Pour en savoir plus, consultez la <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">documentation Pydantic sur les dataclasses</a>.
|
||||
|
||||
## Version { #version }
|
||||
|
||||
C'est disponible depuis FastAPI version `0.67.0`. 🔖
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
# Événements de cycle de vie { #lifespan-events }
|
||||
|
||||
Vous pouvez définir une logique (du code) qui doit être exécutée avant que l'application ne **démarre**. Cela signifie que ce code sera exécuté **une seule fois**, **avant** que l'application ne **commence à recevoir des requêtes**.
|
||||
|
||||
De la même manière, vous pouvez définir une logique (du code) qui doit être exécutée lorsque l'application **s'arrête**. Dans ce cas, ce code sera exécuté **une seule fois**, **après** avoir traité potentiellement **de nombreuses requêtes**.
|
||||
|
||||
Comme ce code est exécuté avant que l'application ne **commence** à recevoir des requêtes, et juste après qu'elle **termine** de les traiter, il couvre tout le **cycle de vie** de l'application (le mot « lifespan » va être important dans un instant 😉).
|
||||
|
||||
Cela peut être très utile pour configurer des **ressources** dont vous avez besoin pour l'ensemble de l'application, qui sont **partagées** entre les requêtes, et/ou que vous devez **nettoyer** ensuite. Par exemple, un pool de connexions à une base de données, ou le chargement d'un modèle d'apprentissage automatique partagé.
|
||||
|
||||
## Cas d'utilisation { #use-case }
|
||||
|
||||
Commençons par un exemple de **cas d'utilisation**, puis voyons comment le résoudre avec ceci.
|
||||
|
||||
Imaginons que vous ayez des **modèles d'apprentissage automatique** que vous souhaitez utiliser pour traiter des requêtes. 🤖
|
||||
|
||||
Les mêmes modèles sont partagés entre les requêtes, ce n'est donc pas un modèle par requête, ni un par utilisateur, ou quelque chose de similaire.
|
||||
|
||||
Imaginons que le chargement du modèle puisse **prendre pas mal de temps**, car il doit lire beaucoup de **données depuis le disque**. Vous ne voulez donc pas le faire pour chaque requête.
|
||||
|
||||
Vous pourriez le charger au niveau supérieur du module/fichier, mais cela signifierait aussi qu'il **chargerait le modèle** même si vous exécutez simplement un test automatisé simple ; ce test serait alors **lent** car il devrait attendre le chargement du modèle avant de pouvoir exécuter une partie indépendante du code.
|
||||
|
||||
C'est ce que nous allons résoudre : chargeons le modèle avant que les requêtes ne soient traitées, mais seulement juste avant que l'application ne commence à recevoir des requêtes, pas pendant le chargement du code.
|
||||
|
||||
## Cycle de vie { #lifespan }
|
||||
|
||||
Vous pouvez définir cette logique de *démarrage* et d'*arrêt* en utilisant le paramètre `lifespan` de l'application `FastAPI`, et un « gestionnaire de contexte » (je vais vous montrer ce que c'est dans un instant).
|
||||
|
||||
Commençons par un exemple, puis voyons-le en détail.
|
||||
|
||||
Nous créons une fonction async `lifespan()` avec `yield` comme ceci :
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
Ici, nous simulons l'opération de *démarrage* coûteuse de chargement du modèle en plaçant la fonction (factice) du modèle dans le dictionnaire avec les modèles d'apprentissage automatique avant le `yield`. Ce code sera exécuté **avant** que l'application ne **commence à recevoir des requêtes**, pendant le *démarrage*.
|
||||
|
||||
Puis, juste après le `yield`, nous déchargeons le modèle. Ce code sera exécuté **après** que l'application **a fini de traiter les requêtes**, juste avant l'*arrêt*. Cela pourrait, par exemple, libérer des ressources comme la mémoire ou un GPU.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
L’« arrêt » se produit lorsque vous **arrêtez** l'application.
|
||||
|
||||
Peut-être devez-vous démarrer une nouvelle version, ou vous en avez simplement assez de l'exécuter. 🤷
|
||||
|
||||
///
|
||||
|
||||
### Fonction de cycle de vie { #lifespan-function }
|
||||
|
||||
La première chose à remarquer est que nous définissons une fonction async avec `yield`. C'est très similaire aux Dépendances avec `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
La première partie de la fonction, avant le `yield`, sera exécutée **avant** le démarrage de l'application.
|
||||
|
||||
Et la partie après le `yield` sera exécutée **après** que l'application a terminé.
|
||||
|
||||
### Gestionnaire de contexte asynchrone { #async-context-manager }
|
||||
|
||||
Si vous regardez, la fonction est décorée avec `@asynccontextmanager`.
|
||||
|
||||
Cela convertit la fonction en quelque chose appelé un « **gestionnaire de contexte asynchrone** ».
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
Un **gestionnaire de contexte** en Python est quelque chose que vous pouvez utiliser dans une instruction `with`. Par exemple, `open()` peut être utilisé comme gestionnaire de contexte :
|
||||
|
||||
```Python
|
||||
with open("file.txt") as file:
|
||||
file.read()
|
||||
```
|
||||
|
||||
Dans les versions récentes de Python, il existe aussi un **gestionnaire de contexte asynchrone**. Vous l'utiliseriez avec `async with` :
|
||||
|
||||
```Python
|
||||
async with lifespan(app):
|
||||
await do_stuff()
|
||||
```
|
||||
|
||||
Quand vous créez un gestionnaire de contexte ou un gestionnaire de contexte asynchrone comme ci-dessus, ce qu'il fait, c'est qu'avant d'entrer dans le bloc `with`, il exécute le code avant le `yield`, et après être sorti du bloc `with`, il exécute le code après le `yield`.
|
||||
|
||||
Dans notre exemple de code ci-dessus, nous ne l'utilisons pas directement, mais nous le transmettons à FastAPI pour qu'il l'utilise.
|
||||
|
||||
Le paramètre `lifespan` de l'application `FastAPI` accepte un **gestionnaire de contexte asynchrone**, nous pouvons donc lui passer notre nouveau gestionnaire de contexte asynchrone `lifespan`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## Événements alternatifs (déprécié) { #alternative-events-deprecated }
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
La méthode recommandée pour gérer le *démarrage* et l'*arrêt* est d'utiliser le paramètre `lifespan` de l'application `FastAPI` comme décrit ci-dessus. Si vous fournissez un paramètre `lifespan`, les gestionnaires d'événements `startup` et `shutdown` ne seront plus appelés. C'est soit tout en `lifespan`, soit tout en événements, pas les deux.
|
||||
|
||||
Vous pouvez probablement passer cette partie.
|
||||
|
||||
///
|
||||
|
||||
Il existe une autre manière de définir cette logique à exécuter au *démarrage* et à l'*arrêt*.
|
||||
|
||||
Vous pouvez définir des gestionnaires d'événements (fonctions) qui doivent être exécutés avant le démarrage de l'application, ou lorsque l'application s'arrête.
|
||||
|
||||
Ces fonctions peuvent être déclarées avec `async def` ou un `def` normal.
|
||||
|
||||
### Événement `startup` { #startup-event }
|
||||
|
||||
Pour ajouter une fonction qui doit être exécutée avant le démarrage de l'application, déclarez-la avec l'événement « startup » :
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Dans ce cas, la fonction gestionnaire de l'événement `startup` initialisera la « base de données » des items (juste un `dict`) avec quelques valeurs.
|
||||
|
||||
Vous pouvez ajouter plusieurs fonctions de gestion d'événements.
|
||||
|
||||
Et votre application ne commencera pas à recevoir des requêtes avant que tous les gestionnaires de l'événement `startup` aient terminé.
|
||||
|
||||
### Événement `shutdown` { #shutdown-event }
|
||||
|
||||
Pour ajouter une fonction qui doit être exécutée lorsque l'application s'arrête, déclarez-la avec l'événement « shutdown » :
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
Ici, la fonction gestionnaire de l'événement `shutdown` écrira une ligne de texte « Application shutdown » dans un fichier `log.txt`.
|
||||
|
||||
/// info
|
||||
|
||||
Dans la fonction `open()`, le `mode="a"` signifie « append » (ajouter) ; la ligne sera donc ajoutée après ce qui se trouve déjà dans ce fichier, sans écraser le contenu précédent.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Notez que dans ce cas, nous utilisons une fonction Python standard `open()` qui interagit avec un fichier.
|
||||
|
||||
Cela implique des E/S (input/output), qui nécessitent « d'attendre » que des choses soient écrites sur le disque.
|
||||
|
||||
Mais `open()` n'utilise pas `async` et `await`.
|
||||
|
||||
Nous déclarons donc la fonction gestionnaire d'événement avec un `def` standard plutôt qu'avec `async def`.
|
||||
|
||||
///
|
||||
|
||||
### `startup` et `shutdown` ensemble { #startup-and-shutdown-together }
|
||||
|
||||
Il y a de fortes chances que la logique de votre *démarrage* et de votre *arrêt* soit liée : vous pourriez vouloir démarrer quelque chose puis le terminer, acquérir une ressource puis la libérer, etc.
|
||||
|
||||
Faire cela dans des fonctions séparées qui ne partagent pas de logique ni de variables est plus difficile, car vous devriez stocker des valeurs dans des variables globales ou recourir à des astuces similaires.
|
||||
|
||||
Pour cette raison, il est désormais recommandé d'utiliser plutôt le `lifespan` comme expliqué ci-dessus.
|
||||
|
||||
## Détails techniques { #technical-details }
|
||||
|
||||
Juste un détail technique pour les nerds curieux. 🤓
|
||||
|
||||
Sous le capot, dans la spécification technique ASGI, cela fait partie du <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">protocole Lifespan</a>, et il y définit des événements appelés `startup` et `shutdown`.
|
||||
|
||||
/// info
|
||||
|
||||
Vous pouvez en lire plus sur les gestionnaires `lifespan` de Starlette dans la <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">documentation « Lifespan » de Starlette</a>.
|
||||
|
||||
Y compris comment gérer l'état de cycle de vie qui peut être utilisé dans d'autres parties de votre code.
|
||||
|
||||
///
|
||||
|
||||
## Sous-applications { #sub-applications }
|
||||
|
||||
🚨 Gardez à l'esprit que ces événements de cycle de vie (démarrage et arrêt) ne seront exécutés que pour l'application principale, pas pour [Sous-applications - Montages](sub-applications.md){.internal-link target=_blank}.
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
# Générer des SDK { #generating-sdks }
|
||||
|
||||
Parce que **FastAPI** est basé sur la spécification **OpenAPI**, ses API peuvent être décrites dans un format standard compris par de nombreux outils.
|
||||
|
||||
Cela facilite la génération de **documentation** à jour, de bibliothèques clientes (<abbr title="Software Development Kits - Kits de développement logiciel">**SDKs**</abbr>) dans plusieurs langages, ainsi que de **tests** ou de **workflows d’automatisation** qui restent synchronisés avec votre code.
|
||||
|
||||
Dans ce guide, vous apprendrez à générer un **SDK TypeScript** pour votre backend FastAPI.
|
||||
|
||||
## Générateurs de SDK open source { #open-source-sdk-generators }
|
||||
|
||||
Une option polyvalente est <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>, qui prend en charge **de nombreux langages de programmation** et peut générer des SDK à partir de votre spécification OpenAPI.
|
||||
|
||||
Pour les **clients TypeScript**, <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> est une solution dédiée, offrant une expérience optimisée pour l’écosystème TypeScript.
|
||||
|
||||
Vous pouvez découvrir davantage de générateurs de SDK sur <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
FastAPI génère automatiquement des spécifications **OpenAPI 3.1**, donc tout outil que vous utilisez doit prendre en charge cette version.
|
||||
|
||||
///
|
||||
|
||||
## Générateurs de SDK par les sponsors de FastAPI { #sdk-generators-from-fastapi-sponsors }
|
||||
|
||||
Cette section met en avant des solutions **soutenues par des fonds** et **par des entreprises** qui sponsorisent FastAPI. Ces produits offrent **des fonctionnalités supplémentaires** et **des intégrations** en plus de SDK de haute qualité générés.
|
||||
|
||||
En ✨ [**sponsorisant FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, ces entreprises contribuent à garantir que le framework et son **écosystème** restent sains et **durables**.
|
||||
|
||||
Leur sponsoring démontre également un fort engagement envers la **communauté** FastAPI (vous), montrant qu’elles se soucient non seulement d’offrir un **excellent service**, mais aussi de soutenir un **framework robuste et florissant**, FastAPI. 🙇
|
||||
|
||||
Par exemple, vous pourriez essayer :
|
||||
|
||||
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
|
||||
* <a href="https://www.stainless.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
|
||||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
|
||||
|
||||
Certaines de ces solutions peuvent aussi être open source ou proposer des niveaux gratuits, afin que vous puissiez les essayer sans engagement financier. D’autres générateurs de SDK commerciaux existent et peuvent être trouvés en ligne. 🤓
|
||||
|
||||
## Créer un SDK TypeScript { #create-a-typescript-sdk }
|
||||
|
||||
Commençons par une application FastAPI simple :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Remarquez que les *chemins d'accès* définissent les modèles qu’ils utilisent pour le payload de requête et le payload de réponse, en utilisant les modèles `Item` et `ResponseMessage`.
|
||||
|
||||
### Documentation de l’API { #api-docs }
|
||||
|
||||
Si vous allez sur `/docs`, vous verrez qu’elle contient les **schémas** pour les données à envoyer dans les requêtes et reçues dans les réponses :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image01.png">
|
||||
|
||||
Vous voyez ces schémas parce qu’ils ont été déclarés avec les modèles dans l’application.
|
||||
|
||||
Ces informations sont disponibles dans le **schéma OpenAPI** de l’application, puis affichées dans la documentation de l’API.
|
||||
|
||||
Ces mêmes informations issues des modèles, incluses dans OpenAPI, peuvent être utilisées pour **générer le code client**.
|
||||
|
||||
### Hey API { #hey-api }
|
||||
|
||||
Une fois que vous avez une application FastAPI avec les modèles, vous pouvez utiliser Hey API pour générer un client TypeScript. Le moyen le plus rapide de le faire est via npx.
|
||||
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
||||
```
|
||||
|
||||
Cela générera un SDK TypeScript dans `./src/client`.
|
||||
|
||||
Vous pouvez apprendre à <a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">installer `@hey-api/openapi-ts`</a> et lire à propos du <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">résultat généré</a> sur leur site.
|
||||
|
||||
### Utiliser le SDK { #using-the-sdk }
|
||||
|
||||
Vous pouvez maintenant importer et utiliser le code client. Cela pourrait ressembler à ceci, remarquez que vous obtenez l’autocomplétion pour les méthodes :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image02.png">
|
||||
|
||||
Vous obtiendrez également l’autocomplétion pour le payload à envoyer :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image03.png">
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez l’autocomplétion pour `name` et `price`, qui a été définie dans l’application FastAPI, dans le modèle `Item`.
|
||||
|
||||
///
|
||||
|
||||
Vous aurez des erreurs en ligne pour les données que vous envoyez :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image04.png">
|
||||
|
||||
L’objet de réponse aura également l’autocomplétion :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image05.png">
|
||||
|
||||
## Application FastAPI avec des tags { #fastapi-app-with-tags }
|
||||
|
||||
Dans de nombreux cas, votre application FastAPI sera plus grande, et vous utiliserez probablement des tags pour séparer différents groupes de *chemins d'accès*.
|
||||
|
||||
Par exemple, vous pourriez avoir une section pour les **items** et une autre section pour les **users**, et elles pourraient être séparées par des tags :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### Générer un client TypeScript avec des tags { #generate-a-typescript-client-with-tags }
|
||||
|
||||
Si vous générez un client pour une application FastAPI utilisant des tags, il séparera normalement aussi le code client en fonction des tags.
|
||||
|
||||
De cette façon, vous pourrez avoir les éléments ordonnés et correctement groupés côté client :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image06.png">
|
||||
|
||||
Dans ce cas, vous avez :
|
||||
|
||||
* `ItemsService`
|
||||
* `UsersService`
|
||||
|
||||
### Noms des méthodes du client { #client-method-names }
|
||||
|
||||
À l’heure actuelle, les noms de méthodes générés comme `createItemItemsPost` ne sont pas très propres :
|
||||
|
||||
```TypeScript
|
||||
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
|
||||
```
|
||||
|
||||
... c’est parce que le générateur de client utilise l’**operation ID** interne OpenAPI pour chaque *chemin d'accès*.
|
||||
|
||||
OpenAPI exige que chaque operation ID soit unique parmi tous les *chemins d'accès*, donc FastAPI utilise le **nom de la fonction**, le **chemin**, et la **méthode/opération HTTP** pour générer cet operation ID, car de cette façon il peut s’assurer que les operation IDs sont uniques.
|
||||
|
||||
Mais je vais vous montrer comment améliorer cela ensuite. 🤓
|
||||
|
||||
## IDs d’opération personnalisés et meilleurs noms de méthodes { #custom-operation-ids-and-better-method-names }
|
||||
|
||||
Vous pouvez **modifier** la façon dont ces operation IDs sont **générés** pour les simplifier et obtenir des **noms de méthodes plus simples** dans les clients.
|
||||
|
||||
Dans ce cas, vous devez vous assurer que chaque operation ID est **unique** d’une autre manière.
|
||||
|
||||
Par exemple, vous pouvez vous assurer que chaque *chemin d'accès* a un tag, puis générer l’operation ID à partir du **tag** et du **nom** du *chemin d'accès* (le nom de la fonction).
|
||||
|
||||
### Fonction personnalisée de génération d’ID unique { #custom-generate-unique-id-function }
|
||||
|
||||
FastAPI utilise un **ID unique** pour chaque *chemin d'accès*, qui est utilisé pour l’**operation ID** et également pour les noms des modèles personnalisés nécessaires, pour les requêtes ou les réponses.
|
||||
|
||||
Vous pouvez personnaliser cette fonction. Elle prend un `APIRoute` et retourne une chaîne.
|
||||
|
||||
Par exemple, ici elle utilise le premier tag (vous n’en aurez probablement qu’un) et le nom du *chemin d'accès* (le nom de la fonction).
|
||||
|
||||
Vous pouvez ensuite passer cette fonction personnalisée à **FastAPI** via le paramètre `generate_unique_id_function` :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### Générer un client TypeScript avec des IDs d’opération personnalisés { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
Maintenant, si vous régénérez le client, vous verrez qu’il possède des noms de méthodes améliorés :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image07.png">
|
||||
|
||||
Comme vous le voyez, les noms de méthodes contiennent maintenant le tag puis le nom de la fonction ; ils n’incluent plus d’informations provenant du chemin d’URL et de l’opération HTTP.
|
||||
|
||||
### Prétraiter la spécification OpenAPI pour le générateur de client { #preprocess-the-openapi-specification-for-the-client-generator }
|
||||
|
||||
Le code généré contient encore des **informations dupliquées**.
|
||||
|
||||
Nous savons déjà que cette méthode est liée aux **items** parce que ce mot figure dans `ItemsService` (issu du tag), mais nous avons encore le nom du tag préfixé dans le nom de la méthode. 😕
|
||||
|
||||
Nous voudrons probablement le conserver pour OpenAPI en général, car cela garantira que les operation IDs sont **uniques**.
|
||||
|
||||
Mais pour le client généré, nous pourrions **modifier** les operation IDs d’OpenAPI juste avant de générer les clients, simplement pour rendre ces noms de méthodes plus agréables et **plus clairs**.
|
||||
|
||||
Nous pourrions télécharger le JSON OpenAPI dans un fichier `openapi.json` puis **supprimer ce tag préfixé** avec un script comme celui-ci :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
```Javascript
|
||||
{!> ../../docs_src/generate_clients/tutorial004.js!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Avec cela, les operation IDs seraient renommés de `items-get_items` en simplement `get_items`, de sorte que le générateur de client puisse produire des noms de méthodes plus simples.
|
||||
|
||||
### Générer un client TypeScript avec l’OpenAPI prétraité { #generate-a-typescript-client-with-the-preprocessed-openapi }
|
||||
|
||||
Puisque le résultat final se trouve maintenant dans un fichier `openapi.json`, vous devez mettre à jour l’emplacement d’entrée :
|
||||
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
|
||||
```
|
||||
|
||||
Après avoir généré le nouveau client, vous aurez désormais des **noms de méthodes propres**, avec toute l’**autocomplétion**, les **erreurs en ligne**, etc. :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image08.png">
|
||||
|
||||
## Avantages { #benefits }
|
||||
|
||||
En utilisant les clients générés automatiquement, vous obtiendrez de l’**autocomplétion** pour :
|
||||
|
||||
* Méthodes.
|
||||
* Payloads de requête dans le corps, paramètres de requête, etc.
|
||||
* Payloads de réponse.
|
||||
|
||||
Vous auriez également des **erreurs en ligne** pour tout.
|
||||
|
||||
Et chaque fois que vous mettez à jour le code du backend et **régénérez** le frontend, il inclura les nouveaux *chemins d'accès* disponibles en tant que méthodes, supprimera les anciens, et tout autre changement sera reflété dans le code généré. 🤓
|
||||
|
||||
Cela signifie aussi que si quelque chose change, cela sera **reflété** automatiquement dans le code client. Et si vous **bâtissez** le client, il échouera en cas de **discordance** dans les données utilisées.
|
||||
|
||||
Ainsi, vous **détecterez de nombreuses erreurs** très tôt dans le cycle de développement au lieu d’attendre qu’elles apparaissent pour vos utilisateurs finaux en production puis de tenter de déboguer l’origine du problème. ✨
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue