committed by
GitHub
767 changed files with 41621 additions and 5643 deletions
@ -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. 😎 |
||||
@ -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) |
||||
|
|
||||
|
/// |
||||
@ -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); |
||||
|
}); |
||||
@ -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. 🤓 |
||||
@ -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 changed in this diff
Loading…
Reference in new issue