🌐 Update translations for fr (update-all and add-missing) (#14920)

* Update all

* Add missing

* 🎨 Auto format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Motov Yurii 2026-02-14 09:12:41 +01:00 committed by GitHub
parent 417990fcb7
commit 7dc1be8b88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
110 changed files with 12889 additions and 605 deletions

503
docs/fr/docs/_llm-test.md Normal file
View File

@ -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 linvite spécifique à la langue dans `docs/{language code}/llm-prompt.md`. Linvite spécifique à la langue est ajoutée à la fin de `general_prompt`.
Les tests ajoutés ici seront visibles par tous les concepteurs dinvites 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, linvite 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 linvite 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 linvite générale dans `scripts/translate.py`.
////
## Guillemets { #quotes }
//// tab | Test
Hier, mon ami a écrit : « Si vous écrivez « incorrectly » correctement, vous lavez écrit de façon incorrecte ». À quoi jai 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 sil conserve la traduction corrigée lors dune retraduction.
///
////
//// tab | Info
Le concepteur de linvite peut choisir sil 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 à linté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 cest tout.
////
//// tab | Info
Le code dans les blocs de code ne doit pas être modifié, à lexception des commentaires.
Voir la section `### Content of code blocks` dans linvite 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 linvite générale dans `scripts/translate.py`.
////
## Liens Web et internes { #web-and-internal-links }
//// tab | Test
Le texte du lien doit être traduit, ladresse 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, ladresse 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 linvite générale dans `scripts/translate.py`.
////
## Éléments HTML « abbr » { #html-abbr-elements }
//// tab | Test
Voici quelques éléments entourés dun élément HTML « abbr » (certains sont inventés) :
### Labbr 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>
### Labbr 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 linvite 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 dune certaine manière.">grappe</dfn>
* <dfn title="Une méthode dapprentissage automatique qui utilise des réseaux de neurones artificiels avec de nombreuses couches cachées entre les couches dentré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 linvite 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 lAPI
* 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 lapplication
* servir la page
* lapp
* lapplication
* la requête
* la réponse
* la réponse derreur
* le chemin daccès
* le décorateur de chemin daccès
* la fonction de chemin daccè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 den-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 darrêt
* lévénement de cycle de vie
* le gestionnaire
* le gestionnaire dévénements
* le gestionnaire dexceptions
* 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
* lobjet 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
* len-tête
* les en-têtes
* len-tête dautorisation
* len-tête `Authorization`
* len-tête transféré
* le système dinjection 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 denv
* la variable denvironnement
* le `PATH`
* la variable `PATH`
* lauthentification
* le fournisseur dauthentification
* lautorisation
* le formulaire dautorisation
* le fournisseur dautorisation
* lutilisateur sauthentifie
* le système authentifie lutilisateur
* la CLI
* linterface 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
* lenum
* le membre denum
* lencodeur
* le décodeur
* encoder
* décoder
* lexception
* lever
* lexpression
* linstruction
* le frontend
* le backend
* la discussion GitHub
* le ticket GitHub
* la performance
* loptimisation 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 darrière-plan
* la fonction de tâche
* le template
* le moteur de templates
* lannotation de type
* lannotation 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
* lappelable
* 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
* lapplication mobile
* le module
* le montage
* le réseau
* lorigine
* 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 sagit dune liste non exhaustive et non normative de termes (principalement) techniques présents dans les documents. Elle peut aider le concepteur de linvite à déterminer pour quels termes le LLM a besoin dun coup de main. Par exemple, lorsquil continue de remplacer une bonne traduction par une traduction sous-optimale. Ou lorsquil 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`.
////

View File

@ -0,0 +1,3 @@
# À propos { #about }
À propos de FastAPI, de sa conception, de ses sources d'inspiration et plus encore. 🤓

View File

@ -8,7 +8,7 @@ Si vous débutez avec **FastAPI**, vous n'en aurez peut-être pas besoin.
///
Vous pouvez déclarer des réponses supplémentaires, avec des codes HTTP, des types de médias, des descriptions, etc.
Vous pouvez déclarer des réponses supplémentaires, avec des codes d'état supplémentaires, des types de médias, des descriptions, etc.
Ces réponses supplémentaires seront incluses dans le schéma OpenAPI, elles apparaîtront donc également dans la documentation de l'API.
@ -26,7 +26,7 @@ Chacun de ces `dict` de réponse peut avoir une clé `model`, contenant un modè
Par exemple, pour déclarer une autre réponse avec un code HTTP `404` et un modèle Pydantic `Message`, vous pouvez écrire :
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Remarque
@ -203,7 +203,7 @@ Par exemple, vous pouvez déclarer une réponse avec un code HTTP `404` qui util
Et une réponse avec un code HTTP `200` qui utilise votre `response_model`, mais inclut un `example` personnalisé :
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Tout sera combiné et inclus dans votre OpenAPI, et affiché dans la documentation de l'API :

View File

@ -36,6 +36,6 @@ Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` s
## Documents OpenAPI et API { #openapi-and-api-docs }
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (la documentation de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (les documents de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires](additional-responses.md){.internal-link target=_blank}.

View File

@ -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 ellemê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 sousdépendances, et cest 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 linstance, que nous utiliserons pour « paramétrer » la dépendance :
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
Dans ce cas, **FastAPI** naccèdera pas à `__init__` et ne sen souciera pas ; nous lutiliserons 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 quattribut `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 linstance, `checker`, et non la classe ellemê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 nest peutêtre pas encore très clair en quoi cest 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èreplan { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Alertes
Vous navez 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 dutilisation 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 darrêt après `yield` sexé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 darrêt après `yield` sexécute après lenvoi 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 darrêt après que la *fonction de chemin d'accès* a retourné, mais juste avant denvoyer la réponse.
Lobjectif é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 darrê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 darrêt après `yield`.
Ce comportement a été annulé en 0.118.0, afin que le code darrêt après `yield` sexécute après lenvoi de la réponse.
/// info
Comme vous le verrez cidessous, cest 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 dutilisation avec sortie anticipée du code { #use-cases-with-early-exit-code }
Il existe certains cas dutilisation avec des conditions spécifiques qui pourraient bénéficier de lancien comportement, où le code darrêt des dépendances avec `yield` sexécute avant lenvoi 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, nutilise pas la base de données.
Dans ce cas, la session de base de données serait conservée jusquà la fin de lenvoi de la réponse, mais si vous ne lutilisez pas, il ne serait pas nécessaire de la conserver.
Voici à quoi cela pourrait ressembler :
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
Le code darrê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 denvoyer les données lentes :
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Mais comme `generate_stream()` nutilise pas la session de base de données, il nest pas vraiment nécessaire de garder la session ouverte pendant lenvoi de la réponse.
Si vous avez ce cas dutilisation spécifique avec SQLModel (ou SQLAlchemy), vous pouvez fermer explicitement la session dès que vous nen 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 dautres requêtes puissent lutiliser.
Si vous avez un autre cas dutilisation 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 dune fermeture anticipée pour les dépendances avec `yield`.
Sil existe des cas dutilisation convaincants pour une fermeture anticipée dans les dépendances avec `yield`, jenvisagerai dajouter une nouvelle façon dy 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 lexception, lexception était automatiquement levée/transmise à tout gestionnaire dexceptions ou au gestionnaire derreur 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èreplan 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 darrêt dans les dépendances avec `yield` sexécutait après lenvoi 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 dutiliser les mêmes objets « générés par yield » par les dépendances à lintérieur de tâches darrièreplan, car le code darrêt sexécutait après la fin des tâches darrièreplan.
Cela a été modifié dans FastAPI 0.106.0 afin de ne pas conserver des ressources pendant lattente de la transmission de la réponse sur le réseau.
/// tip | Astuce
De plus, une tâche darrièreplan 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 darrièreplan à lintérieur de la tâche ellemême, et nutiliser en interne que des données qui ne dépendent pas des ressources des dépendances avec `yield`.
Par exemple, au lieu dutiliser la même session de base de données, vous créeriez une nouvelle session de base de données à lintérieur de la tâche darrièreplan, et vous obtiendriez les objets depuis la base de données en utilisant cette nouvelle session. Puis, au lieu de passer lobjet obtenu depuis la base de données en paramètre à la fonction de tâche darrièreplan, vous passeriez lidentifiant (ID) de cet objet et vous obtiendriez à nouveau lobjet à lintérieur de la fonction de la tâche darrièreplan.

View File

@ -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`. 😎

View File

@ -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")`.
///

View File

@ -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 entê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 entê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 entê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 entê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 entê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 entê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 entê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 sousapplications.
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 audessus (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` audessus.
## À 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 celuici (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 sousapplication (comme décrit dans [Sousapplications - 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. ✨

View File

@ -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}.

View File

@ -0,0 +1,95 @@
# Utiliser des dataclasses { #using-dataclasses }
FastAPI est construit audessus 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 cidessus 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 dropin :
{* ../../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 dropin 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.
Reportezvous aux annotations dans le code cidessus 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`. 🔖

View File

@ -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}.

View File

@ -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 dautomatisation** 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 quelles se soucient non seulement doffrir 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. Dautres 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 quils utilisent pour le payload de requête et le payload de réponse, en utilisant les modèles `Item` et `ResponseMessage`.
### Documentation de lAPI { #api-docs }
Si vous allez sur `/docs`, vous verrez quelle 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 quils ont été déclarés avec les modèles dans lapplication.
Ces informations sont disponibles dans le **schéma OpenAPI** de lapplication, puis affichées dans la documentation de lAPI.
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 lautocomplétion pour les méthodes :
<img src="/img/tutorial/generate-clients/image02.png">
Vous obtiendrez également lautocomplétion pour le payload à envoyer :
<img src="/img/tutorial/generate-clients/image03.png">
/// tip | Astuce
Remarquez lautocomplétion pour `name` et `price`, qui a été définie dans lapplication 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">
Lobjet de réponse aura également lautocomplé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 }
À lheure 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})
```
... cest 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 sassurer que les operation IDs sont uniques.
Mais je vais vous montrer comment améliorer cela ensuite. 🤓
## IDs dopé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** dune autre manière.
Par exemple, vous pouvez vous assurer que chaque *chemin d'accès* a un tag, puis générer loperation ID à partir du **tag** et du **nom** du *chemin d'accès* (le nom de la fonction).
### Fonction personnalisée de génération dID 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 nen aurez probablement quun) 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 dopération personnalisés { #generate-a-typescript-client-with-custom-operation-ids }
Maintenant, si vous régénérez le client, vous verrez quil 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 nincluent plus dinformations provenant du chemin dURL et de lopé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 dOpenAPI 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 lOpenAPI 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 lemplacement dentré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 dattendre quelles apparaissent pour vos utilisateurs finaux en production puis de tenter de déboguer lorigine du problème. ✨

View File

@ -0,0 +1,97 @@
# Utiliser des middlewares avancés { #advanced-middleware }
Dans le tutoriel principal, vous avez vu comment ajouter des [middlewares personnalisés](../tutorial/middleware.md){.internal-link target=_blank} à votre application.
Vous avez également vu comment gérer [CORS avec le `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
Dans cette section, nous allons voir comment utiliser d'autres middlewares.
## Ajouter des middlewares ASGI { #adding-asgi-middlewares }
Comme **FastAPI** est basé sur Starlette et implémente la spécification <abbr title="Asynchronous Server Gateway Interface - Interface passerelle serveur asynchrone">ASGI</abbr>, vous pouvez utiliser n'importe quel middleware ASGI.
Un middleware n'a pas besoin d'être conçu pour FastAPI ou Starlette pour fonctionner, tant qu'il suit la spécification ASGI.
En général, les middlewares ASGI sont des classes qui s'attendent à recevoir une application ASGI en premier argument.
Ainsi, dans la documentation de middlewares ASGI tiers, on vous indiquera probablement de faire quelque chose comme :
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Mais FastAPI (en fait Starlette) fournit une manière plus simple de le faire, qui garantit que les middlewares internes gèrent les erreurs serveur et que les gestionnaires d'exceptions personnalisés fonctionnent correctement.
Pour cela, vous utilisez `app.add_middleware()` (comme dans l'exemple pour CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` reçoit une classe de middleware en premier argument, ainsi que tout argument supplémentaire à transmettre au middleware.
## Utiliser les middlewares intégrés { #integrated-middlewares }
**FastAPI** inclut plusieurs middlewares pour des cas d'usage courants ; voyons comment les utiliser.
/// note | Détails techniques
Pour les prochains exemples, vous pourriez aussi utiliser `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** fournit plusieurs middlewares dans `fastapi.middleware` simplement pour vous faciliter la vie, en tant que développeur. Mais la plupart des middlewares disponibles viennent directement de Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Impose que toutes les requêtes entrantes soient soit `https`, soit `wss`.
Toute requête entrante en `http` ou `ws` sera redirigée vers le schéma sécurisé correspondant.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Impose que toutes les requêtes entrantes aient un en-tête `Host` correctement défini, afin de se prémunir contre les attaques de type HTTP Host Header.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Les arguments suivants sont pris en charge :
- `allowed_hosts` - Une liste de noms de domaine autorisés comme noms d'hôte. Les domaines génériques tels que `*.example.com` sont pris en charge pour faire correspondre les sous-domaines. Pour autoriser n'importe quel nom d'hôte, utilisez `allowed_hosts=["*"]` ou omettez le middleware.
- `www_redirect` - Si défini à `True`, les requêtes vers les versions sans www des hôtes autorisés seront redirigées vers leurs équivalents avec www. Valeur par défaut : `True`.
Si une requête entrante n'est pas valide, une réponse `400` sera envoyée.
## `GZipMiddleware` { #gzipmiddleware }
Gère les réponses GZip pour toute requête qui inclut « gzip » dans l'en-tête `Accept-Encoding`.
Le middleware gérera les réponses standard et en streaming.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Les arguments suivants sont pris en charge :
- `minimum_size` - Ne pas compresser en GZip les réponses dont la taille est inférieure à ce minimum en octets. Valeur par défaut : `500`.
- `compresslevel` - Utilisé pendant la compression GZip. Entier compris entre 1 et 9. Valeur par défaut : `9`. Une valeur plus faible entraîne une compression plus rapide mais des fichiers plus volumineux, tandis qu'une valeur plus élevée entraîne une compression plus lente mais des fichiers plus petits.
## Autres middlewares { #other-middlewares }
Il existe de nombreux autres middlewares ASGI.
Par exemple :
- <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Le `ProxyHeadersMiddleware` d'Uvicorn</a>
- <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
Pour voir d'autres middlewares disponibles, consultez <a href="https://www.starlette.dev/middleware/" class="external-link" target="_blank">la documentation des middlewares de Starlette</a> et la <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">liste ASGI Awesome</a>.

View File

@ -0,0 +1,186 @@
# Callbacks OpenAPI { #openapi-callbacks }
Vous pourriez créer une API avec un *chemin d'accès* qui déclenche une requête vers une *API externe* créée par quelqu'un d'autre (probablement la même personne développeuse qui utiliserait votre API).
Le processus qui se produit lorsque votre application API appelle l*API externe* sappelle un « callback ». Parce que le logiciel écrit par la personne développeuse externe envoie une requête à votre API puis votre API « rappelle », en envoyant une requête à une *API externe* (probablement créée par la même personne développeuse).
Dans ce cas, vous pourriez vouloir documenter à quoi cette API externe devrait ressembler. Quel *chemin d'accès* elle devrait avoir, quel corps elle devrait attendre, quelle réponse elle devrait renvoyer, etc.
## Une application avec des callbacks { #an-app-with-callbacks }
Voyons tout cela avec un exemple.
Imaginez que vous développiez une application qui permet de créer des factures.
Ces factures auront un `id`, un `title` (facultatif), un `customer` et un `total`.
Lutilisateur de votre API (une personne développeuse externe) créera une facture dans votre API avec une requête POST.
Ensuite votre API va (imaginons) :
* Envoyer la facture à un client de la personne développeuse externe.
* Encaisser largent.
* Renvoyer une notification à lutilisateur de lAPI (la personne développeuse externe).
* Cela sera fait en envoyant une requête POST (depuis *votre API*) vers une *API externe* fournie par cette personne développeuse externe (cest le « callback »).
## Lapplication **FastAPI** normale { #the-normal-fastapi-app }
Voyons dabord à quoi ressemble lapplication API normale avant dajouter le callback.
Elle aura un *chemin d'accès* qui recevra un corps `Invoice`, et un paramètre de requête `callback_url` qui contiendra lURL pour le callback.
Cette partie est assez normale, la plupart du code vous est probablement déjà familier :
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | Astuce
Le paramètre de requête `callback_url` utilise un type Pydantic <a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a>.
///
La seule nouveauté est `callbacks=invoices_callback_router.routes` comme argument du *décorateur de chemin d'accès*. Nous allons voir ce que cest ensuite.
## Documenter le callback { #documenting-the-callback }
Le code réel du callback dépendra fortement de votre application API.
Et il variera probablement beaucoup dune application à lautre.
Cela pourrait être seulement une ou deux lignes de code, comme :
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Mais la partie la plus importante du callback est sans doute de vous assurer que lutilisateur de votre API (la personne développeuse externe) implémente correctement l*API externe*, conformément aux données que *votre API* va envoyer dans le corps de la requête du callback, etc.
Ainsi, ce que nous allons faire ensuite, cest ajouter le code pour documenter à quoi cette *API externe* devrait ressembler pour recevoir le callback de *votre API*.
Cette documentation apparaîtra dans Swagger UI à `/docs` dans votre API, et permettra aux personnes développeuses externes de savoir comment construire l*API externe*.
Cet exemple nimplémente pas le callback lui-même (qui pourrait être une simple ligne de code), uniquement la partie documentation.
/// tip | Astuce
Le callback réel nest quune requête HTTP.
En implémentant vous-même le callback, vous pourriez utiliser quelque chose comme <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> ou <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>.
///
## Écrire le code de documentation du callback { #write-the-callback-documentation-code }
Ce code ne sera pas exécuté dans votre application, nous en avons seulement besoin pour *documenter* à quoi devrait ressembler cette *API externe*.
Mais vous savez déjà comment créer facilement une documentation automatique pour une API avec **FastAPI**.
Nous allons donc utiliser ce même savoir pour documenter à quoi l*API externe* devrait ressembler ... en créant le(s) *chemin(s) d'accès* que lAPI externe devrait implémenter (ceux que votre API appellera).
/// tip | Astuce
Lorsque vous écrivez le code pour documenter un callback, il peut être utile dimaginer que vous êtes cette *personne développeuse externe*. Et que vous implémentez actuellement l*API externe*, pas *votre API*.
Adopter temporairement ce point de vue (celui de la *personne développeuse externe*) peut vous aider à trouver plus évident où placer les paramètres, le modèle Pydantic pour le corps, pour la réponse, etc., pour cette *API externe*.
///
### Créer un `APIRouter` de callback { #create-a-callback-apirouter }
Commencez par créer un nouveau `APIRouter` qui contiendra un ou plusieurs callbacks.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Créer le *chemin d'accès* du callback { #create-the-callback-path-operation }
Pour créer le *chemin d'accès* du callback, utilisez le même `APIRouter` que vous avez créé ci-dessus.
Il devrait ressembler exactement à un *chemin d'accès* FastAPI normal :
* Il devrait probablement déclarer le corps quil doit recevoir, par exemple `body: InvoiceEvent`.
* Et il pourrait aussi déclarer la réponse quil doit renvoyer, par exemple `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Il y a 2 principales différences par rapport à un *chemin d'accès* normal :
* Il na pas besoin davoir de code réel, car votre application nappellera jamais ce code. Il sert uniquement à documenter l*API externe*. La fonction peut donc simplement contenir `pass`.
* Le *chemin* peut contenir une <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expression OpenAPI 3</a> (voir plus bas) où il peut utiliser des variables avec des paramètres et des parties de la requête originale envoyée à *votre API*.
### Lexpression du chemin de callback { #the-callback-path-expression }
Le *chemin* du callback peut contenir une <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expression OpenAPI 3</a> qui peut inclure des parties de la requête originale envoyée à *votre API*.
Dans ce cas, cest la `str` :
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Ainsi, si lutilisateur de votre API (la personne développeuse externe) envoie une requête à *votre API* vers :
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
avec un corps JSON :
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
alors *votre API* traitera la facture et, à un moment ultérieur, enverra une requête de callback à `callback_url` (l*API externe*) :
```
https://www.external.org/events/invoices/2expen51ve
```
avec un corps JSON contenant quelque chose comme :
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
et elle sattendra à une réponse de cette *API externe* avec un corps JSON comme :
```JSON
{
"ok": true
}
```
/// tip | Astuce
Remarquez que lURL de callback utilisée contient lURL reçue en paramètre de requête dans `callback_url` (`https://www.external.org/events`) et aussi l`id` de la facture à lintérieur du corps JSON (`2expen51ve`).
///
### Ajouter le routeur de callback { #add-the-callback-router }
À ce stade, vous avez le(s) *chemin(s) d'accès de callback* nécessaire(s) (celui/ceux que la *personne développeuse externe* doit implémenter dans l*API externe*) dans le routeur de callback que vous avez créé ci-dessus.
Utilisez maintenant le paramètre `callbacks` dans *le décorateur de chemin d'accès de votre API* pour passer lattribut `.routes` (qui est en fait juste une `list` de routes/*chemins d'accès*) depuis ce routeur de callback :
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | Astuce
Remarquez que vous ne passez pas le routeur lui-même (`invoices_callback_router`) à `callback=`, mais lattribut `.routes`, comme dans `invoices_callback_router.routes`.
///
### Vérifier la documentation { #check-the-docs }
Vous pouvez maintenant démarrer votre application et aller sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Vous verrez votre documentation incluant une section « Callbacks » pour votre *chemin d'accès* qui montre à quoi l*API externe* devrait ressembler :
<img src="/img/tutorial/openapi-callbacks/image01.png">

View File

@ -0,0 +1,55 @@
# Webhooks OpenAPI { #openapi-webhooks }
Il existe des cas où vous voulez informer les utilisateurs de votre API que votre application peut appeler leur application (en envoyant une requête) avec des données, généralement pour notifier un type d'événement.
Cela signifie qu'au lieu du processus habituel où vos utilisateurs envoient des requêtes à votre API, c'est votre API (ou votre application) qui peut envoyer des requêtes vers leur système (vers leur API, leur application).
On appelle généralement cela un webhook.
## Étapes des webhooks { #webhooks-steps }
Le processus consiste généralement à définir dans votre code le message que vous enverrez, c'est-à-dire le corps de la requête.
Vous définissez également, d'une manière ou d'une autre, à quels moments votre application enverra ces requêtes ou événements.
Et vos utilisateurs définissent aussi, d'une manière ou d'une autre (par exemple dans un tableau de bord Web), l'URL à laquelle votre application doit envoyer ces requêtes.
Toute la logique de gestion des URL des webhooks et le code qui envoie effectivement ces requêtes vous incombent. Vous l'implémentez comme vous le souhaitez dans votre propre code.
## Documenter des webhooks avec FastAPI et OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
Avec FastAPI, en utilisant OpenAPI, vous pouvez définir les noms de ces webhooks, les types d'opérations HTTP que votre application peut envoyer (par exemple `POST`, `PUT`, etc.) et les corps des requêtes que votre application enverra.
Cela peut grandement faciliter la tâche de vos utilisateurs pour implémenter leurs API afin de recevoir vos requêtes de webhook ; ils pourront même peut-être générer automatiquement une partie de leur propre code d'API.
/// info
Les webhooks sont disponibles dans OpenAPI 3.1.0 et versions ultérieures, pris en charge par FastAPI `0.99.0` et versions ultérieures.
///
## Créer une application avec des webhooks { #an-app-with-webhooks }
Lorsque vous créez une application FastAPI, il existe un attribut `webhooks` que vous pouvez utiliser pour définir des webhooks, de la même manière que vous définiriez des chemins d'accès, par exemple avec `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Les webhooks que vous définissez apparaîtront dans le schéma **OpenAPI** et dans l'interface de **documentation** automatique.
/// info
L'objet `app.webhooks` est en fait simplement un `APIRouter`, le même type que vous utiliseriez pour structurer votre application en plusieurs fichiers.
///
Notez qu'avec les webhooks, vous ne déclarez pas réellement un chemin (comme `/items/`), le texte que vous y passez est simplement un identifiant du webhook (le nom de l'événement). Par exemple, dans `@app.webhooks.post("new-subscription")`, le nom du webhook est `new-subscription`.
C'est parce qu'on s'attend à ce que vos utilisateurs définissent, par un autre moyen (par exemple un tableau de bord Web), le véritable chemin d'URL où ils souhaitent recevoir la requête de webhook.
### Consulter la documentation { #check-the-docs }
Vous pouvez maintenant démarrer votre application et aller sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Vous verrez que votre documentation contient les chemins d'accès habituels et désormais aussi des webhooks :
<img src="/img/tutorial/openapi-webhooks/image01.png">

View File

@ -12,7 +12,7 @@ Vous pouvez définir lOpenAPI `operationId` à utiliser dans votre chemin d
Vous devez vous assurer quil est unique pour chaque opération.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Utiliser le nom de la fonction de chemin daccès comme operationId { #using-the-path-operation-function-name-as-the-operationid }
@ -20,7 +20,7 @@ Si vous souhaitez utiliser les noms de fonction de vos API comme `operationId`,
Vous devez le faire après avoir ajouté tous vos chemins daccès.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Astuce
@ -40,7 +40,7 @@ Même si elles se trouvent dans des modules différents (fichiers Python).
Pour exclure un chemin daccès du schéma OpenAPI généré (et donc des systèmes de documentation automatiques), utilisez le paramètre `include_in_schema` et définissez-le à `False` :
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Description avancée depuis la docstring { #advanced-description-from-docstring }
@ -92,7 +92,7 @@ Vous pouvez étendre le schéma OpenAPI pour un chemin daccès en utilisant l
Cet `openapi_extra` peut être utile, par exemple, pour déclarer des [Extensions OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) :
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Si vous ouvrez la documentation automatique de lAPI, votre extension apparaîtra en bas du chemin daccès spécifique.
@ -139,9 +139,9 @@ Par exemple, vous pourriez décider de lire et de valider la requête avec votre
Vous pourriez le faire avec `openapi_extra` :
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
Dans cet exemple, nous navons déclaré aucun modèle Pydantic. En fait, le corps de la requête nest même pas <abbr title="converti d'un format simple, comme des octets, en objets Python">parsé</abbr> en JSON, il est lu directement en tant que `bytes`, et la fonction `magic_data_reader()` serait chargée de lanalyser dune manière ou dune autre.
Dans cet exemple, nous navons déclaré aucun modèle Pydantic. En fait, le corps de la requête nest même pas <dfn title="converti d'un format simple, comme des octets, en objets Python">parsé</dfn> en JSON, il est lu directement en tant que `bytes`, et la fonction `magic_data_reader()` serait chargée de lanalyser dune manière ou dune autre.
Néanmoins, nous pouvons déclarer le schéma attendu pour le corps de la requête.
@ -153,7 +153,7 @@ Et vous pourriez le faire même si le type de données dans la requête nest
Par exemple, dans cette application nous nutilisons pas la fonctionnalité intégrée de FastAPI pour extraire le JSON Schema des modèles Pydantic ni la validation automatique pour le JSON. En fait, nous déclarons le type de contenu de la requête comme YAML, pas JSON :
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Néanmoins, bien que nous nutilisions pas la fonctionnalité intégrée par défaut, nous utilisons toujours un modèle Pydantic pour générer manuellement le JSON Schema pour les données que nous souhaitons recevoir en YAML.
@ -161,7 +161,7 @@ Ensuite, nous utilisons directement la requête et extrayons le corps en tant qu
Ensuite, dans notre code, nous analysons directement ce contenu YAML, puis nous utilisons à nouveau le même modèle Pydantic pour valider le contenu YAML :
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Astuce

View File

@ -0,0 +1,31 @@
# Réponse - Modifier le code d'état { #response-change-status-code }
Vous avez probablement déjà lu que vous pouvez définir un [Code d'état de la réponse](../tutorial/response-status-code.md){.internal-link target=_blank} par défaut.
Mais dans certains cas, vous devez renvoyer un code d'état différent de celui par défaut.
## Cas d'utilisation { #use-case }
Par exemple, imaginez que vous vouliez renvoyer par défaut un code d'état HTTP « OK » `200`.
Mais si les données n'existent pas, vous voulez les créer et renvoyer un code d'état HTTP « CREATED » `201`.
Mais vous souhaitez toujours pouvoir filtrer et convertir les données que vous renvoyez avec un `response_model`.
Pour ces cas, vous pouvez utiliser un paramètre `Response`.
## Utiliser un paramètre `Response` { #use-a-response-parameter }
Vous pouvez déclarer un paramètre de type `Response` dans votre fonction de chemin d'accès (comme vous pouvez le faire pour les cookies et les en-têtes).
Vous pouvez ensuite définir le `status_code` dans cet objet de réponse *temporaire*.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
Vous pouvez ensuite renvoyer n'importe quel objet nécessaire, comme d'habitude (un `dict`, un modèle de base de données, etc.).
Et si vous avez déclaré un `response_model`, il sera toujours utilisé pour filtrer et convertir l'objet que vous avez renvoyé.
**FastAPI** utilisera cette réponse *temporaire* pour extraire le code d'état (ainsi que les cookies et les en-têtes), et les placera dans la réponse finale qui contient la valeur que vous avez renvoyée, filtrée par tout `response_model`.
Vous pouvez également déclarer le paramètre `Response` dans des dépendances et y définir le code d'état. Mais gardez à l'esprit que la dernière valeur définie prévaut.

View File

@ -0,0 +1,51 @@
# Cookies de réponse { #response-cookies }
## Utiliser un paramètre `Response` { #use-a-response-parameter }
Vous pouvez déclarer un paramètre de type `Response` dans votre fonction de chemin d'accès.
Vous pouvez ensuite définir des cookies dans cet objet de réponse *temporaire*.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
Vous pouvez ensuite renvoyer n'importe quel objet dont vous avez besoin, comme d'habitude (un `dict`, un modèle de base de données, etc.).
Et si vous avez déclaré un `response_model`, il sera toujours utilisé pour filtrer et convertir l'objet que vous avez renvoyé.
**FastAPI** utilisera cette réponse *temporaire* pour extraire les cookies (ainsi que les en-têtes et le code d'état), et les placera dans la réponse finale qui contient la valeur que vous avez renvoyée, filtrée par tout `response_model`.
Vous pouvez également déclarer le paramètre `Response` dans des dépendances, et y définir des cookies (et des en-têtes).
## Renvoyer une `Response` directement { #return-a-response-directly }
Vous pouvez également créer des cookies en renvoyant une `Response` directement dans votre code.
Pour ce faire, vous pouvez créer une réponse comme décrit dans [Renvoyer une Response directement](response-directly.md){.internal-link target=_blank}.
Définissez ensuite des cookies dessus, puis renvoyez-la :
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip | Astuce
Gardez à l'esprit que si vous renvoyez une réponse directement au lieu d'utiliser le paramètre `Response`, FastAPI la renverra telle quelle.
Vous devez donc vous assurer que vos données sont du type correct. Par exemple, qu'elles sont compatibles avec JSON si vous renvoyez une `JSONResponse`.
Et également que vous n'envoyez pas de données qui auraient dû être filtrées par un `response_model`.
///
### En savoir plus { #more-info }
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fournit les mêmes `starlette.responses` que `fastapi.responses` simplement pour votre commodité, en tant que développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
Et comme `Response` peut être utilisé fréquemment pour définir des en-têtes et des cookies, **FastAPI** la met également à disposition via `fastapi.Response`.
///
Pour voir tous les paramètres et options disponibles, consultez la <a href="https://www.starlette.dev/responses/#set-cookie" class="external-link" target="_blank">documentation de Starlette</a>.

View File

@ -22,7 +22,7 @@ En fait, vous pouvez retourner n'importe quelle `Response` ou n'importe quelle s
Et quand vous retournez une `Response`, **FastAPI** la transmet directement.
Elle ne fera aucune conversion de données avec les modèles Pydantic, elle ne convertira pas le contenu en un type quelconque.
Elle ne fera aucune conversion de données avec les modèles Pydantic, elle ne convertira pas le contenu en un type quelconque, etc.
Cela vous donne beaucoup de flexibilité. Vous pouvez retourner n'importe quel type de données, surcharger n'importe quelle déclaration ou validation de données, etc.
@ -54,7 +54,7 @@ Disons que vous voulez retourner une réponse <a href="https://en.wikipedia.org/
Vous pouvez mettre votre contenu XML dans une chaîne de caractères, la placer dans une `Response`, et la retourner :
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Notes { #notes }

View File

@ -0,0 +1,41 @@
# En-têtes de réponse { #response-headers }
## Utiliser un paramètre `Response` { #use-a-response-parameter }
Vous pouvez déclarer un paramètre de type `Response` dans votre fonction de chemin d'accès (comme vous pouvez le faire pour les cookies).
Vous pouvez ensuite définir des en-têtes dans cet objet de réponse temporaire.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
Ensuite, vous pouvez renvoyer n'importe quel objet dont vous avez besoin, comme d'habitude (un `dict`, un modèle de base de données, etc.).
Et si vous avez déclaré un `response_model`, il sera toujours utilisé pour filtrer et convertir l'objet que vous avez renvoyé.
**FastAPI** utilisera cette réponse temporaire pour extraire les en-têtes (ainsi que les cookies et le code de statut), et les placera dans la réponse finale qui contient la valeur que vous avez renvoyée, filtrée par tout `response_model`.
Vous pouvez également déclarer le paramètre `Response` dans des dépendances, et y définir des en-têtes (et des cookies).
## Renvoyer une `Response` directement { #return-a-response-directly }
Vous pouvez également ajouter des en-têtes lorsque vous renvoyez une `Response` directement.
Créez une réponse comme décrit dans [Renvoyer une Response directement](response-directly.md){.internal-link target=_blank} et passez les en-têtes comme paramètre supplémentaire :
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` simplement pour votre commodité, en tant que développeur. Mais la plupart des réponses disponibles viennent directement de Starlette.
Et comme `Response` peut être utilisée fréquemment pour définir des en-têtes et des cookies, **FastAPI** la fournit aussi via `fastapi.Response`.
///
## En-têtes personnalisés { #custom-headers }
Gardez à l'esprit que des en-têtes propriétaires personnalisés peuvent être ajoutés <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">en utilisant le préfixe `X-`</a>.
Mais si vous avez des en-têtes personnalisés que vous voulez qu'un client dans un navigateur puisse voir, vous devez les ajouter à vos configurations CORS (en savoir plus dans [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), en utilisant le paramètre `expose_headers` documenté dans <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">la documentation CORS de Starlette</a>.

View File

@ -0,0 +1,107 @@
# Authentification HTTP Basic { #http-basic-auth }
Pour les cas les plus simples, vous pouvez utiliser l'authentification HTTP Basic.
Avec l'authentification HTTP Basic, l'application attend un en-tête contenant un nom d'utilisateur et un mot de passe.
Si elle ne le reçoit pas, elle renvoie une erreur HTTP 401 « Unauthorized ».
Et elle renvoie un en-tête `WWW-Authenticate` avec la valeur `Basic`, et un paramètre optionnel `realm`.
Cela indique au navigateur d'afficher l'invite intégrée pour saisir un nom d'utilisateur et un mot de passe.
Ensuite, lorsque vous saisissez ce nom d'utilisateur et ce mot de passe, le navigateur les envoie automatiquement dans l'en-tête.
## Authentification HTTP Basic simple { #simple-http-basic-auth }
- Importer `HTTPBasic` et `HTTPBasicCredentials`.
- Créer un « schéma de sécurité » en utilisant `HTTPBasic`.
- Utiliser ce `security` avec une dépendance dans votre chemin d'accès.
- Cela renvoie un objet de type `HTTPBasicCredentials` :
- Il contient le `username` et le `password` envoyés.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Lorsque vous essayez d'ouvrir l'URL pour la première fois (ou cliquez sur le bouton « Execute » dans les documents) le navigateur vous demandera votre nom d'utilisateur et votre mot de passe :
<img src="/img/tutorial/security/image12.png">
## Vérifier le nom d'utilisateur { #check-the-username }
Voici un exemple plus complet.
Utilisez une dépendance pour vérifier si le nom d'utilisateur et le mot de passe sont corrects.
Pour cela, utilisez le module standard Python <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> pour vérifier le nom d'utilisateur et le mot de passe.
`secrets.compare_digest()` doit recevoir des `bytes` ou une `str` ne contenant que des caractères ASCII (ceux de l'anglais), ce qui signifie qu'elle ne fonctionnerait pas avec des caractères comme `á`, comme dans `Sebastián`.
Pour gérer cela, nous convertissons d'abord `username` et `password` en `bytes` en les encodant en UTF-8.
Nous pouvons ensuite utiliser `secrets.compare_digest()` pour vérifier que `credentials.username` est « stanleyjobson » et que `credentials.password` est « swordfish ».
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Cela serait équivalent à :
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Renvoyer une erreur
...
```
Mais en utilisant `secrets.compare_digest()`, cela sera sécurisé contre un type d'attaques appelé « attaques par chronométrage ».
### Attaques par chronométrage { #timing-attacks }
Mais qu'est-ce qu'une « attaque par chronométrage » ?
Imaginons que des attaquants essaient de deviner le nom d'utilisateur et le mot de passe.
Ils envoient alors une requête avec un nom d'utilisateur `johndoe` et un mot de passe `love123`.
Le code Python de votre application serait alors équivalent à quelque chose comme :
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Mais au moment où Python compare le premier `j` de `johndoe` au premier `s` de `stanleyjobson`, il retournera `False`, car il sait déjà que ces deux chaînes ne sont pas identiques, en se disant qu'« il n'est pas nécessaire de gaspiller plus de calcul pour comparer le reste des lettres ». Et votre application dira « Nom d'utilisateur ou mot de passe incorrect ».
Mais ensuite, les attaquants essaient avec le nom d'utilisateur `stanleyjobsox` et le mot de passe `love123`.
Et le code de votre application fait quelque chose comme :
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python devra comparer tout `stanleyjobso` dans `stanleyjobsox` et `stanleyjobson` avant de réaliser que les deux chaînes ne sont pas identiques. Cela prendra donc quelques microsecondes supplémentaires pour répondre « Nom d'utilisateur ou mot de passe incorrect ».
#### Le temps de réponse aide les attaquants { #the-time-to-answer-helps-the-attackers }
À ce stade, en remarquant que le serveur a mis quelques microsecondes de plus à envoyer la réponse « Nom d'utilisateur ou mot de passe incorrect », les attaquants sauront qu'ils ont trouvé quelque chose de juste : certaines des premières lettres étaient correctes.
Ils peuvent alors réessayer en sachant que c'est probablement quelque chose de plus proche de `stanleyjobsox` que de `johndoe`.
#### Une attaque « professionnelle » { #a-professional-attack }
Bien sûr, les attaquants n'essaieraient pas tout cela à la main ; ils écriraient un programme pour le faire, avec éventuellement des milliers ou des millions de tests par seconde. Ils obtiendraient une lettre correcte supplémentaire à la fois.
Ce faisant, en quelques minutes ou heures, les attaquants devineraient le nom d'utilisateur et le mot de passe corrects, avec « l'aide » de notre application, simplement en se basant sur le temps de réponse.
#### Corrigez-le avec `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Mais dans notre code nous utilisons justement `secrets.compare_digest()`.
En bref, il faudra le même temps pour comparer `stanleyjobsox` à `stanleyjobson` que pour comparer `johndoe` à `stanleyjobson`. Il en va de même pour le mot de passe.
Ainsi, en utilisant `secrets.compare_digest()` dans le code de votre application, votre application sera protégée contre toute cette gamme d'attaques de sécurité.
### Renvoyer l'erreur { #return-the-error }
Après avoir détecté que les identifiants sont incorrects, renvoyez une `HTTPException` avec un code d'état 401 (le même que lorsque aucun identifiant n'est fourni) et ajoutez l'en-tête `WWW-Authenticate` pour que le navigateur affiche à nouveau l'invite de connexion :
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}

View File

@ -0,0 +1,19 @@
# Sécurité avancée { #advanced-security }
## Fonctionnalités supplémentaires { #additional-features }
Il existe des fonctionnalités supplémentaires pour gérer la sécurité en plus de celles couvertes dans le [Tutoriel - Guide utilisateur : Sécurité](../../tutorial/security/index.md){.internal-link target=_blank}.
/// tip | Astuce
Les sections suivantes ne sont pas nécessairement « advanced ».
Et il est possible que, pour votre cas dutilisation, la solution se trouve dans lune dentre elles.
///
## Lire dabord le tutoriel { #read-the-tutorial-first }
Les sections suivantes partent du principe que vous avez déjà lu le [Tutoriel - Guide utilisateur : Sécurité](../../tutorial/security/index.md){.internal-link target=_blank} principal.
Elles sappuient toutes sur les mêmes concepts, mais permettent des fonctionnalités supplémentaires.

View File

@ -0,0 +1,274 @@
# Scopes OAuth2 { #oauth2-scopes }
Vous pouvez utiliser des scopes OAuth2 directement avec **FastAPI**, ils sont intégrés pour fonctionner de manière transparente.
Cela vous permettrait davoir un système dautorisations plus fin, conforme au standard OAuth2, intégré à votre application OpenAPI (et à la documentation de lAPI).
OAuth2 avec scopes est le mécanisme utilisé par de nombreux grands fournisseurs dauthentification, comme Facebook, Google, GitHub, Microsoft, X (Twitter), etc. Ils lutilisent pour fournir des permissions spécifiques aux utilisateurs et aux applications.
Chaque fois que vous « log in with » Facebook, Google, GitHub, Microsoft, X (Twitter), cette application utilise OAuth2 avec scopes.
Dans cette section, vous verrez comment gérer lauthentification et lautorisation avec le même OAuth2 avec scopes dans votre application **FastAPI**.
/// warning | Alertes
Cest une section plus ou moins avancée. Si vous débutez, vous pouvez la passer.
Vous navez pas nécessairement besoin des scopes OAuth2, et vous pouvez gérer lauthentification et lautorisation comme vous le souhaitez.
Mais OAuth2 avec scopes peut sintégrer élégamment à votre API (avec OpenAPI) et à votre documentation dAPI.
Néanmoins, cest toujours à vous de faire appliquer ces scopes, ou toute autre exigence de sécurité/autorisation, selon vos besoins, dans votre code.
Dans de nombreux cas, OAuth2 avec scopes peut être excessif.
Mais si vous savez que vous en avez besoin, ou si vous êtes curieux, continuez à lire.
///
## Scopes OAuth2 et OpenAPI { #oauth2-scopes-and-openapi }
La spécification OAuth2 définit des « scopes » comme une liste de chaînes séparées par des espaces.
Le contenu de chacune de ces chaînes peut avoir nimporte quel format, mais ne doit pas contenir despaces.
Ces scopes représentent des « permissions ».
Dans OpenAPI (par ex. la documentation de lAPI), vous pouvez définir des « schémas de sécurité ».
Lorsquun de ces schémas de sécurité utilise OAuth2, vous pouvez aussi déclarer et utiliser des scopes.
Chaque « scope » est juste une chaîne (sans espaces).
Ils sont généralement utilisés pour déclarer des permissions de sécurité spécifiques, par exemple :
* `users:read` ou `users:write` sont des exemples courants.
* `instagram_basic` est utilisé par Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` est utilisé par Google.
/// info
Dans OAuth2, un « scope » est simplement une chaîne qui déclare une permission spécifique requise.
Peu importe sil contient dautres caractères comme `:` ou si cest une URL.
Ces détails dépendent de limplémentation.
Pour OAuth2, ce ne sont que des chaînes.
///
## Vue densemble { #global-view }
Voyons dabord rapidement les parties qui changent par rapport aux exemples du **Tutoriel - Guide utilisateur** pour [OAuth2 avec mot de passe (et hachage), Bearer avec jetons JWT](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Cette fois, en utilisant des scopes OAuth2 :
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Passons maintenant en revue ces changements étape par étape.
## Déclarer le schéma de sécurité OAuth2 { #oauth2-security-scheme }
Le premier changement est que nous déclarons maintenant le schéma de sécurité OAuth2 avec deux scopes disponibles, `me` et `items`.
Le paramètre `scopes` reçoit un `dict` avec chaque scope en clé et la description en valeur :
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Comme nous déclarons maintenant ces scopes, ils apparaîtront dans la documentation de lAPI lorsque vous vous authentifiez/autorisez.
Et vous pourrez sélectionner à quels scopes vous souhaitez accorder laccès : `me` et `items`.
Cest le même mécanisme utilisé lorsque vous donnez des permissions en vous connectant avec Facebook, Google, GitHub, etc. :
<img src="/img/tutorial/security/image11.png">
## Jeton JWT avec scopes { #jwt-token-with-scopes }
Modifiez maintenant le *chemin daccès* du jeton pour renvoyer les scopes demandés.
Nous utilisons toujours le même `OAuth2PasswordRequestForm`. Il inclut une propriété `scopes` avec une `list` de `str`, contenant chaque scope reçu dans la requête.
Et nous renvoyons les scopes comme partie du jeton JWT.
/// danger | Danger
Pour simplifier, ici nous ajoutons directement au jeton les scopes reçus.
Mais dans votre application, pour la sécurité, vous devez vous assurer de najouter que les scopes que lutilisateur est réellement autorisé à avoir, ou ceux que vous avez prédéfinis.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Déclarer des scopes dans les chemins daccès et les dépendances { #declare-scopes-in-path-operations-and-dependencies }
Nous déclarons maintenant que le *chemin daccès* `/users/me/items/` nécessite le scope `items`.
Pour cela, nous importons et utilisons `Security` depuis `fastapi`.
Vous pouvez utiliser `Security` pour déclarer des dépendances (comme `Depends`), mais `Security` reçoit aussi un paramètre `scopes` avec une liste de scopes (chaînes).
Dans ce cas, nous passons une fonction de dépendance `get_current_active_user` à `Security` (de la même manière que nous le ferions avec `Depends`).
Mais nous passons aussi une `list` de scopes, ici avec un seul scope : `items` (il pourrait y en avoir plus).
Et la fonction de dépendance `get_current_active_user` peut également déclarer des sous-dépendances, non seulement avec `Depends` mais aussi avec `Security`. En déclarant sa propre fonction de sous-dépendance (`get_current_user`), et davantage dexigences de scopes.
Dans ce cas, elle nécessite le scope `me` (elle pourrait en exiger plusieurs).
/// note | Remarque
Vous navez pas nécessairement besoin dajouter des scopes différents à différents endroits.
Nous le faisons ici pour montrer comment **FastAPI** gère des scopes déclarés à différents niveaux.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Détails techniques
`Security` est en réalité une sous-classe de `Depends`, et elle na quun paramètre supplémentaire que nous verrons plus tard.
Mais en utilisant `Security` au lieu de `Depends`, **FastAPI** saura quil peut déclarer des scopes de sécurité, les utiliser en interne et documenter lAPI avec OpenAPI.
Cependant, lorsque vous importez `Query`, `Path`, `Depends`, `Security` et dautres depuis `fastapi`, ce sont en fait des fonctions qui renvoient des classes spéciales.
///
## Utiliser `SecurityScopes` { #use-securityscopes }
Mettez maintenant à jour la dépendance `get_current_user`.
Cest celle utilisée par les dépendances ci-dessus.
Cest ici que nous utilisons le même schéma OAuth2 que nous avons créé auparavant, en le déclarant comme dépendance : `oauth2_scheme`.
Comme cette fonction de dépendance na pas elle-même dexigences de scope, nous pouvons utiliser `Depends` avec `oauth2_scheme`, nous navons pas à utiliser `Security` quand nous navons pas besoin de spécifier des scopes de sécurité.
Nous déclarons également un paramètre spécial de type `SecurityScopes`, importé de `fastapi.security`.
Cette classe `SecurityScopes` est similaire à `Request` (`Request` servait à obtenir directement lobjet requête).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Utiliser les `scopes` { #use-the-scopes }
Le paramètre `security_scopes` sera de type `SecurityScopes`.
Il aura une propriété `scopes` avec une liste contenant tous les scopes requis par lui-même et par toutes les dépendances qui lutilisent comme sous-dépendance. Cela signifie, tous les « dépendants » ... cela peut paraître déroutant, cest expliqué à nouveau plus bas.
Lobjet `security_scopes` (de classe `SecurityScopes`) fournit aussi un attribut `scope_str` avec une chaîne unique, contenant ces scopes séparés par des espaces (nous allons lutiliser).
Nous créons une `HTTPException` que nous pouvons réutiliser (`raise`) plus tard à plusieurs endroits.
Dans cette exception, nous incluons les scopes requis (le cas échéant) sous forme de chaîne séparée par des espaces (en utilisant `scope_str`). Nous plaçons cette chaîne contenant les scopes dans len-tête `WWW-Authenticate` (cela fait partie de la spécification).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Vérifier le `username` et la structure des données { #verify-the-username-and-data-shape }
Nous vérifions que nous obtenons un `username`, et extrayons les scopes.
Nous validons ensuite ces données avec le modèle Pydantic (en capturant lexception `ValidationError`), et si nous obtenons une erreur lors de la lecture du jeton JWT ou de la validation des données avec Pydantic, nous levons la `HTTPException` que nous avons créée auparavant.
Pour cela, nous mettons à jour le modèle Pydantic `TokenData` avec une nouvelle propriété `scopes`.
En validant les données avec Pydantic, nous pouvons nous assurer que nous avons, par exemple, exactement une `list` de `str` pour les scopes et un `str` pour le `username`.
Au lieu, par exemple, dun `dict`, ou autre chose, ce qui pourrait casser lapplication plus tard et constituer un risque de sécurité.
Nous vérifions également que nous avons un utilisateur avec ce nom dutilisateur, et sinon, nous levons la même exception que précédemment.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Vérifier les `scopes` { #verify-the-scopes }
Nous vérifions maintenant que tous les scopes requis, par cette dépendance et tous les dépendants (y compris les *chemins daccès*), sont inclus dans les scopes fournis dans le jeton reçu, sinon nous levons une `HTTPException`.
Pour cela, nous utilisons `security_scopes.scopes`, qui contient une `list` avec tous ces scopes en `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Arbre de dépendances et scopes { #dependency-tree-and-scopes }
Revoyons encore cet arbre de dépendances et les scopes.
Comme la dépendance `get_current_active_user` a une sous-dépendance `get_current_user`, le scope « me » déclaré dans `get_current_active_user` sera inclus dans la liste des scopes requis dans `security_scopes.scopes` passé à `get_current_user`.
Le *chemin daccès* lui-même déclare également un scope, « items », il sera donc aussi présent dans la liste `security_scopes.scopes` passée à `get_current_user`.
Voici à quoi ressemble la hiérarchie des dépendances et des scopes :
* Le *chemin daccès* `read_own_items` a :
* Des scopes requis `["items"]` avec la dépendance :
* `get_current_active_user` :
* La fonction de dépendance `get_current_active_user` a :
* Des scopes requis `["me"]` avec la dépendance :
* `get_current_user` :
* La fonction de dépendance `get_current_user` a :
* Aucun scope requis par elle-même.
* Une dépendance utilisant `oauth2_scheme`.
* Un paramètre `security_scopes` de type `SecurityScopes` :
* Ce paramètre `security_scopes` a une propriété `scopes` avec une `list` contenant tous les scopes déclarés ci-dessus, donc :
* `security_scopes.scopes` contiendra `["me", "items"]` pour le *chemin daccès* `read_own_items`.
* `security_scopes.scopes` contiendra `["me"]` pour le *chemin daccès* `read_users_me`, car il est déclaré dans la dépendance `get_current_active_user`.
* `security_scopes.scopes` contiendra `[]` (rien) pour le *chemin daccès* `read_system_status`, car il na déclaré aucun `Security` avec des `scopes`, et sa dépendance, `get_current_user`, ne déclare pas non plus de `scopes`.
/// tip | Astuce
Lélément important et « magique » ici est que `get_current_user` aura une liste différente de `scopes` à vérifier pour chaque *chemin daccès*.
Tout dépend des `scopes` déclarés dans chaque *chemin daccès* et chaque dépendance dans larbre de dépendances pour ce *chemin daccès* spécifique.
///
## Détails supplémentaires sur `SecurityScopes` { #more-details-about-securityscopes }
Vous pouvez utiliser `SecurityScopes` à nimporte quel endroit, et à de multiples endroits, il na pas besoin dêtre dans la dépendance « root ».
Il aura toujours les scopes de sécurité déclarés dans les dépendances `Security` actuelles et tous les dépendants pour **ce** *chemin daccès* spécifique et **cet** arbre de dépendances spécifique.
Comme `SecurityScopes` contient tous les scopes déclarés par les dépendants, vous pouvez lutiliser pour vérifier quun jeton possède les scopes requis dans une fonction de dépendance centrale, puis déclarer des exigences de scopes différentes dans différents *chemins daccès*.
Elles seront vérifiées indépendamment pour chaque *chemin daccès*.
## Tester { #check-it }
Si vous ouvrez la documentation de lAPI, vous pouvez vous authentifier et spécifier quels scopes vous voulez autoriser.
<img src="/img/tutorial/security/image11.png">
Si vous ne sélectionnez aucun scope, vous serez « authenticated », mais lorsque vous essayerez daccéder à `/users/me/` ou `/users/me/items/`, vous obtiendrez une erreur indiquant que vous navez pas suffisamment de permissions. Vous pourrez toujours accéder à `/status/`.
Et si vous sélectionnez le scope `me` mais pas le scope `items`, vous pourrez accéder à `/users/me/` mais pas à `/users/me/items/`.
Cest ce qui arriverait à une application tierce qui tenterait daccéder à lun de ces *chemins daccès* avec un jeton fourni par un utilisateur, selon le nombre de permissions que lutilisateur a accordées à lapplication.
## À propos des intégrations tierces { #about-third-party-integrations }
Dans cet exemple, nous utilisons le flux OAuth2 « password ».
Cest approprié lorsque nous nous connectons à notre propre application, probablement avec notre propre frontend.
Parce que nous pouvons lui faire confiance pour recevoir le `username` et le `password`, puisque nous le contrôlons.
Mais si vous construisez une application OAuth2 à laquelle dautres se connecteraient (c.-à-d., si vous construisez un fournisseur dauthentification équivalent à Facebook, Google, GitHub, etc.), vous devez utiliser lun des autres flux.
Le plus courant est le flux implicite.
Le plus sûr est le flux « code », mais il est plus complexe à implémenter car il nécessite plus détapes. Comme il est plus complexe, de nombreux fournisseurs finissent par recommander le flux implicite.
/// note | Remarque
Il est courant que chaque fournisseur dauthentification nomme ses flux différemment, pour en faire une partie de sa marque.
Mais au final, ils implémentent le même standard OAuth2.
///
**FastAPI** inclut des utilitaires pour tous ces flux dauthentification OAuth2 dans `fastapi.security.oauth2`.
## `Security` dans les dépendances du décorateur `dependencies` { #security-in-decorator-dependencies }
De la même manière que vous pouvez définir une `list` de `Depends` dans le paramètre `dependencies` du décorateur (comme expliqué dans [Dépendances dans les décorateurs de chemins daccès](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), vous pouvez aussi utiliser `Security` avec des `scopes` à cet endroit.

View File

@ -0,0 +1,302 @@
# Paramètres et variables d'environnement { #settings-and-environment-variables }
Dans de nombreux cas, votre application peut avoir besoin de paramètres ou de configurations externes, par exemple des clés secrètes, des identifiants de base de données, des identifiants pour des services d'e-mail, etc.
La plupart de ces paramètres sont variables (peuvent changer), comme les URL de base de données. Et beaucoup peuvent être sensibles, comme les secrets.
C'est pourquoi il est courant de les fournir via des variables d'environnement lues par l'application.
/// tip | Astuce
Pour comprendre les variables d'environnement, vous pouvez lire [Variables d'environnement](../environment-variables.md){.internal-link target=_blank}.
///
## Types et validation { #types-and-validation }
Ces variables d'environnement ne gèrent que des chaînes de texte, car elles sont externes à Python et doivent être compatibles avec d'autres programmes et le reste du système (et même avec différents systèmes d'exploitation, comme Linux, Windows, macOS).
Cela signifie que toute valeur lue en Python depuis une variable d'environnement sera une `str`, et toute conversion vers un autre type ou toute validation doit être effectuée dans le code.
## Pydantic `Settings` { #pydantic-settings }
Heureusement, Pydantic fournit un excellent utilitaire pour gérer ces paramètres provenant des variables d'environnement avec <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic : gestion des paramètres</a>.
### Installer `pydantic-settings` { #install-pydantic-settings }
D'abord, vous devez créer votre [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, l'activer, puis installer le paquet `pydantic-settings` :
<div class="termy">
```console
$ pip install pydantic-settings
---> 100%
```
</div>
Il est également inclus lorsque vous installez les extras `all` avec :
<div class="termy">
```console
$ pip install "fastapi[all]"
---> 100%
```
</div>
### Créer l'objet `Settings` { #create-the-settings-object }
Importez `BaseSettings` depuis Pydantic et créez une sous-classe, comme pour un modèle Pydantic.
De la même manière qu'avec les modèles Pydantic, vous déclarez des attributs de classe avec des annotations de type, et éventuellement des valeurs par défaut.
Vous pouvez utiliser toutes les mêmes fonctionnalités et outils de validation que pour les modèles Pydantic, comme différents types de données et des validations supplémentaires avec `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Astuce
Si vous voulez quelque chose à copier-coller rapidement, n'utilisez pas cet exemple, utilisez le dernier ci-dessous.
///
Ensuite, lorsque vous créez une instance de cette classe `Settings` (dans ce cas, l'objet `settings`), Pydantic lira les variables d'environnement de manière insensible à la casse, donc une variable en majuscules `APP_NAME` sera tout de même lue pour l'attribut `app_name`.
Il convertira ensuite et validera les données. Ainsi, lorsque vous utilisez cet objet `settings`, vous aurez des données des types que vous avez déclarés (par exemple, `items_per_user` sera un `int`).
### Utiliser `settings` { #use-the-settings }
Vous pouvez ensuite utiliser le nouvel objet `settings` dans votre application :
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Exécuter le serveur { #run-the-server }
Ensuite, vous exécutez le serveur en passant les configurations comme variables d'environnement ; par exemple, vous pouvez définir un `ADMIN_EMAIL` et `APP_NAME` avec :
<div class="termy">
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
/// tip | Astuce
Pour définir plusieurs variables d'environnement pour une seule commande, séparez-les simplement par un espace et placez-les toutes avant la commande.
///
Ainsi, le paramètre `admin_email` sera défini sur « deadpool@example.com ».
Le `app_name` sera « ChimichangApp ».
Et `items_per_user` conservera sa valeur par défaut de `50`.
## Paramètres dans un autre module { #settings-in-another-module }
Vous pouvez placer ces paramètres dans un autre module comme vous l'avez vu dans [Applications plus grandes - Plusieurs fichiers](../tutorial/bigger-applications.md){.internal-link target=_blank}.
Par exemple, vous pourriez avoir un fichier `config.py` avec :
{* ../../docs_src/settings/app01_py310/config.py *}
Puis l'utiliser dans un fichier `main.py` :
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Astuce
Vous aurez également besoin d'un fichier `__init__.py` comme vous l'avez vu dans [Applications plus grandes - Plusieurs fichiers](../tutorial/bigger-applications.md){.internal-link target=_blank}.
///
## Paramètres dans une dépendance { #settings-in-a-dependency }
Dans certains cas, il peut être utile de fournir les paramètres via une dépendance, au lieu d'avoir un objet global `settings` utilisé partout.
Cela peut être particulièrement utile pendant les tests, car il est très facile de surcharger une dépendance avec vos propres paramètres personnalisés.
### Le fichier de configuration { #the-config-file }
En repartant de l'exemple précédent, votre fichier `config.py` pourrait ressembler à :
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Notez que maintenant, nous ne créons pas d'instance par défaut `settings = Settings()`.
### Le fichier principal de l'application { #the-main-app-file }
Nous créons maintenant une dépendance qui renvoie un nouveau `config.Settings()`.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Astuce
Nous parlerons de `@lru_cache` dans un instant.
Pour l'instant, vous pouvez supposer que `get_settings()` est une fonction normale.
///
Nous pouvons ensuite l'exiger depuis la fonction de chemin d'accès comme dépendance et l'utiliser où nous en avons besoin.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Paramètres et tests { #settings-and-testing }
Il devient alors très simple de fournir un autre objet de paramètres pendant les tests en créant une surcharge de dépendance pour `get_settings` :
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
Dans la surcharge de dépendance, nous définissons une nouvelle valeur pour `admin_email` lors de la création du nouvel objet `Settings`, puis nous renvoyons ce nouvel objet.
Nous pouvons ensuite tester qu'il est bien utilisé.
## Lire un fichier `.env` { #reading-a-env-file }
Si vous avez de nombreux paramètres susceptibles de beaucoup changer, peut-être selon les environnements, il peut être utile de les placer dans un fichier, puis de les lire comme s'il s'agissait de variables d'environnement.
Cette pratique est suffisamment courante pour avoir un nom ; ces variables d'environnement sont fréquemment placées dans un fichier `.env`, et le fichier est appelé un « dotenv ».
/// tip | Astuce
Un fichier commençant par un point (`.`) est un fichier caché dans les systèmes de type Unix, comme Linux et macOS.
Mais un fichier dotenv n'a pas forcément exactement ce nom de fichier.
///
Pydantic prend en charge la lecture depuis ce type de fichiers en utilisant une bibliothèque externe. Vous pouvez en lire davantage ici : <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings : prise en charge de Dotenv (.env)</a>.
/// tip | Astuce
Pour que cela fonctionne, vous devez exécuter `pip install python-dotenv`.
///
### Le fichier `.env` { #the-env-file }
Vous pouvez avoir un fichier `.env` avec :
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Lire les paramètres depuis `.env` { #read-settings-from-env }
Puis mettre à jour votre `config.py` avec :
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Astuce
L'attribut `model_config` est utilisé uniquement pour la configuration Pydantic. Vous pouvez en lire davantage ici : <a href="https://docs.pydantic.dev/latest/concepts/config/" class="external-link" target="_blank">Pydantic : Concepts : Configuration</a>.
///
Ici, nous définissons la configuration `env_file` à l'intérieur de votre classe Pydantic `Settings` et lui attribuons le nom du fichier dotenv que nous voulons utiliser.
### Créer `Settings` une seule fois avec `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Lire un fichier depuis le disque est normalement une opération coûteuse (lente), vous voudrez donc probablement le faire une seule fois puis réutiliser le même objet de paramètres, au lieu de le lire à chaque requête.
Mais chaque fois que nous faisons :
```Python
Settings()
```
un nouvel objet `Settings` serait créé, et à sa création il lirait à nouveau le fichier `.env`.
Si la fonction de dépendance était simplement :
```Python
def get_settings():
return Settings()
```
nous créerions cet objet pour chaque requête, et nous lirions le fichier `.env` pour chaque requête. ⚠️
Mais comme nous utilisons le décorateur `@lru_cache` au-dessus, l'objet `Settings` sera créé une seule fois, la première fois qu'il est appelé. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Ensuite, pour tout appel ultérieur de `get_settings()` dans les dépendances pour les requêtes suivantes, au lieu d'exécuter le code interne de `get_settings()` et de créer un nouvel objet `Settings`, il renverra le même objet que celui retourné au premier appel, encore et encore.
#### Détails techniques de `lru_cache` { #lru-cache-technical-details }
`@lru_cache` modifie la fonction qu'il décore pour renvoyer la même valeur que celle qui a été retournée la première fois, au lieu de la recalculer en exécutant le code de la fonction à chaque fois.
Ainsi, la fonction située en dessous sera exécutée une fois pour chaque combinaison d'arguments. Ensuite, les valeurs renvoyées par chacune de ces combinaisons d'arguments seront réutilisées à chaque fois que la fonction sera appelée avec exactement la même combinaison d'arguments.
Par exemple, si vous avez une fonction :
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
votre programme pourrait s'exécuter comme ceci :
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
Dans le cas de notre dépendance `get_settings()`, la fonction ne prend même aucun argument, elle renvoie donc toujours la même valeur.
De cette façon, elle se comporte presque comme s'il s'agissait simplement d'une variable globale. Mais comme elle utilise une fonction de dépendance, nous pouvons alors la surcharger facilement pour les tests.
`@lru_cache` fait partie de `functools` qui fait partie de la bibliothèque standard de Python, vous pouvez en lire davantage dans la <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">documentation Python pour `@lru_cache`</a>.
## Récapitulatif { #recap }
Vous pouvez utiliser Pydantic Settings pour gérer les paramètres ou configurations de votre application, avec toute la puissance des modèles Pydantic.
* En utilisant une dépendance, vous pouvez simplifier les tests.
* Vous pouvez utiliser des fichiers `.env`.
* Utiliser `@lru_cache` vous permet d'éviter de relire le fichier dotenv à chaque requête, tout en vous permettant de le surcharger pendant les tests.

View File

@ -0,0 +1,67 @@
# Sous-applications - Montage { #sub-applications-mounts }
Si vous avez besoin de deux applications FastAPI indépendantes, avec leur propre OpenAPI et leurs propres interfaces de la documentation, vous pouvez avoir une application principale et « monter » une (ou plusieurs) sousapplication(s).
## Monter une application **FastAPI** { #mounting-a-fastapi-application }
« Monter » signifie ajouter une application entièrement « indépendante » à un chemin spécifique, qui se chargera ensuite de tout gérer sous ce chemin, avec les _chemins d'accès_ déclarés dans cette sousapplication.
### Application de premier niveau { #top-level-application }
Créez d'abord l'application **FastAPI** principale (de premier niveau) et ses *chemins d'accès* :
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Sous-application { #sub-application }
Ensuite, créez votre sousapplication et ses *chemins d'accès*.
Cette sousapplication est simplement une autre application FastAPI standard, mais c'est celle qui sera « montée » :
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Monter la sous-application { #mount-the-sub-application }
Dans votre application de premier niveau, `app`, montez la sousapplication, `subapi`.
Dans ce cas, elle sera montée au chemin `/subapi` :
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Vérifier la documentation API automatique { #check-the-automatic-api-docs }
Exécutez maintenant la commande `fastapi` avec votre fichier :
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Puis ouvrez la documentation à <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Vous verrez la documentation API automatique pour l'application principale, n'incluant que ses propres _chemins d'accès_ :
<img src="/img/tutorial/sub-applications/image01.png">
Ensuite, ouvrez la documentation de la sousapplication à <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>.
Vous verrez la documentation API automatique pour la sousapplication, n'incluant que ses propres _chemins d'accès_, tous sous le préfixe de souschemin correct `/subapi` :
<img src="/img/tutorial/sub-applications/image02.png">
Si vous essayez d'interagir avec l'une ou l'autre des deux interfaces, elles fonctionneront correctement, car le navigateur pourra communiquer avec chaque application ou sousapplication spécifique.
### Détails techniques : `root_path` { #technical-details-root-path }
Lorsque vous montez une sousapplication comme cidessus, FastAPI se charge de communiquer le chemin de montage à la sousapplication au moyen d'un mécanisme de la spécification ASGI appelé `root_path`.
De cette manière, la sousapplication saura utiliser ce préfixe de chemin pour l'interface de documentation.
La sousapplication peut également avoir ses propres sousapplications montées et tout fonctionnera correctement, car FastAPI gère automatiquement tous ces `root_path`.
Vous en apprendrez davantage sur `root_path` et sur la façon de l'utiliser explicitement dans la section [Derrière un proxy](behind-a-proxy.md){.internal-link target=_blank}.

View File

@ -0,0 +1,126 @@
# Templates { #templates }
Vous pouvez utiliser n'importe quel moteur de templates avec **FastAPI**.
Un choix courant est Jinja2, le même que celui utilisé par Flask et d'autres outils.
Il existe des utilitaires pour le configurer facilement que vous pouvez utiliser directement dans votre application **FastAPI** (fournis par Starlette).
## Installer les dépendances { #install-dependencies }
Vous devez créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, l'activer, puis installer `jinja2` :
<div class="termy">
```console
$ pip install jinja2
---> 100%
```
</div>
## Utiliser `Jinja2Templates` { #using-jinja2templates }
- Importez `Jinja2Templates`.
- Créez un objet `templates` que vous pourrez réutiliser par la suite.
- Déclarez un paramètre `Request` dans le *chemin d'accès* qui renverra un template.
- Utilisez l'objet `templates` que vous avez créé pour rendre et retourner une `TemplateResponse`, en transmettant le nom du template, l'objet de requête et un dictionnaire de « context » avec des paires clé-valeur à utiliser dans le template Jinja2.
{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
/// note | Remarque
Avant FastAPI 0.108.0 et Starlette 0.29.0, `name` était le premier paramètre.
De plus, auparavant, dans les versions précédentes, l'objet `request` faisait partie des paires clé-valeur du contexte pour Jinja2.
///
/// tip | Astuce
En déclarant `response_class=HTMLResponse`, l'interface de la documentation saura que la réponse sera en HTML.
///
/// note | Détails techniques
Vous pouvez aussi utiliser `from starlette.templating import Jinja2Templates`.
**FastAPI** expose le même `starlette.templating` sous `fastapi.templating` par simple commodité pour vous, développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette. C'est également le cas pour `Request` et `StaticFiles`.
///
## Écrire des templates { #writing-templates }
Vous pouvez ensuite écrire un template dans `templates/item.html`, par exemple :
```jinja hl_lines="7"
{!../../docs_src/templates/templates/item.html!}
```
### Valeurs de contexte du template { #template-context-values }
Dans le HTML qui contient :
{% raw %}
```jinja
Item ID: {{ id }}
```
{% endraw %}
... il affichera l`id` récupéré à partir du `dict` « context » que vous avez passé :
```Python
{"id": id}
```
Par exemple, avec un ID de `42`, cela rendrait :
```html
Item ID: 42
```
### Arguments de `url_for` dans le template { #template-url-for-arguments }
Vous pouvez aussi utiliser `url_for()` dans le template ; elle prend en paramètres les mêmes arguments que ceux utilisés par votre *fonction de chemin d'accès*.
Ainsi, la section suivante :
{% raw %}
```jinja
<a href="{{ url_for('read_item', id=id) }}">
```
{% endraw %}
... générera un lien vers la même URL que celle gérée par la *fonction de chemin d'accès* `read_item(id=id)`.
Par exemple, avec un ID de `42`, cela rendrait :
```html
<a href="/items/42">
```
## Templates et fichiers statiques { #templates-and-static-files }
Vous pouvez aussi utiliser `url_for()` dans le template, par exemple avec les `StaticFiles` que vous avez montés avec `name="static"`.
```jinja hl_lines="4"
{!../../docs_src/templates/templates/item.html!}
```
Dans cet exemple, cela créera un lien vers un fichier CSS `static/styles.css` avec :
```CSS hl_lines="4"
{!../../docs_src/templates/static/styles.css!}
```
Et comme vous utilisez `StaticFiles`, ce fichier CSS est servi automatiquement par votre application **FastAPI** à lURL `/static/styles.css`.
## En savoir plus { #more-details }
Pour plus de détails, y compris sur la façon de tester des templates, consultez <a href="https://www.starlette.dev/templates/" class="external-link" target="_blank">la documentation de Starlette sur les templates</a>.

View File

@ -0,0 +1,52 @@
# Tester des dépendances avec des surcharges { #testing-dependencies-with-overrides }
## Surcharger des dépendances pendant les tests { #overriding-dependencies-during-testing }
Il existe des cas où vous souhaiterez surcharger une dépendance pendant les tests.
Vous ne voulez pas exécuter la dépendance originale (ni ses éventuelles sousdépendances).
À la place, vous souhaitez fournir une dépendance différente, utilisée uniquement pendant les tests (éventuellement seulement pour certains tests), et qui fournira une valeur utilisable partout où lon utilisait celle de la dépendance originale.
### Cas dusage : service externe { #use-cases-external-service }
Par exemple, vous avez un fournisseur dauthentification externe à appeler.
Vous lui envoyez un token et il renvoie un utilisateur authentifié.
Ce fournisseur peut vous facturer à la requête, et lappeler peut prendre plus de temps que si vous aviez un utilisateur factice fixe pour les tests.
Vous voudrez probablement tester le fournisseur externe une fois, mais pas nécessairement lappeler pour chaque test exécuté.
Dans ce cas, vous pouvez surcharger la dépendance qui appelle ce fournisseur et utiliser une dépendance personnalisée qui renvoie un utilisateur factice, uniquement pour vos tests.
### Utiliser lattribut `app.dependency_overrides` { #use-the-app-dependency-overrides-attribute }
Pour ces cas, votre **FastAPI** application possède un attribut `app.dependency_overrides` ; cest un simple `dict`.
Pour surcharger une dépendance lors des tests, vous mettez comme clé la dépendance originale (une fonction) et comme valeur votre surcharge de dépendance (une autre fonction).
Ensuite, **FastAPI** appellera cette surcharge au lieu de la dépendance originale.
{* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *}
/// tip | Astuce
Vous pouvez définir une surcharge de dépendance pour une dépendance utilisée nimporte où dans votre application **FastAPI**.
La dépendance originale peut être utilisée dans une fonction de chemin d'accès, un décorateur de chemin d'accès (quand vous nutilisez pas la valeur de retour), un appel à `.include_router()`, etc.
FastAPI pourra toujours la surcharger.
///
Vous pouvez ensuite réinitialiser vos surcharges (les supprimer) en affectant à `app.dependency_overrides` un `dict` vide :
```Python
app.dependency_overrides = {}
```
/// tip | Astuce
Si vous souhaitez surcharger une dépendance uniquement pendant certains tests, vous pouvez définir la surcharge au début du test (dans la fonction de test) et la réinitialiser à la fin (à la fin de la fonction de test).
///

View File

@ -0,0 +1,11 @@
# Tester les événements : lifespan et startup - shutdown { #testing-events-lifespan-and-startup-shutdown }
Lorsque vous avez besoin d'exécuter `lifespan` dans vos tests, vous pouvez utiliser `TestClient` avec une instruction `with` :
{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
Vous pouvez lire plus de détails dans [« Exécuter lifespan dans les tests sur le site de documentation officiel de Starlette. »](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
Pour les événements dépréciés `startup` et `shutdown`, vous pouvez utiliser le `TestClient` comme suit :
{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}

View File

@ -0,0 +1,13 @@
# Tester les WebSockets { #testing-websockets }
Vous pouvez utiliser le même `TestClient` pour tester les WebSockets.
Pour cela, vous utilisez `TestClient` dans une instruction `with`, en vous connectant au WebSocket :
{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
/// note | Remarque
Pour plus de détails, consultez la documentation de Starlette sur le <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">test des WebSockets</a>.
///

View File

@ -0,0 +1,56 @@
# Utiliser Request directement { #using-the-request-directly }
Jusqu'à présent, vous avez déclaré les parties de la requête dont vous avez besoin, avec leurs types.
En récupérant des données depuis :
* Le chemin, sous forme de paramètres.
* En-têtes.
* Cookies.
* etc.
Et ce faisant, **FastAPI** valide ces données, les convertit et génère automatiquement la documentation de votre API.
Mais il existe des situations où vous pouvez avoir besoin d'accéder directement à l'objet `Request`.
## Détails sur l'objet `Request` { #details-about-the-request-object }
Comme **FastAPI** est en fait **Starlette** en dessous, avec une couche de plusieurs outils au-dessus, vous pouvez utiliser directement l'objet <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`</a> de Starlette lorsque vous en avez besoin.
Cela signifie aussi que si vous récupérez des données directement à partir de l'objet `Request` (par exemple, lire le corps), elles ne seront pas validées, converties ni documentées (avec OpenAPI, pour l'interface utilisateur automatique de l'API) par FastAPI.
En revanche, tout autre paramètre déclaré normalement (par exemple, le corps avec un modèle Pydantic) sera toujours validé, converti, annoté, etc.
Mais il existe des cas spécifiques où il est utile d'obtenir l'objet `Request`.
## Utiliser l'objet `Request` directement { #use-the-request-object-directly }
Imaginons que vous souhaitiez obtenir l'adresse IP/l'hôte du client dans votre fonction de chemin d'accès.
Pour cela, vous devez accéder directement à la requête.
{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
En déclarant un paramètre de fonction de chemin d'accès de type `Request`, **FastAPI** saura passer la `Request` dans ce paramètre.
/// tip | Astuce
Notez que, dans ce cas, nous déclarons un paramètre de chemin en plus du paramètre de requête.
Ainsi, le paramètre de chemin sera extrait, validé, converti vers le type spécifié et annoté avec OpenAPI.
De la même façon, vous pouvez déclarer tout autre paramètre normalement, et en plus, obtenir aussi la `Request`.
///
## Documentation de `Request` { #request-documentation }
Vous pouvez lire plus de détails sur <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">l'objet `Request` sur le site de documentation officiel de Starlette</a>.
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.requests import Request`.
**FastAPI** le fournit directement pour votre commodité, en tant que développeur. Mais il provient directement de Starlette.
///

View File

@ -0,0 +1,186 @@
# WebSockets { #websockets }
Vous pouvez utiliser <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">API WebSockets</a> avec **FastAPI**.
## Installer `websockets` { #install-websockets }
Vous devez créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, l'activer, et installer `websockets` (une bibliothèque Python qui facilite l'utilisation du protocole « WebSocket ») :
<div class="termy">
```console
$ pip install websockets
---> 100%
```
</div>
## Client WebSocket { #websockets-client }
### En production { #in-production }
Dans votre système de production, vous avez probablement un frontend créé avec un framework moderne comme React, Vue.js ou Angular.
Et pour communiquer en utilisant WebSockets avec votre backend, vous utiliseriez probablement les outils fournis par votre frontend.
Ou vous pouvez avoir une application mobile native qui communique directement avec votre backend WebSocket, en code natif.
Ou vous pouvez avoir toute autre façon de communiquer avec l'endpoint WebSocket.
---
Mais pour cet exemple, nous utiliserons un document HTML très simple avec un peu de JavaScript, le tout dans une longue chaîne.
Cela, bien entendu, n'est pas optimal et vous ne l'utiliseriez pas en production.
En production, vous auriez l'une des options ci-dessus.
Mais c'est la façon la plus simple de se concentrer sur la partie serveur des WebSockets et d'avoir un exemple fonctionnel :
{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
## Créer un `websocket` { #create-a-websocket }
Dans votre application **FastAPI**, créez un `websocket` :
{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.websockets import WebSocket`.
**FastAPI** fournit le même `WebSocket` directement, simplement pour vous faciliter la vie en tant que développeur. Mais il provient directement de Starlette.
///
## Attendre des messages et envoyer des messages { #await-for-messages-and-send-messages }
Dans votre route WebSocket, vous pouvez `await` des messages et envoyer des messages.
{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
Vous pouvez recevoir et envoyer des données binaires, texte et JSON.
## Essayer { #try-it }
Si votre fichier s'appelle `main.py`, exécutez votre application avec :
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Ouvrez votre navigateur à l'adresse <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Vous verrez une page simple comme :
<img src="/img/tutorial/websockets/image01.png">
Vous pouvez saisir des messages dans le champ de saisie et les envoyer :
<img src="/img/tutorial/websockets/image02.png">
Et votre application **FastAPI** avec WebSockets vous répondra :
<img src="/img/tutorial/websockets/image03.png">
Vous pouvez envoyer (et recevoir) de nombreux messages :
<img src="/img/tutorial/websockets/image04.png">
Et tous utiliseront la même connexion WebSocket.
## Utiliser `Depends` et autres { #using-depends-and-others }
Dans les endpoints WebSocket, vous pouvez importer depuis `fastapi` et utiliser :
* `Depends`
* `Security`
* `Cookie`
* `Header`
* `Path`
* `Query`
Ils fonctionnent de la même manière que pour les autres endpoints/*chemins d'accès* FastAPI :
{* ../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82] *}
/// info
Comme il s'agit d'un WebSocket, il n'est pas vraiment logique de lever une `HTTPException`, nous levons plutôt une `WebSocketException`.
Vous pouvez utiliser un code de fermeture parmi les <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">codes valides définis dans la spécification</a>.
///
### Essayez les WebSockets avec des dépendances { #try-the-websockets-with-dependencies }
Si votre fichier s'appelle `main.py`, exécutez votre application avec :
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Ouvrez votre navigateur à l'adresse <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Là, vous pouvez définir :
* « Item ID », utilisé dans le chemin.
* « Token » utilisé comme paramètre de requête.
/// tip | Astuce
Notez que le `token` de requête sera géré par une dépendance.
///
Avec cela, vous pouvez connecter le WebSocket puis envoyer et recevoir des messages :
<img src="/img/tutorial/websockets/image05.png">
## Gérer les déconnexions et plusieurs clients { #handling-disconnections-and-multiple-clients }
Lorsqu'une connexion WebSocket est fermée, l'instruction `await websocket.receive_text()` lèvera une exception `WebSocketDisconnect`, que vous pouvez ensuite intercepter et gérer comme dans cet exemple.
{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
Pour l'essayer :
* Ouvrez l'application dans plusieurs onglets du navigateur.
* Écrivez des messages depuis ceux-ci.
* Puis fermez l'un des onglets.
Cela lèvera l'exception `WebSocketDisconnect`, et tous les autres clients recevront un message comme :
```
Client #1596980209979 left the chat
```
/// tip | Astuce
L'application ci-dessus est un exemple minimal et simple pour montrer comment gérer et diffuser des messages à plusieurs connexions WebSocket.
Mais gardez à l'esprit que, comme tout est géré en mémoire, dans une seule liste, cela ne fonctionnera que tant que le processus s'exécute et uniquement avec un seul processus.
Si vous avez besoin de quelque chose de facile à intégrer avec FastAPI mais plus robuste, pris en charge par Redis, PostgreSQL ou autres, consultez <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>.
///
## Plus d'informations { #more-info }
Pour en savoir plus sur les options, consultez la documentation de Starlette concernant :
* <a href="https://www.starlette.dev/websockets/" class="external-link" target="_blank">La classe `WebSocket`</a>.
* <a href="https://www.starlette.dev/endpoints/#websocketendpoint" class="external-link" target="_blank">Gestion des WebSocket basée sur des classes</a>.

View File

@ -0,0 +1,51 @@
# Inclure WSGI - Flask, Django, autres { #including-wsgi-flask-django-others }
Vous pouvez monter des applications WSGI comme vous l'avez vu avec [Sous-applications - Montages](sub-applications.md){.internal-link target=_blank}, [Derrière un proxy](behind-a-proxy.md){.internal-link target=_blank}.
Pour cela, vous pouvez utiliser `WSGIMiddleware` et l'utiliser pour envelopper votre application WSGI, par exemple Flask, Django, etc.
## Utiliser `WSGIMiddleware` { #using-wsgimiddleware }
/// info
Cela nécessite l'installation de `a2wsgi`, par exemple avec `pip install a2wsgi`.
///
Vous devez importer `WSGIMiddleware` depuis `a2wsgi`.
Ensuite, enveloppez l'application WSGI (par ex. Flask) avec le middleware.
Puis, montez-la sous un chemin.
{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
/// note | Remarque
Auparavant, il était recommandé d'utiliser `WSGIMiddleware` depuis `fastapi.middleware.wsgi`, mais il est désormais déprécié.
Il est conseillé d'utiliser le package `a2wsgi` à la place. L'utilisation reste la même.
Assurez-vous simplement que le package `a2wsgi` est installé et importez `WSGIMiddleware` correctement depuis `a2wsgi`.
///
## Vérifiez { #check-it }
Désormais, chaque requête sous le chemin `/v1/` sera gérée par l'application Flask.
Et le reste sera géré par **FastAPI**.
Si vous l'exécutez et allez à <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a>, vous verrez la réponse de Flask :
```txt
Hello, World from Flask!
```
Et si vous allez à <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a>, vous verrez la réponse de FastAPI :
```JSON
{
"message": "Hello World"
}
```

View File

@ -1,8 +1,8 @@
# Alternatives, inspiration et comparaisons
# Alternatives, inspiration et comparaisons { #alternatives-inspiration-and-comparisons }
Ce qui a inspiré **FastAPI**, comment il se compare à d'autres solutions et ce qu'il en a appris.
## Intro
## Intro { #intro }
**FastAPI** n'existerait pas sans les précédentes contributions d'autres projets.
@ -13,11 +13,11 @@ fonctionnalités couvertes par **FastAPI** en utilisant de nombreux frameworks,
Mais à un moment donné il n'y avait pas d'autre option que de créer quelque chose qui offrait toutes ces
fonctionnalités, en reprenant et en combinant de la meilleure façon possible les meilleures idées des outils
précédents, en utilisant des fonctionnalités du langage qui n'étaient même pas disponibles auparavant (type hints depuis Python 3.6+).
précédents, en utilisant des fonctionnalités du langage qui n'étaient même pas disponibles auparavant (annotations de type depuis Python 3.6+).
## Outils précédents
## Outils précédents { #previous-tools }
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a>
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a> { #django }
C'est le framework Python le plus populaire et il bénéficie d'une grande confiance. Il est utilisé pour construire
des systèmes tel qu'Instagram.
@ -26,18 +26,18 @@ Il est relativement fortement couplé aux bases de données relationnelles (comm
n'est pas très facile d'utiliser une base de données NoSQL (comme Couchbase, MongoDB, Cassandra, etc.) comme principal moyen de
stockage.
Il a été créé pour générer le HTML en backend, pas pour créer des API consommées par un frontend moderne (comme React, Vue.js et Angular) ou par d'autres systèmes (comme les appareils <abbr title="Internet of Things">IoT</abbr>) communiquant avec lui.
Il a été créé pour générer le HTML en backend, pas pour créer des API consommées par un frontend moderne (comme React, Vue.js et Angular) ou par d'autres systèmes (comme les appareils <abbr title="Internet of Things - Internet des objets">IoT</abbr>) communiquant avec lui.
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a>
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> { #django-rest-framework }
Django REST framework a été conçu comme une boîte à outils flexible permettant de construire des API Web à partir de Django, afin d'améliorer ses capacités en matière d'API.
Il est utilisé par de nombreuses entreprises, dont Mozilla, Red Hat et Eventbrite.
Il s'agissait de l'un des premiers exemples de **documentation automatique pour API**, et c'est précisément l'une des
premières idées qui a inspiré "la recherche de" **FastAPI**.
premières idées qui a inspiré « la recherche de » **FastAPI**.
/// note
/// note | Remarque
Django REST framework a été créé par Tom Christie. Le créateur de Starlette et Uvicorn, sur lesquels **FastAPI** est basé.
@ -49,9 +49,9 @@ Avoir une interface de documentation automatique de l'API.
///
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a>
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a> { #flask }
Flask est un "micro-framework", il ne comprend pas d'intégrations de bases de données ni beaucoup de choses qui sont fournies par défaut dans Django.
Flask est un « microframework », il ne comprend pas d'intégrations de bases de données ni beaucoup de choses qui sont fournies par défaut dans Django.
Cette simplicité et cette flexibilité permettent d'utiliser des bases de données NoSQL comme principal système de stockage de données.
@ -60,20 +60,20 @@ technique par moments.
Il est aussi couramment utilisé pour d'autres applications qui n'ont pas nécessairement besoin d'une base de données, de gestion des utilisateurs ou de l'une des nombreuses fonctionnalités préinstallées dans Django. Bien que beaucoup de ces fonctionnalités puissent être ajoutées avec des plug-ins.
Ce découplage des parties, et le fait d'être un "micro-framework" qui puisse être étendu pour couvrir exactement ce
Ce découplage des parties, et le fait d'être un « microframework » qui puisse être étendu pour couvrir exactement ce
qui est nécessaire, était une caractéristique clé que je voulais conserver.
Compte tenu de la simplicité de Flask, il semblait bien adapté à la création d'API. La prochaine chose à trouver était un "Django REST Framework" pour Flask.
Compte tenu de la simplicité de Flask, il semblait bien adapté à la création d'API. La prochaine chose à trouver était un « Django REST Framework » pour Flask.
/// check | A inspiré **FastAPI** à
Être un micro-framework. Il est donc facile de combiner les outils et les pièces nécessaires.
Être un microframework. Il est donc facile de combiner les outils et les pièces nécessaires.
Proposer un système de routage simple et facile à utiliser.
///
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a>
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a> { #requests }
**FastAPI** n'est pas réellement une alternative à **Requests**. Leur cadre est très différent.
@ -97,7 +97,7 @@ La façon dont vous l'utilisez est très simple. Par exemple, pour faire une req
response = requests.get("http://example.com/some/url")
```
En contrepartie l'API _des opérations de chemin_ de FastAPI pourrait ressembler à ceci :
Lopération de chemin d'accès correspondante dans **FastAPI** pourrait ressembler à ceci :
```Python hl_lines="1"
@app.get("/some/url")
@ -109,13 +109,13 @@ Notez les similitudes entre `requests.get(...)` et `@app.get(...)`.
/// check | A inspiré **FastAPI** à
Avoir une API simple et intuitive.
Utiliser les noms de méthodes HTTP (opérations) directement, de manière simple et intuitive. \* Avoir des valeurs par défaut raisonnables, mais des personnalisations puissantes.
* Avoir une API simple et intuitive.
* Utiliser les noms de méthodes HTTP (opérations) directement, de manière simple et intuitive.
* Avoir des valeurs par défaut raisonnables, mais des personnalisations puissantes.
///
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a>
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a> { #swagger-openapi }
La principale fonctionnalité que j'ai emprunté à Django REST Framework était la documentation automatique des API.
@ -126,7 +126,7 @@ Swagger pour une API permettrait d'utiliser cette interface utilisateur web auto
À un moment donné, Swagger a été cédé à la Fondation Linux, puis a été rebaptisé OpenAPI.
C'est pourquoi, lorsqu'on parle de la version 2.0, il est courant de dire "Swagger", et pour la version 3+ "OpenAPI".
C'est pourquoi, lorsqu'on parle de la version 2.0, il est courant de dire « Swagger », et pour la version 3+ « OpenAPI ».
/// check | A inspiré **FastAPI** à
@ -141,16 +141,15 @@ Ces deux-là ont été choisis parce qu'ils sont populaires et stables, mais en
///
### Frameworks REST pour Flask
### Frameworks REST pour Flask { #flask-rest-frameworks }
Il y a plusieurs frameworks REST pour Flask, mais après avoir investi du temps et du travail pour les étudier, j'ai
découvert que le développement de beaucoup d'entre eux sont suspendus ou abandonnés, avec plusieurs problèmes
permanents qui les rendent inadaptés.
### <a href="https://marshmallow.readthedocs.io/en/3.0/" class="external-link" target="_blank">Marshmallow</a>
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
L'une des principales fonctionnalités nécessaires aux systèmes API est la "<abbr title="également appelée
marshalling, conversion">sérialisation</abbr>" des données, qui consiste à prendre les données du code (Python) et à
L'une des principales fonctionnalités nécessaires aux systèmes API est la « <dfn title="aussi appelé : marshalling, conversion">sérialisation</dfn> » des données, qui consiste à prendre les données du code (Python) et à
les convertir en quelque chose qui peut être envoyé sur le réseau. Par exemple, convertir un objet contenant des
données provenant d'une base de données en un objet JSON. Convertir des objets `datetime` en strings, etc.
@ -163,19 +162,17 @@ Sans un système de validation des données, vous devriez effectuer toutes les v
Ces fonctionnalités sont ce pourquoi Marshmallow a été construit. C'est une excellente bibliothèque, et je l'ai déjà beaucoup utilisée.
Mais elle a été créée avant que les type hints n'existent en Python. Ainsi, pour définir chaque <abbr title="la définition de
la façon dont les données doivent être formées">schéma</abbr>, vous devez utiliser des utilitaires et des classes spécifiques fournies par Marshmallow.
Mais elle a été créée avant que les annotations de type n'existent en Python. Ainsi, pour définir chaque <dfn title="la définition de la façon dont les données doivent être formées">schéma</dfn>, vous devez utiliser des utilitaires et des classes spécifiques fournies par Marshmallow.
/// check | A inspiré **FastAPI** à
Utilisez du code pour définir des "schémas" qui fournissent automatiquement les types de données et la validation.
Utilisez du code pour définir des « schémas » qui fournissent automatiquement les types de données et la validation.
///
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a>
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
Une autre grande fonctionnalité requise par les API est le <abbr title="la lecture et la conversion en données
Python">parsing</abbr> des données provenant des requêtes entrantes.
Une autre grande fonctionnalité requise par les API est l<dfn title="lecture et conversion en données Python">analyse</dfn> des données provenant des requêtes entrantes.
Webargs est un outil qui a été créé pour fournir cela par-dessus plusieurs frameworks, dont Flask.
@ -195,7 +192,7 @@ Disposer d'une validation automatique des données des requêtes entrantes.
///
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a>
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> { #apispec }
Marshmallow et Webargs fournissent la validation, l'analyse et la sérialisation en tant que plug-ins.
@ -225,7 +222,7 @@ Supporter la norme ouverte pour les API, OpenAPI.
///
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a>
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a> { #flask-apispec }
C'est un plug-in pour Flask, qui relie Webargs, Marshmallow et APISpec.
@ -240,11 +237,11 @@ Cette combinaison de Flask, Flask-apispec avec Marshmallow et Webargs était ma
Son utilisation a conduit à la création de plusieurs générateurs Flask full-stack. Ce sont les principales stacks que
j'ai (ainsi que plusieurs équipes externes) utilisées jusqu'à présent :
- <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
- <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
- <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a>
* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a>
* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a>
Ces mêmes générateurs full-stack ont servi de base aux [Générateurs de projets pour **FastAPI**](project-generation.md){.internal-link target=\_blank}.
Ces mêmes générateurs full-stack ont servi de base aux [Générateurs de projets pour **FastAPI**](project-generation.md){.internal-link target=_blank}.
/// info
@ -258,15 +255,15 @@ Générer le schéma OpenAPI automatiquement, à partir du même code qui défin
///
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (et <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>)
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (et <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>) { #nestjs-and-angular }
Ce n'est même pas du Python, NestJS est un framework JavaScript (TypeScript) NodeJS inspiré d'Angular.
Il réalise quelque chose de similaire à ce qui peut être fait avec Flask-apispec.
Il possède un système d'injection de dépendances intégré, inspiré d'Angular 2. Il nécessite de pré-enregistrer les "injectables" (comme tous les autres systèmes d'injection de dépendances que je connais), donc, cela ajoute à la verbosité et à la répétition du code.
Il possède un système d'injection de dépendances intégré, inspiré d'Angular 2. Il nécessite de pré-enregistrer les « injectables » (comme tous les autres systèmes d'injection de dépendances que je connais), donc, cela ajoute à la verbosité et à la répétition du code.
Comme les paramètres sont décrits avec des types TypeScript (similaires aux type hints de Python), la prise en charge
Comme les paramètres sont décrits avec des types TypeScript (similaires aux annotations de type de Python), la prise en charge
par l'éditeur est assez bonne.
Mais comme les données TypeScript ne sont pas préservées après la compilation en JavaScript, il ne peut pas compter sur les types pour définir la validation, la sérialisation et la documentation en même temps. En raison de cela et de certaines décisions de conception, pour obtenir la validation, la sérialisation et la génération automatique de schémas, il est nécessaire d'ajouter des décorateurs à de nombreux endroits. Cela devient donc assez verbeux.
@ -281,7 +278,7 @@ Disposer d'un puissant système d'injection de dépendances. Trouver un moyen de
///
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a>
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a> { #sanic }
C'était l'un des premiers frameworks Python extrêmement rapides basés sur `asyncio`. Il a été conçu pour être très similaire à Flask.
@ -301,14 +298,12 @@ C'est pourquoi **FastAPI** est basé sur Starlette, car il s'agit du framework l
///
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a>
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a> { #falcon }
Falcon est un autre framework Python haute performance, il est conçu pour être minimal, et est utilisé comme fondation pour d'autres frameworks comme Hug.
Il utilise le standard précédent pour les frameworks web Python (WSGI) qui est synchrone, donc il ne peut pas gérer les WebSockets et d'autres cas d'utilisation. Néanmoins, il offre de très bonnes performances.
Il est conçu pour avoir des fonctions qui reçoivent deux paramètres, une "requête" et une "réponse". Ensuite, vous
"lisez" des parties de la requête et "écrivez" des parties dans la réponse. En raison de cette conception, il n'est
Il est conçu pour avoir des fonctions qui reçoivent deux paramètres, une « requête » et une « réponse ». Ensuite, vous
« lisez » des parties de la requête et « écrivez » des parties dans la réponse. En raison de cette conception, il n'est
pas possible de déclarer des paramètres de requête et des corps avec des indications de type Python standard comme paramètres de fonction.
Ainsi, la validation, la sérialisation et la documentation des données doivent être effectuées dans le code, et non pas automatiquement. Ou bien elles doivent être implémentées comme un framework au-dessus de Falcon, comme Hug. Cette même distinction se retrouve dans d'autres frameworks qui s'inspirent de la conception de Falcon, qui consiste à avoir un objet de requête et un objet de réponse comme paramètres.
@ -323,20 +318,20 @@ Bien que dans FastAPI, il est facultatif, et est utilisé principalement pour d
///
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a>
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a> { #molten }
J'ai découvert Molten lors des premières étapes de développement de **FastAPI**. Et il a des idées assez similaires :
- Basé sur les type hints Python.
- Validation et documentation via ces types.
- Système d'injection de dépendances.
* Basé sur les annotations de type Python.
* Validation et documentation via ces types.
* Système d'injection de dépendances.
Il n'utilise pas une librairie tiers de validation, sérialisation et de documentation tel que Pydantic, il utilise son propre système. Ainsi, ces définitions de types de données ne sont pas réutilisables aussi facilement.
Il nécessite une configuration un peu plus verbeuse. Et comme il est basé sur WSGI (au lieu dASGI), il n'est pas
Il nécessite une configuration un peu plus verbeuse. Et comme il est basé sur WSGI (au lieu d'ASGI), il n'est pas
conçu pour profiter des hautes performances fournies par des outils comme Uvicorn, Starlette et Sanic.
Le système d'injection de dépendances exige le pré-enregistrement des dépendances et les dépendances sont résolues sur la base des types déclarés. Ainsi, il n'est pas possible de déclarer plus d'un "composant" qui fournit un certain type.
Le système d'injection de dépendances exige le pré-enregistrement des dépendances et les dépendances sont résolues sur la base des types déclarés. Ainsi, il n'est pas possible de déclarer plus d'un « composant » qui fournit un certain type.
Les routes sont déclarées à un seul endroit, en utilisant des fonctions déclarées à d'autres endroits (au lieu
d'utiliser des décorateurs qui peuvent être placés juste au-dessus de la fonction qui gère l'endpoint). Cette
@ -345,15 +340,15 @@ qui sont relativement fortement couplées.
/// check | A inspiré **FastAPI** à
Définir des validations supplémentaires pour les types de données utilisant la valeur "par défaut" des attributs du modèle. Ceci améliore le support de l'éditeur, et n'était pas disponible dans Pydantic auparavant.
Définir des validations supplémentaires pour les types de données utilisant la valeur « par défaut » des attributs du modèle. Ceci améliore le support de l'éditeur, et n'était pas disponible dans Pydantic auparavant.
Cela a en fait inspiré la mise à jour de certaines parties de Pydantic, afin de supporter le même style de déclaration de validation (toute cette fonctionnalité est maintenant déjà disponible dans Pydantic).
///
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a>
### <a href="https://github.com/hugapi/hug" class="external-link" target="_blank">Hug</a> { #hug }
Hug a été l'un des premiers frameworks à implémenter la déclaration des types de paramètres d'API en utilisant les type hints Python. C'était une excellente idée qui a inspiré d'autres outils à faire de même.
Hug a été l'un des premiers frameworks à implémenter la déclaration des types de paramètres d'API en utilisant les annotations de type Python. C'était une excellente idée qui a inspiré d'autres outils à faire de même.
Il utilisait des types personnalisés dans ses déclarations au lieu des types Python standard, mais c'était tout de même un énorme pas en avant.
@ -372,28 +367,28 @@ Hug a été créé par Timothy Crosley, le créateur de <a href="https://github.
///
/// check | A inspiré **FastAPI** à
/// check | Idées ayant inspiré **FastAPI**
Hug a inspiré certaines parties d'APIStar, et était l'un des outils que je trouvais les plus prometteurs, à côté d'APIStar.
Hug a contribué à inspirer **FastAPI** pour utiliser les type hints Python
Hug a contribué à inspirer **FastAPI** pour utiliser les annotations de type Python
pour déclarer les paramètres, et pour générer automatiquement un schéma définissant l'API.
Hug a inspiré **FastAPI** pour déclarer un paramètre `response` dans les fonctions pour définir les en-têtes et les cookies.
///
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5)
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5) { #apistar-0-5 }
Juste avant de décider de développer **FastAPI**, j'ai trouvé le serveur **APIStar**. Il contenait presque tout ce
que je recherchais et avait un beau design.
C'était l'une des premières implémentations d'un framework utilisant les type hints Python pour déclarer les paramètres
C'était l'une des premières implémentations d'un framework utilisant les annotations de type Python pour déclarer les paramètres
et les requêtes que j'ai vues (avant NestJS et Molten). Je l'ai trouvé plus ou moins en même temps que Hug. Mais APIStar utilisait le standard OpenAPI.
Il disposait de la validation automatique, sérialisation des données et d'une génération de schéma OpenAPI basée sur les mêmes type hints à plusieurs endroits.
Il disposait de la validation automatique, sérialisation des données et d'une génération de schéma OpenAPI basée sur les mêmes annotations de type à plusieurs endroits.
La définition du schéma de corps de requête n'utilisait pas les mêmes type hints Python que Pydantic, il était un peu plus proche de Marshmallow, donc le support de l'éditeur n'était pas aussi bon, mais APIStar était quand même la meilleure option disponible.
La définition du schéma de corps de requête n'utilisait pas les mêmes annotations de type Python que Pydantic, il était un peu plus proche de Marshmallow, donc le support de l'éditeur n'était pas aussi bon, mais APIStar était quand même la meilleure option disponible.
Il avait les meilleures performances d'après les benchmarks de l'époque (seulement surpassé par Starlette).
@ -429,20 +424,20 @@ Et après avoir longtemps cherché un framework similaire et testé de nombreuse
Puis APIStar a cessé d'exister en tant que serveur et Starlette a été créé, et a constitué une meilleure base pour un tel système. Ce fut l'inspiration finale pour construire **FastAPI**.
Je considère **FastAPI** comme un "successeur spirituel" d'APIStar, tout en améliorant et en augmentant les fonctionnalités, le système de typage et d'autres parties, sur la base des enseignements tirés de tous ces outils précédents.
Je considère **FastAPI** comme un « successeur spirituel » d'APIStar, tout en améliorant et en augmentant les fonctionnalités, le système de typage et d'autres parties, sur la base des enseignements tirés de tous ces outils précédents.
///
## Utilisés par **FastAPI**
## Utilisés par **FastAPI** { #used-by-fastapi }
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>
### <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> { #pydantic }
Pydantic est une bibliothèque permettant de définir la validation, la sérialisation et la documentation des données (à l'aide de JSON Schema) en se basant sur les Python type hints.
Pydantic est une bibliothèque permettant de définir la validation, la sérialisation et la documentation des données (à l'aide de JSON Schema) en se basant sur les annotations de type Python.
Cela le rend extrêmement intuitif.
Il est comparable à Marshmallow. Bien qu'il soit plus rapide que Marshmallow dans les benchmarks. Et comme il est
basé sur les mêmes type hints Python, le support de l'éditeur est grand.
basé sur les mêmes annotations de type Python, le support de l'éditeur est grand.
/// check | **FastAPI** l'utilise pour
@ -452,9 +447,9 @@ Gérer toute la validation des données, leur sérialisation et la documentation
///
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a>
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
Starlette est un framework/toolkit léger <abbr title="Le nouveau standard pour construire des applications web asynchrones en Python">ASGI</abbr>, qui est idéal pour construire des services asyncio performants.
Starlette est un framework/toolkit léger <dfn title="La nouvelle norme pour créer des applications web Python asynchrones">ASGI</dfn>, qui est idéal pour construire des services asyncio performants.
Il est très simple et intuitif. Il est conçu pour être facilement extensible et avoir des composants modulaires.
@ -462,29 +457,28 @@ Il offre :
- Des performances vraiment impressionnantes.
- Le support des WebSockets.
- Le support de GraphQL.
- Les tâches d'arrière-plan.
- Les événements de démarrage et d'arrêt.
- Un client de test basé sur request.
- Un client de test basé sur HTTPX.
- CORS, GZip, fichiers statiques, streaming des réponses.
- Le support des sessions et des cookies.
- Une couverture de test à 100 %.
- 100 % de la base de code avec des annotations de type.
- Zéro forte dépendance à d'autres packages.
- Peu de dépendances strictes.
Starlette est actuellement le framework Python le plus rapide testé. Seulement dépassé par Uvicorn, qui n'est pas un framework, mais un serveur.
Starlette fournit toutes les fonctionnalités de base d'un micro-framework web.
Starlette fournit toutes les fonctionnalités de base d'un microframework web.
Mais il ne fournit pas de validation automatique des données, de sérialisation ou de documentation.
C'est l'une des principales choses que **FastAPI** ajoute par-dessus, le tout basé sur les type hints Python (en utilisant Pydantic). Cela, plus le système d'injection de dépendances, les utilitaires de sécurité, la génération de schémas OpenAPI, etc.
C'est l'une des principales choses que **FastAPI** ajoute par-dessus, le tout basé sur les annotations de type Python (en utilisant Pydantic). Cela, plus le système d'injection de dépendances, les utilitaires de sécurité, la génération de schémas OpenAPI, etc.
/// note | Détails techniques
ASGI est une nouvelle "norme" développée par les membres de l'équipe principale de Django. Il ne s'agit pas encore d'une "norme Python" (un PEP), bien qu'ils soient en train de le faire.
ASGI est une nouvelle « norme » développée par les membres de l'équipe principale de Django. Il ne s'agit pas encore d'une « norme Python » (un PEP), bien qu'ils soient en train de le faire.
Néanmoins, il est déjà utilisé comme "standard" par plusieurs outils. Cela améliore grandement l'interopérabilité, puisque vous pouvez remplacer Uvicorn par n'importe quel autre serveur ASGI (comme Daphne ou Hypercorn), ou vous pouvez ajouter des outils compatibles ASGI, comme `python-socketio`.
Néanmoins, il est déjà utilisé comme « standard » par plusieurs outils. Cela améliore grandement l'interopérabilité, puisque vous pouvez remplacer Uvicorn par n'importe quel autre serveur ASGI (comme Daphne ou Hypercorn), ou vous pouvez ajouter des outils compatibles ASGI, comme `python-socketio`.
///
@ -498,7 +492,7 @@ Ainsi, tout ce que vous pouvez faire avec Starlette, vous pouvez le faire direct
///
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>
### <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a> { #uvicorn }
Uvicorn est un serveur ASGI rapide comme l'éclair, basé sur uvloop et httptools.
@ -511,12 +505,12 @@ C'est le serveur recommandé pour Starlette et **FastAPI**.
Le serveur web principal pour exécuter les applications **FastAPI**.
Vous pouvez le combiner avec Gunicorn, pour avoir un serveur multi-processus asynchrone.
Vous pouvez également utiliser l'option de ligne de commande `--workers` pour avoir un serveur multiprocessus asynchrone.
Pour plus de détails, consultez la section [Déploiement](deployment/index.md){.internal-link target=_blank}.
///
## Benchmarks et vitesse
## Benchmarks et vitesse { #benchmarks-and-speed }
Pour comprendre, comparer et voir la différence entre Uvicorn, Starlette et FastAPI, consultez la section sur les [Benchmarks](benchmarks.md){.internal-link target=\_blank}.
Pour comprendre, comparer et voir la différence entre Uvicorn, Starlette et FastAPI, consultez la section sur les [Benchmarks](benchmarks.md){.internal-link target=_blank}.

View File

@ -1,17 +1,18 @@
# Concurrence et les mots-clés async et await
# Concurrence et async / await { #concurrency-and-async-await }
Cette page vise à fournir des détails sur la syntaxe `async def` pour les *fonctions de chemins* et quelques rappels sur le code asynchrone, la concurrence et le parallélisme.
Détails sur la syntaxe `async def` pour les *fonctions de chemin d'accès* et quelques rappels sur le code asynchrone, la concurrence et le parallélisme.
## Vous êtes pressés ?
## Vous êtes pressés ? { #in-a-hurry }
<abbr title="'too long; didn't read' en anglais, ou 'trop long ; j'ai pas lu'"><strong>TL;DR :</strong></abbr>
<abbr title="too long; didn't read - trop long ; pas lu"><strong>TL;DR :</strong></abbr>
Si vous utilisez des bibliothèques tierces qui nécessitent d'être appelées avec `await`, telles que :
```Python
results = await some_library()
```
Alors, déclarez vos *fonctions de chemins* avec `async def` comme ceci :
Alors, déclarez vos *fonctions de chemin d'accès* avec `async def` comme ceci :
```Python hl_lines="2"
@app.get('/')
@ -20,7 +21,7 @@ async def read_results():
return results
```
/// note
/// note | Remarque
Vous pouvez uniquement utiliser `await` dans les fonctions créées avec `async def`.
@ -28,7 +29,7 @@ Vous pouvez uniquement utiliser `await` dans les fonctions créées avec `async
---
Si vous utilisez une bibliothèque externe qui communique avec quelque chose (une BDD, une API, un système de fichiers, etc.) et qui ne supporte pas l'utilisation d'`await` (ce qui est actuellement le cas pour la majorité des bibliothèques de BDD), alors déclarez vos *fonctions de chemin* normalement, avec le classique `def`, comme ceci :
Si vous utilisez une bibliothèque externe qui communique avec quelque chose (une base de données, une API, le système de fichiers, etc.) et qui ne supporte pas l'utilisation d'`await` (ce qui est actuellement le cas pour la majorité des bibliothèques de base de données), alors déclarez vos *fonctions de chemin d'accès* normalement, avec le classique `def`, comme ceci :
```Python hl_lines="2"
@app.get('/')
@ -39,7 +40,7 @@ def results():
---
Si votre application n'a pas à communiquer avec une bibliothèque externe et pas à attendre de réponse, utilisez `async def`.
Si votre application n'a pas à communiquer avec une autre chose et à attendre sa réponse, utilisez `async def`, même si vous n'avez pas besoin d'utiliser `await` à l'intérieur.
---
@ -47,15 +48,15 @@ Si vous ne savez pas, utilisez seulement `def` comme vous le feriez habituelleme
---
**Note** : vous pouvez mélanger `def` et `async def` dans vos *fonctions de chemin* autant que nécessaire, **FastAPI** saura faire ce qu'il faut avec.
Note : vous pouvez mélanger `def` et `async def` dans vos *fonctions de chemin d'accès* autant que nécessaire, et définir chacune avec loption la plus adaptée pour vous. FastAPI fera ce qu'il faut avec elles.
Au final, peu importe le cas parmi ceux ci-dessus, **FastAPI** fonctionnera de manière asynchrone et sera extrêmement rapide.
Au final, peu importe le cas parmi ceux ci-dessus, FastAPI fonctionnera de manière asynchrone et sera extrêmement rapide.
Mais si vous suivez bien les instructions ci-dessus, alors **FastAPI** pourra effectuer quelques optimisations et ainsi améliorer les performances.
Mais si vous suivez bien les instructions ci-dessus, il pourra effectuer quelques optimisations et ainsi améliorer les performances.
## Détails techniques
## Détails techniques { #technical-details }
Les versions modernes de Python supportent le **code asynchrone** grâce aux **"coroutines"** avec les syntaxes **`async` et `await`**.
Les versions modernes de Python supportent le **code asynchrone** grâce aux **« coroutines »** avec les syntaxes **`async` et `await`**.
Analysons les différentes parties de cette phrase dans les sections suivantes :
@ -63,46 +64,46 @@ Analysons les différentes parties de cette phrase dans les sections suivantes :
* **`async` et `await`**
* **Coroutines**
## Code asynchrone
## Code asynchrone { #asynchronous-code }
Faire du code asynchrone signifie que le langage 💬 est capable de dire à l'ordinateur / au programme 🤖 qu'à un moment du code, il 🤖 devra attendre que *quelque chose d'autre* se termine autre part. Disons que ce *quelque chose d'autre* est appelé "fichier-lent" 📝.
Faire du code asynchrone signifie que le langage 💬 est capable de dire à l'ordinateur / au programme 🤖 qu'à un moment du code, il 🤖 devra attendre que *quelque chose d'autre* se termine autre part. Disons que ce *quelque chose d'autre* est appelé « slow-file » 📝.
Donc, pendant ce temps, l'ordinateur pourra effectuer d'autres tâches, pendant que "fichier-lent" 📝 se termine.
Donc, pendant ce temps, l'ordinateur pourra effectuer d'autres tâches, pendant que « slow-file » 📝 se termine.
Ensuite l'ordinateur / le programme 🤖 reviendra à chaque fois qu'il en a la chance que ce soit parce qu'il attend à nouveau, ou car il 🤖 a fini tout le travail qu'il avait à faire. Il 🤖 regardera donc si les tâches qu'il attend ont terminé d'être effectuées.
Ensuite, il 🤖 prendra la première tâche à finir (disons, notre "fichier-lent" 📝) et continuera à faire avec cette dernière ce qu'il était censé.
Ensuite, il 🤖 prendra la première tâche à finir (disons, notre « slow-file » 📝) et continuera à faire avec cette dernière ce qu'il était censé.
Ce "attendre quelque chose d'autre" fait généralement référence à des opérations <abbr title="Input/Output ou Entrées et Sorties ">I/O</abbr> qui sont relativement "lentes" (comparées à la vitesse du processeur et de la mémoire RAM) telles qu'attendre que :
Ce « attendre quelque chose d'autre » fait généralement référence à des opérations <abbr title="Input and Output - Entrées et sorties">I/O</abbr> qui sont relativement « lentes » (comparées à la vitesse du processeur et de la mémoire RAM) telles qu'attendre que :
* de la donnée soit envoyée par le client à travers le réseau
* de la donnée envoyée depuis votre programme soit reçue par le client à travers le réseau
* le contenu d'un fichier sur le disque soit lu par le système et passé à votre programme
* le contenu que votre programme a passé au système soit écrit sur le disque
* une opération effectuée à distance par une API se termine
* une opération en BDD se termine
* une requête à une BDD renvoie un résultat
* une opération en base de données se termine
* une requête à une base de données renvoie un résultat
* etc.
Le temps d'exécution étant consommé majoritairement par l'attente d'opérations <abbr title="Input/Output ou Entrées et Sorties ">I/O</abbr> on appelle ceci des opérations <a href="https://fr.wikipedia.org/wiki/I/O_bound" class="external-link" target="_blank">"I/O bound"</a>.
Le temps d'exécution étant consommé majoritairement par l'attente d'opérations <abbr title="Input and Output - Entrées et sorties">I/O</abbr>, on appelle ceci des opérations « I/O bound ».
Ce concept se nomme l'"asynchronisme" car l'ordinateur / le programme n'a pas besoin d'être "synchronisé" avec la tâche, attendant le moment exact où cette dernière se terminera en ne faisant rien, pour être capable de récupérer le résultat de la tâche et l'utiliser dans la suite des opérations.
Ce concept se nomme « asynchrone » car l'ordinateur / le programme n'a pas besoin d'être « synchronisé » avec la tâche, attendant le moment exact où cette dernière se terminera en ne faisant rien, pour être capable de récupérer le résultat de la tâche et l'utiliser dans la suite des opérations.
À la place, en étant "asynchrone", une fois terminée, une tâche peut légèrement attendre (quelques microsecondes) que l'ordinateur / le programme finisse ce qu'il était en train de faire, et revienne récupérer le résultat.
À la place, en étant « asynchrone », une fois terminée, une tâche peut légèrement attendre (quelques microsecondes) que l'ordinateur / le programme finisse ce qu'il était en train de faire, et revienne récupérer le résultat.
Pour parler de tâches "synchrones" (en opposition à "asynchrones"), on utilise souvent le terme "séquentiel", car l'ordinateur / le programme va effectuer toutes les étapes d'une tâche séquentiellement avant de passer à une autre tâche, même si ces étapes impliquent de l'attente.
Pour parler de tâches « synchrones » (en opposition à « asynchrones »), on utilise souvent le terme « séquentiel », car l'ordinateur / le programme va effectuer toutes les étapes d'une tâche séquentiellement avant de passer à une autre tâche, même si ces étapes impliquent de l'attente.
### Concurrence et Burgers
### Concurrence et Burgers { #concurrency-and-burgers }
L'idée de code **asynchrone** décrite ci-dessus est parfois aussi appelée **"concurrence"**. Ce qui est différent du **"parallélisme"**.
L'idée de code **asynchrone** décrite ci-dessus est parfois aussi appelée **« concurrence »**. Ce qui est différent du **« parallélisme »**.
La **concurrence** et le **parallélisme** sont tous deux liés à l'idée de "différentes choses arrivant plus ou moins au même moment".
La **concurrence** et le **parallélisme** sont tous deux liés à l'idée de « différentes choses arrivant plus ou moins au même moment ».
Mais les détails entre la **concurrence** et le **parallélisme** diffèrent sur de nombreux points.
Pour expliquer la différence, voici une histoire de burgers :
#### Burgers concurrents
### Burgers concurrents { #concurrent-burgers }
Vous amenez votre crush 😍 dans votre fast food 🍔 favori, et faites la queue pendant que le serveur 💁 prend les commandes des personnes devant vous.
@ -122,13 +123,13 @@ Le serveur 💁 vous donne le numéro assigné à votre commande.
<img src="/img/async/concurrent-burgers/concurrent-burgers-04.png" class="illustration">
Pendant que vous attendez, vous allez choisir une table avec votre crush 😍, vous discutez avec votre crush 😍 pendant un long moment (les burgers étant "magnifiques" ils sont très longs à préparer ✨🍔✨).
Pendant que vous attendez, vous allez choisir une table avec votre crush 😍, vous discutez avec votre crush 😍 pendant un long moment (les burgers étant « magnifiques » ils sont très longs à préparer ✨🍔✨).
Pendant que vous êtes assis à table, en attendant que les burgers 🍔 soient prêts, vous pouvez passer ce temps à admirer à quel point votre crush 😍 est géniale, mignonne et intelligente ✨😍✨.
<img src="/img/async/concurrent-burgers/concurrent-burgers-05.png" class="illustration">
Pendant que vous discutez avec votre crush 😍, de temps en temps vous jetez un coup d'oeil au nombre affiché au-dessus du comptoir pour savoir si c'est à votre tour d'être servis.
Pendant que vous discutez avec votre crush 😍, de temps en temps vous jetez un coup d’œil au nombre affiché au-dessus du comptoir pour savoir si c'est à votre tour d'être servis.
Jusqu'au moment où c'est (enfin) votre tour. Vous allez au comptoir, récupérez vos burgers 🍔 et revenez à votre table.
@ -148,23 +149,23 @@ Illustrations proposées par <a href="https://www.instagram.com/ketrinadrawsalot
Imaginez que vous êtes l'ordinateur / le programme 🤖 dans cette histoire.
Pendant que vous faites la queue, vous être simplement inactif 😴, attendant votre tour, ne faisant rien de "productif". Mais la queue est rapide car le serveur 💁 prend seulement les commandes (et ne les prépare pas), donc tout va bien.
Pendant que vous faites la queue, vous être simplement inactif 😴, attendant votre tour, ne faisant rien de « productif ». Mais la queue est rapide car le serveur 💁 prend seulement les commandes (et ne les prépare pas), donc tout va bien.
Ensuite, quand c'est votre tour, vous faites des actions "productives" 🤓, vous étudiez le menu, décidez ce que vous voulez, demandez à votre crush 😍 son choix, payez 💸, vérifiez que vous utilisez la bonne carte de crédit, vérifiez que le montant débité sur la carte est correct, vérifiez que la commande contient les bons produits, etc.
Ensuite, quand c'est votre tour, vous faites des actions « productives » 🤓, vous étudiez le menu, décidez ce que vous voulez, demandez à votre crush 😍 son choix, payez 💸, vérifiez que vous utilisez la bonne carte de crédit, vérifiez que le montant débité sur la carte est correct, vérifiez que la commande contient les bons produits, etc.
Mais ensuite, même si vous n'avez pas encore vos burgers 🍔, votre travail avec le serveur 💁 est "en pause" ⏸, car vous devez attendre 🕙 que vos burgers soient prêts.
Mais ensuite, même si vous n'avez pas encore vos burgers 🍔, votre travail avec le serveur 💁 est « en pause » ⏸, car vous devez attendre 🕙 que vos burgers soient prêts.
Après vous être écarté du comptoir et vous être assis à votre table avec le numéro de votre commande, vous pouvez tourner 🔀 votre attention vers votre crush 😍, et "travailler" ⏯ 🤓 là-dessus. Vous êtes donc à nouveau en train de faire quelque chose de "productif" 🤓, vous flirtez avec votre crush 😍.
Après vous être écarté du comptoir et vous être assis à votre table avec le numéro de votre commande, vous pouvez tourner 🔀 votre attention vers votre crush 😍, et « travailler » ⏯ 🤓 là-dessus. Vous êtes donc à nouveau en train de faire quelque chose de « productif » 🤓, vous flirtez avec votre crush 😍.
Puis le serveur 💁 dit "J'ai fini de préparer les burgers" 🍔 en mettant votre numéro sur l'affichage du comptoir, mais vous ne courrez pas immédiatement au moment où votre numéro s'affiche. Vous savez que personne ne volera vos burgers 🍔 car vous avez votre numéro et les autres clients ont le leur.
Puis le serveur 💁 dit « J'ai fini de préparer les burgers » 🍔 en mettant votre numéro sur l'affichage du comptoir, mais vous ne courez pas immédiatement au moment où votre numéro s'affiche. Vous savez que personne ne volera vos burgers 🍔 car vous avez votre numéro et les autres clients ont le leur.
Vous attendez donc que votre crush 😍 finisse son histoire, souriez gentiment et dites que vous allez chercher les burgers ⏸.
Pour finir vous allez au comptoir 🔀, vers la tâche initiale qui est désormais terminée ⏯, récupérez les burgers 🍔, remerciez le serveur et ramenez les burgers 🍔 à votre table. Ceci termine l'étape / la tâche d'interaction avec le comptoir ⏹. Ce qui ensuite, crée une nouvelle tâche de "manger les burgers" 🔀 ⏯, mais la précédente, "récupérer les burgers" est terminée ⏹.
Pour finir vous allez au comptoir 🔀, vers la tâche initiale qui est désormais terminée ⏯, récupérez les burgers 🍔, remerciez le serveur et ramenez les burgers 🍔 à votre table. Ceci termine l'étape / la tâche d'interaction avec le comptoir ⏹. Ce qui ensuite, crée une nouvelle tâche de « manger les burgers » 🔀 ⏯, mais la précédente, « récupérer les burgers » est terminée ⏹.
#### Burgers parallèles
### Burgers parallèles { #parallel-burgers }
Imaginons désormais que ce ne sont pas des "burgers concurrents" mais des "burgers parallèles".
Imaginons désormais que ce ne sont pas des « burgers concurrents » mais des « burgers parallèles ».
Vous allez avec votre crush 😍 dans un fast food 🍔 parallélisé.
@ -188,7 +189,7 @@ Vous attendez devant le comptoir afin que personne ne prenne vos burgers 🍔 av
Vous et votre crush 😍 étant occupés à vérifier que personne ne passe devant vous prendre vos burgers au moment où ils arriveront 🕙, vous ne pouvez pas vous préoccuper de votre crush 😞.
C'est du travail "synchrone", vous être "synchronisés" avec le serveur/cuisinier 👨‍🍳. Vous devez attendre 🕙 et être présent au moment exact où le serveur/cuisinier 👨‍🍳 finira les burgers 🍔 et vous les donnera, sinon quelqu'un risque de vous les prendre.
C'est du travail « synchrone », vous être « synchronisés » avec le serveur/cuisinier 👨‍🍳. Vous devez attendre 🕙 et être présent au moment exact où le serveur/cuisinier 👨‍🍳 finira les burgers 🍔 et vous les donnera, sinon quelqu'un risque de vous les prendre.
<img src="/img/async/parallel-burgers/parallel-burgers-04.png" class="illustration">
@ -212,7 +213,7 @@ Illustrations proposées par <a href="https://www.instagram.com/ketrinadrawsalot
---
Dans ce scénario de burgers parallèles, vous êtes un ordinateur / programme 🤖 avec deux processeurs (vous et votre crush 😍) attendant 🕙 à deux et dédiant votre attention 🕙 à "attendre devant le comptoir" pour une longue durée.
Dans ce scénario de burgers parallèles, vous êtes un ordinateur / programme 🤖 avec deux processeurs (vous et votre crush 😍) attendant 🕙 à deux et dédiant votre attention ⏯ à « attendre devant le comptoir » 🕙 pour une longue durée.
Le fast-food a 8 processeurs (serveurs/cuisiniers) 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳. Alors que le fast-food de burgers concurrents en avait 2 (un serveur et un cuisinier).
@ -222,7 +223,7 @@ Et pourtant l'expérience finale n'est pas meilleure 😞.
C'est donc l'histoire équivalente parallèle pour les burgers 🍔.
Pour un exemple plus courant dans la "vie réelle", imaginez une banque.
Pour un exemple plus courant dans la « vie réelle », imaginez une banque.
Jusqu'à récemment, la plupart des banques avaient plusieurs caisses (et banquiers) 👨‍💼👨‍💼👨‍💼👨‍💼 et une unique file d'attente 🕙🕙🕙🕙🕙🕙🕙🕙.
@ -232,9 +233,9 @@ Et vous deviez attendre 🕙 dans la file pendant un long moment ou vous perdiez
Vous n'auriez donc probablement pas envie d'amener votre crush 😍 avec vous à la banque 🏦.
#### Conclusion
### Conclusion sur les burgers { #burger-conclusion }
Dans ce scénario des "burgers du fast-food avec votre crush", comme il y a beaucoup d'attente 🕙, il est très logique d'avoir un système concurrent ⏸🔀⏯.
Dans ce scénario des « burgers du fast-food avec votre crush », comme il y a beaucoup d'attente 🕙, il est très logique d'avoir un système concurrent ⏸🔀⏯.
Et c'est le cas pour la plupart des applications web.
@ -242,17 +243,17 @@ Vous aurez de nombreux, nombreux utilisateurs, mais votre serveur attendra 🕙
Puis vous attendrez 🕙 de nouveau que leurs réponses reviennent.
Cette "attente" 🕙 se mesure en microsecondes, mais tout de même, en cumulé cela fait beaucoup d'attente.
Cette « attente » 🕙 se mesure en microsecondes, mais tout de même, en cumulé cela fait beaucoup d'attente.
C'est pourquoi il est logique d'utiliser du code asynchrone ⏸🔀⏯ pour des APIs web.
Ce type d'asynchronicité est ce qui a rendu NodeJS populaire (bien que NodeJS ne soit pas parallèle) et c'est la force du Go en tant que langage de programmation.
Ce type d'asynchronicité est ce qui a rendu NodeJS populaire (bien que NodeJS ne soit pas parallèle) et c'est la force de Go en tant que langage de programmation.
Et c'est le même niveau de performance que celui obtenu avec **FastAPI**.
Et comme on peut avoir du parallélisme et de l'asynchronicité en même temps, on obtient des performances plus hautes que la plupart des frameworks NodeJS et égales à celles du Go, qui est un langage compilé plus proche du C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(tout ça grâce à Starlette)</a>.
Et comme on peut avoir du parallélisme et de l'asynchronicité en même temps, on obtient des performances plus hautes que la plupart des frameworks NodeJS testés et égales à celles du Go, qui est un langage compilé plus proche du C <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(tout ça grâce à Starlette)</a>.
### Est-ce que la concurrence est mieux que le parallélisme ?
### Est-ce que la concurrence est mieux que le parallélisme ? { #is-concurrency-better-than-parallelism }
Nope ! C'est ça la morale de l'histoire.
@ -276,11 +277,11 @@ Mais dans ce cas, si pouviez amener 8 ex-serveurs/cuisiniers/devenus-nettoyeurs
Dans ce scénario, chacun des nettoyeurs (vous y compris) serait un processeur, faisant sa partie du travail.
Et comme la plupart du temps d'exécution est pris par du "vrai" travail (et non de l'attente), et que le travail dans un ordinateur est fait par un <abbr title="Central Processing Unit">CPU</abbr>, ce sont des problèmes dits "CPU bound".
Et comme la plupart du temps d'exécution est pris par du « vrai » travail (et non de l'attente), et que le travail dans un ordinateur est fait par un <abbr title="Central Processing Unit - Unité centrale de traitement">CPU</abbr>, ce sont des problèmes dits « CPU bound ».
---
Des exemples communs d'opérations "CPU bounds" sont les procédés qui requièrent des traitements mathématiques complexes.
Des exemples communs d'opérations « CPU bound » sont les procédés qui requièrent des traitements mathématiques complexes.
Par exemple :
@ -289,19 +290,19 @@ Par exemple :
* L'apprentissage automatique (ou **Machine Learning**) : cela nécessite de nombreuses multiplications de matrices et vecteurs. Imaginez une énorme feuille de calcul remplie de nombres que vous multiplierez entre eux tous au même moment.
* L'apprentissage profond (ou **Deep Learning**) : est un sous-domaine du **Machine Learning**, donc les mêmes raisons s'appliquent. Avec la différence qu'il n'y a pas une unique feuille de calcul de nombres à multiplier, mais une énorme quantité d'entre elles, et dans de nombreux cas, on utilise un processeur spécial pour construire et / ou utiliser ces modèles.
### Concurrence + Parallélisme : Web + Machine Learning
### Concurrence + Parallélisme : Web + Machine Learning { #concurrency-parallelism-web-machine-learning }
Avec **FastAPI** vous pouvez bénéficier de la concurrence qui est très courante en développement web (c'est l'attrait principal de NodeJS).
Mais vous pouvez aussi profiter du parallélisme et multiprocessing afin de gérer des charges **CPU bound** qui sont récurrentes dans les systèmes de *Machine Learning*.
Mais vous pouvez aussi profiter du parallélisme et du multiprocessing (plusieurs processus s'exécutant en parallèle) afin de gérer des charges **CPU bound** qui sont récurrentes dans les systèmes de *Machine Learning*.
Ça, ajouté au fait que Python soit le langage le plus populaire pour la **Data Science**, le **Machine Learning** et surtout le **Deep Learning**, font de **FastAPI** un très bon choix pour les APIs et applications de **Data Science** / **Machine Learning**.
Pour comprendre comment mettre en place ce parallélisme en production, allez lire la section [Déploiement](deployment/index.md){.internal-link target=_blank}.
## `async` et `await`
## `async` et `await` { #async-and-await }
Les versions modernes de Python ont une manière très intuitive de définir le code asynchrone, tout en gardant une apparence de code "séquentiel" classique en laissant Python faire l'attente pour vous au bon moment.
Les versions modernes de Python ont une manière très intuitive de définir le code asynchrone, tout en gardant une apparence de code « séquentiel » classique en laissant Python faire l'attente pour vous au bon moment.
Pour une opération qui nécessite de l'attente avant de donner un résultat et qui supporte ces nouvelles fonctionnalités Python, vous pouvez l'utiliser comme tel :
@ -319,12 +320,12 @@ async def get_burgers(number: int):
return burgers
```
...et non `def` :
... et non `def` :
```Python hl_lines="2"
# Ceci n'est pas asynchrone
def get_sequential_burgers(number: int):
# Opérations asynchrones pour créer les burgers
# Opérations séquentielles pour créer les burgers
return burgers
```
@ -339,7 +340,7 @@ burgers = get_burgers(2)
---
Donc, si vous utilisez une bibliothèque qui nécessite que ses fonctions soient appelées avec `await`, vous devez définir la *fonction de chemin* en utilisant `async def` comme dans :
Donc, si vous utilisez une bibliothèque qui nécessite que ses fonctions soient appelées avec `await`, vous devez définir la *fonction de chemin d'accès* en utilisant `async def` comme dans :
```Python hl_lines="2-3"
@app.get('/burgers')
@ -348,52 +349,61 @@ async def read_burgers():
return burgers
```
### Plus de détails techniques
### Plus de détails techniques { #more-technical-details }
Vous avez donc compris que `await` peut seulement être utilisé dans des fonctions définies avec `async def`.
Mais en même temps, les fonctions définies avec `async def` doivent être appelées avec `await` et donc dans des fonctions définies elles aussi avec `async def`.
Vous avez donc remarqué ce paradoxe d'oeuf et de la poule, comment appelle-t-on la première fonction `async` ?
Vous avez donc remarqué ce paradoxe d'œuf et de la poule, comment appelle-t-on la première fonction `async` ?
Si vous utilisez **FastAPI**, pas besoin de vous en inquiéter, car cette "première" fonction sera votre *fonction de chemin* ; et **FastAPI** saura comment arriver au résultat attendu.
Si vous utilisez **FastAPI**, pas besoin de vous en inquiéter, car cette « première » fonction sera votre *fonction de chemin d'accès* ; et **FastAPI** saura comment arriver au résultat attendu.
Mais si vous utilisez `async` / `await` sans **FastAPI**, <a href="https://docs.python.org/3/library/asyncio-task.html#coroutine" class="external-link" target="_blank">allez jetez un coup d'oeil à la documentation officielle de Python</a>.
Mais si vous souhaitez utiliser `async` / `await` sans FastAPI, vous pouvez également le faire.
### Autres formes de code asynchrone
### Écrire votre propre code async { #write-your-own-async-code }
Starlette (et **FastAPI**) sappuie sur <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, ce qui le rend compatible à la fois avec la bibliothèque standard <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> de Python et avec <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
En particulier, vous pouvez utiliser directement <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> pour vos cas dusage de concurrence avancés qui nécessitent des schémas plus élaborés dans votre propre code.
Et même si vous nutilisiez pas FastAPI, vous pourriez aussi écrire vos propres applications async avec <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> pour une grande compatibilité et pour bénéficier de ses avantages (par ex. la « structured concurrency »).
Jai créé une autre bibliothèque au-dessus dAnyIO, comme une fine surcouche, pour améliorer un peu les annotations de type et obtenir une meilleure **autocomplétion**, des **erreurs en ligne**, etc. Elle propose également une introduction et un tutoriel accessibles pour vous aider à **comprendre** et écrire **votre propre code async** : <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. Elle sera particulièrement utile si vous devez **combiner du code async avec du code classique** (bloquant/synchrone).
### Autres formes de code asynchrone { #other-forms-of-asynchronous-code }
L'utilisation d'`async` et `await` est relativement nouvelle dans ce langage.
Mais cela rend la programmation asynchrone bien plus simple.
Cette même syntaxe (ou presque) était aussi incluse dans les versions modernes de Javascript (dans les versions navigateur et NodeJS).
Cette même syntaxe (ou presque) a aussi été incluse récemment dans les versions modernes de JavaScript (dans les navigateurs et NodeJS).
Mais avant ça, gérer du code asynchrone était bien plus complexe et difficile.
Dans les versions précédentes de Python, vous auriez utilisé des *threads* ou <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>. Mais le code aurait été bien plus difficile à comprendre, débugger, et concevoir.
Dans les versions précédentes de Python, vous auriez utilisé des threads ou <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a>. Mais le code aurait été bien plus difficile à comprendre, débugger, et concevoir.
Dans les versions précédentes de Javascript NodeJS / Navigateur, vous auriez utilisé des "callbacks". Menant potentiellement à ce que l'on appelle le "callback hell".
Dans les versions précédentes de JavaScript côté navigateur / NodeJS, vous auriez utilisé des « callbacks ». Menant potentiellement à ce que l'on appelle le « callback hell ».
## Coroutines { #coroutines }
## Coroutines
« Coroutine » est juste un terme élaboré pour désigner ce qui est retourné par une fonction définie avec `async def`. Python sait que c'est comme une fonction classique qui va démarrer à un moment et terminer à un autre, mais qu'elle peut aussi être mise en pause ⏸, du moment qu'il y a un `await` dans son contenu.
**Coroutine** est juste un terme élaboré pour désigner ce qui est retourné par une fonction définie avec `async def`. Python sait que c'est comme une fonction classique qui va démarrer à un moment et terminer à un autre, mais qu'elle peut aussi être mise en pause ⏸, du moment qu'il y a un `await` dans son contenu.
Mais toutes ces fonctionnalités d'utilisation de code asynchrone avec `async` et `await` sont souvent résumées comme l'utilisation des « coroutines ». On peut comparer cela à la principale fonctionnalité clé de Go, les « Goroutines ».
Mais toutes ces fonctionnalités d'utilisation de code asynchrone avec `async` et `await` sont souvent résumées comme l'utilisation des *coroutines*. On peut comparer cela à la principale fonctionnalité clé de Go, les "Goroutines".
## Conclusion
## Conclusion { #conclusion }
Reprenons la phrase du début de la page :
> Les versions modernes de Python supportent le **code asynchrone** grâce aux **"coroutines"** avec les syntaxes **`async` et `await`**.
> Les versions modernes de Python supportent le **code asynchrone** grâce aux **« coroutines »** avec les syntaxes **`async` et `await`**.
Ceci devrait être plus compréhensible désormais. ✨
Tout ceci est donc ce qui donne sa force à **FastAPI** (à travers Starlette) et lui permet d'avoir des performances aussi impressionnantes.
Tout ceci est donc ce qui donne sa force à FastAPI (à travers Starlette) et lui permet d'avoir une performance aussi impressionnante.
## Détails très techniques
## Détails très techniques { #very-technical-details }
/// warning | Attention !
/// warning | Alertes
Vous pouvez probablement ignorer cela.
@ -403,32 +413,32 @@ Si vous avez de bonnes connaissances techniques (coroutines, threads, code bloqu
///
### Fonctions de chemin
### Fonctions de chemin d'accès { #path-operation-functions }
Quand vous déclarez une *fonction de chemin* avec un `def` normal et non `async def`, elle est exécutée dans un groupe de threads (threadpool) externe qui est ensuite attendu, plutôt que d'être appelée directement (car cela bloquerait le serveur).
Quand vous déclarez une *fonction de chemin d'accès* avec un `def` normal et non `async def`, elle est exécutée dans un groupe de threads (threadpool) externe qui est ensuite attendu, plutôt que d'être appelée directement (car cela bloquerait le serveur).
Si vous venez d'un autre framework asynchrone qui ne fonctionne pas comme de la façon décrite ci-dessus et que vous êtes habitués à définir des *fonctions de chemin* basiques avec un simple `def` pour un faible gain de performance (environ 100 nanosecondes), veuillez noter que dans **FastAPI**, l'effet serait plutôt contraire. Dans ces cas-là, il vaut mieux utiliser `async def` à moins que votre *fonction de chemin* utilise du code qui effectue des opérations <abbr title="Input/Output ou Entrées et Sorties ">I/O</abbr> bloquantes.
Si vous venez d'un autre framework asynchrone qui ne fonctionne pas comme de la façon décrite ci-dessus et que vous êtes habitué à définir des *fonctions de chemin d'accès* basiques et purement calculatoires avec un simple `def` pour un faible gain de performance (environ 100 nanosecondes), veuillez noter que dans **FastAPI**, l'effet serait plutôt contraire. Dans ces cas-là, il vaut mieux utiliser `async def` à moins que votre *fonction de chemin d'accès* utilise du code qui effectue des opérations <abbr title="Input/Output - Entrées/Sorties: lecture ou écriture sur le disque, communications réseau.">I/O</abbr> bloquantes.
Au final, dans les deux situations, il est fort probable que **FastAPI** soit tout de même [plus rapide](index.md#performance){.internal-link target=_blank} que (ou au moins de vitesse égale à) votre framework précédent.
### Dépendances
### Dépendances { #dependencies }
La même chose s'applique aux dépendances. Si une dépendance est définie avec `def` plutôt que `async def`, elle est exécutée dans la threadpool externe.
La même chose s'applique aux [dépendances](tutorial/dependencies/index.md){.internal-link target=_blank}. Si une dépendance est définie avec `def` plutôt que `async def`, elle est exécutée dans la threadpool externe.
### Sous-dépendances
### Sous-dépendances { #sub-dependencies }
Vous pouvez avoir de multiples dépendances et sous-dépendances dépendant les unes des autres (en tant que paramètres de la définition de la *fonction de chemin*), certaines créées avec `async def` et d'autres avec `def`. Cela fonctionnerait aussi, et celles définies avec un simple `def` seraient exécutées sur un thread externe (venant de la threadpool) plutôt que d'être "attendues".
Vous pouvez avoir de multiples dépendances et [sous-dépendances](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} dépendant les unes des autres (en tant que paramètres de la définition de la *fonction de chemin d'accès*), certaines créées avec `async def` et d'autres avec `def`. Cela fonctionnerait aussi, et celles définies avec un simple `def` seraient exécutées sur un thread externe (venant de la threadpool) plutôt que d'être « attendues ».
### Autres fonctions utilitaires
### Autres fonctions utilitaires { #other-utility-functions }
Toute autre fonction utilitaire que vous appelez directement peut être créée avec un classique `def` ou avec `async def` et **FastAPI** n'aura pas d'impact sur la façon dont vous l'appelez.
Toute autre fonction utilitaire que vous appelez directement peut être créée avec un classique `def` ou avec `async def` et FastAPI n'aura pas d'impact sur la façon dont vous l'appelez.
Contrairement aux fonctions que **FastAPI** appelle pour vous : les *fonctions de chemin* et dépendances.
Contrairement aux fonctions que FastAPI appelle pour vous : les *fonctions de chemin d'accès* et dépendances.
Si votre fonction utilitaire est une fonction classique définie avec `def`, elle sera appelée directement (telle qu'écrite dans votre code), pas dans une threadpool, si la fonction est définie avec `async def` alors vous devrez attendre (avec `await`) que cette fonction se termine avant de passer à la suite du code.
Si votre fonction utilitaire est une fonction classique définie avec `def`, elle sera appelée directement (telle qu'écrite dans votre code), pas dans une threadpool ; si la fonction est définie avec `async def` alors vous devrez attendre (avec `await`) que cette fonction se termine avant de passer à la suite du code.
---
Encore une fois, ce sont des détails très techniques qui peuvent être utiles si vous venez ici les chercher.
Sinon, les instructions de la section <a href="#vous-etes-presses">Vous êtes pressés ?</a> ci-dessus sont largement suffisantes.
Sinon, les instructions de la section <a href="#in-a-hurry">Vous êtes pressés ?</a> ci-dessus sont largement suffisantes.

View File

@ -0,0 +1,24 @@
# Déployer FastAPI sur des fournisseurs cloud { #deploy-fastapi-on-cloud-providers }
Vous pouvez utiliser pratiquement n'importe quel fournisseur cloud pour déployer votre application FastAPI.
Dans la plupart des cas, les principaux fournisseurs cloud proposent des guides pour déployer FastAPI avec leurs services.
## FastAPI Cloud { #fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** est créée par le même auteur et l'équipe à l'origine de **FastAPI**.
Elle simplifie le processus de **création**, de **déploiement** et **d'accès** à une API avec un effort minimal.
Elle apporte la même **expérience développeur** que celle de la création d'applications avec FastAPI au **déploiement** de celles-ci dans le cloud. 🎉
FastAPI Cloud est le sponsor principal et le financeur des projets open source *FastAPI and friends*. ✨
## Fournisseurs cloud - Sponsors { #cloud-providers-sponsors }
D'autres fournisseurs cloud ✨ [**parrainent FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨ également. 🙇
Vous pouvez également envisager ces fournisseurs pour suivre leurs guides et essayer leurs services :
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
* <a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" class="external-link" target="_blank">Railway</a>

View File

@ -0,0 +1,321 @@
# Concepts de déploiement { #deployments-concepts }
Lorsque vous déployez une application **FastAPI**, ou en fait n'importe quel type de web API, il existe plusieurs concepts qui vous importent probablement, et en les utilisant vous pouvez trouver la manière la **plus appropriée** de **déployer votre application**.
Parmi les concepts importants, on trouve :
* Sécurité - HTTPS
* Exécuter au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes préalables avant de démarrer
Nous allons voir comment ils affectent les **déploiements**.
Au final, l'objectif ultime est de pouvoir **servir vos clients d'API** de manière **sécurisée**, d'**éviter les interruptions**, et d'utiliser les **ressources de calcul** (par exemple des serveurs/VM distants) aussi efficacement que possible. 🚀
Je vais vous en dire un peu plus ici sur ces **concepts**, ce qui devrait vous donner l'**intuition** nécessaire pour décider comment déployer votre API dans des environnements très différents, voire même dans des environnements **futurs** qui n'existent pas encore.
En tenant compte de ces concepts, vous serez en mesure **d'évaluer et de concevoir** la meilleure façon de déployer **vos propres API**.
Dans les chapitres suivants, je vous donnerai des **recettes concrètes** pour déployer des applications FastAPI.
Mais pour l'instant, voyons ces **idées conceptuelles** importantes. Ces concepts s'appliquent aussi à tout autre type de web API. 💡
## Sécurité - HTTPS { #security-https }
Dans le [chapitre précédent à propos de HTTPS](https.md){.internal-link target=_blank}, nous avons vu comment HTTPS fournit le chiffrement pour votre API.
Nous avons également vu que HTTPS est normalement fourni par un composant **externe** à votre serveur d'application, un **TLS Termination Proxy**.
Et il doit y avoir quelque chose chargé de **renouveler les certificats HTTPS** ; cela peut être le même composant ou quelque chose de différent.
### Exemples doutils pour HTTPS { #example-tools-for-https }
Parmi les outils que vous pourriez utiliser comme TLS Termination Proxy :
* Traefik
* Gère automatiquement le renouvellement des certificats ✨
* Caddy
* Gère automatiquement le renouvellement des certificats ✨
* Nginx
* Avec un composant externe comme Certbot pour le renouvellement des certificats
* HAProxy
* Avec un composant externe comme Certbot pour le renouvellement des certificats
* Kubernetes avec un Ingress Controller comme Nginx
* Avec un composant externe comme cert-manager pour le renouvellement des certificats
* Pris en charge en interne par un fournisseur cloud dans le cadre de ses services (lisez ci-dessous 👇)
Une autre option consiste à utiliser un **service cloud** qui fait davantage de travail, y compris la mise en place de HTTPS. Il peut avoir certaines restrictions ou vous facturer davantage, etc. Mais dans ce cas, vous n'auriez pas à configurer vousmême un TLS Termination Proxy.
Je vous montrerai des exemples concrets dans les prochains chapitres.
---
Les concepts suivants à considérer concernent tous le programme qui exécute votre API réelle (par ex. Uvicorn).
## Programme et processus { #program-and-process }
Nous allons beaucoup parler du « **processus** » en cours d'exécution, il est donc utile d'être clair sur ce que cela signifie, et sur la différence avec le mot « **programme** ».
### Qu'est-ce qu'un programme { #what-is-a-program }
Le mot **programme** est couramment utilisé pour décrire plusieurs choses :
* Le **code** que vous écrivez, les **fichiers Python**.
* Le **fichier** qui peut être **exécuté** par le système d'exploitation, par exemple : `python`, `python.exe` ou `uvicorn`.
* Un programme particulier lorsqu'il **s'exécute** sur le système d'exploitation, utilisant le CPU et stockant des choses en mémoire. On appelle aussi cela un **processus**.
### Qu'est-ce qu'un processus { #what-is-a-process }
Le mot **processus** est normalement utilisé de manière plus spécifique, en ne se référant qu'à l'élément qui s'exécute dans le système d'exploitation (comme dans le dernier point cidessus) :
* Un programme particulier lorsqu'il **s'exécute** sur le système d'exploitation.
* Cela ne se réfère ni au fichier, ni au code ; cela se réfère **spécifiquement** à l'élément qui est **exécuté** et géré par le système d'exploitation.
* N'importe quel programme, n'importe quel code, **ne peut faire des choses** que lorsqu'il est **exécuté**. Donc, lorsqu'il y a un **processus en cours**.
* Le processus peut être **arrêté** (ou « tué ») par vous ou par le système d'exploitation. À ce momentlà, il cesse de s'exécuter/d'être exécuté, et il **ne peut plus rien faire**.
* Chaque application que vous avez en cours d'exécution sur votre ordinateur a un processus derrière elle, chaque programme lancé, chaque fenêtre, etc. Et il y a normalement de nombreux processus exécutés **en même temps** tant qu'un ordinateur est allumé.
* Il peut y avoir **plusieurs processus** du **même programme** exécutés simultanément.
Si vous ouvrez le « gestionnaire des tâches » ou le « moniteur système » (ou des outils similaires) de votre système d'exploitation, vous verrez nombre de ces processus en cours d'exécution.
Et, par exemple, vous verrez probablement qu'il y a plusieurs processus exécutant le même navigateur (Firefox, Chrome, Edge, etc.). Ils exécutent normalement un processus par onglet, plus quelques processus supplémentaires.
<img class="shadow" src="/img/deployment/concepts/image01.png">
---
Maintenant que nous connaissons la différence entre les termes **processus** et **programme**, continuons à parler des déploiements.
## Exécuter au démarrage { #running-on-startup }
Dans la plupart des cas, lorsque vous créez une web API, vous voulez qu'elle **tourne en permanence**, sans interruption, afin que vos clients puissent toujours y accéder. Bien sûr, sauf si vous avez une raison spécifique de ne vouloir l'exécuter que dans certaines situations, mais la plupart du temps vous la voulez constamment en cours et **disponible**.
### Sur un serveur distant { #in-a-remote-server }
Lorsque vous configurez un serveur distant (un serveur cloud, une machine virtuelle, etc.), la chose la plus simple à faire est d'utiliser `fastapi run` (qui utilise Uvicorn) ou quelque chose de similaire, manuellement, de la même manière que lorsque vous développez en local.
Et cela fonctionnera et sera utile **pendant le développement**.
Mais si votre connexion au serveur est coupée, le **processus en cours** va probablement s'arrêter.
Et si le serveur est redémarré (par exemple après des mises à jour, ou des migrations chez le fournisseur cloud) vous **ne le remarquerez probablement pas**. Et à cause de cela, vous ne saurez même pas que vous devez redémarrer le processus manuellement. Ainsi, votre API restera tout simplement à l'arrêt. 😱
### Lancer automatiquement au démarrage { #run-automatically-on-startup }
En général, vous voudrez probablement que le programme serveur (par ex. Uvicorn) soit démarré automatiquement au démarrage du serveur, et sans aucune **intervention humaine**, afin d'avoir en permanence un processus exécutant votre API (par ex. Uvicorn exécutant votre app FastAPI).
### Programme séparé { #separate-program }
Pour y parvenir, vous aurez normalement un **programme séparé** qui s'assure que votre application est lancée au démarrage. Et dans de nombreux cas, il s'assurera également que d'autres composants ou applications sont également lancés, par exemple une base de données.
### Exemples doutils pour lancer au démarrage { #example-tools-to-run-at-startup }
Voici quelques exemples d'outils capables de faire ce travail :
* Docker
* Kubernetes
* Docker Compose
* Docker en mode Swarm
* Systemd
* Supervisor
* Pris en charge en interne par un fournisseur cloud dans le cadre de ses services
* Autres ...
Je vous donnerai des exemples plus concrets dans les prochains chapitres.
## Redémarrages { #restarts }
De la même manière que vous voulez vous assurer que votre application est lancée au démarrage, vous voulez probablement aussi vous assurer qu'elle est **redémarrée** après des échecs.
### Nous faisons des erreurs { #we-make-mistakes }
Nous, humains, faisons des **erreurs**, tout le temps. Les logiciels ont presque *toujours* des **bugs** cachés à différents endroits. 🐛
Et nous, développeurs, continuons à améliorer le code au fur et à mesure que nous trouvons ces bugs et que nous implémentons de nouvelles fonctionnalités (en ajoutant éventuellement de nouveaux bugs aussi 😅).
### Petites erreurs gérées automatiquement { #small-errors-automatically-handled }
Lors de la création de web API avec FastAPI, s'il y a une erreur dans notre code, FastAPI la contiendra normalement à la seule requête qui a déclenché l'erreur. 🛡
Le client recevra un **500 Internal Server Error** pour cette requête, mais l'application continuera de fonctionner pour les requêtes suivantes au lieu de simplement s'effondrer complètement.
### Erreurs plus importantes - plantages { #bigger-errors-crashes }
Néanmoins, il peut y avoir des cas où nous écrivons du code qui **fait planter l'application entière**, faisant planter Uvicorn et Python. 💥
Et malgré cela, vous ne voudrez probablement pas que l'application reste à l'arrêt parce qu'il y a eu une erreur à un endroit ; vous voudrez probablement qu'elle **continue de tourner**, au moins pour les *chemins d'accès* qui ne sont pas cassés.
### Redémarrer après un plantage { #restart-after-crash }
Mais dans ces cas avec de très mauvaises erreurs qui font planter le **processus** en cours, vous voudrez un composant externe chargé de **redémarrer** le processus, au moins quelques fois ...
/// tip | Astuce
... Bien que si l'application entière **plante immédiatement**, il n'est probablement pas logique de continuer à la redémarrer indéfiniment. Mais dans ces cas, vous le remarquerez probablement pendant le développement, ou au moins juste après le déploiement.
Concentronsnous donc sur les cas principaux, où elle pourrait planter entièrement dans certaines situations particulières **à l'avenir**, et où il est toujours logique de la redémarrer.
///
Vous voudrez probablement que l'élément chargé de redémarrer votre application soit un **composant externe**, car à ce stade, l'application ellemême avec Uvicorn et Python a déjà planté, donc il n'y a rien dans le même code de la même app qui pourrait y faire quoi que ce soit.
### Exemples doutils pour redémarrer automatiquement { #example-tools-to-restart-automatically }
Dans la plupart des cas, le même outil qui est utilisé pour **lancer le programme au démarrage** est également utilisé pour gérer les **redémarrages** automatiques.
Par exemple, cela peut être géré par :
* Docker
* Kubernetes
* Docker Compose
* Docker en mode Swarm
* Systemd
* Supervisor
* Pris en charge en interne par un fournisseur cloud dans le cadre de ses services
* Autres ...
## Réplication - Processus et mémoire { #replication-processes-and-memory }
Avec une application FastAPI, en utilisant un programme serveur comme la commande `fastapi` qui exécute Uvicorn, l'exécuter une fois dans **un processus** peut servir plusieurs clients simultanément.
Mais dans de nombreux cas, vous voudrez exécuter plusieurs processus de travail en même temps.
### Multiples processus - Workers { #multiple-processes-workers }
Si vous avez plus de clients que ce qu'un seul processus peut gérer (par exemple si la machine virtuelle n'est pas très grande) et que vous avez **plusieurs cœurs** dans le CPU du serveur, alors vous pouvez avoir **plusieurs processus** exécutant la même application simultanément, et distribuer toutes les requêtes entre eux.
Quand vous exécutez **plusieurs processus** du même programme d'API, on les appelle couramment des **workers**.
### Processus workers et ports { #worker-processes-and-ports }
Rappelezvous, d'après les documents [À propos de HTTPS](https.md){.internal-link target=_blank}, qu'un seul processus peut écouter une combinaison de port et d'adresse IP sur un serveur ?
C'est toujours vrai.
Donc, pour pouvoir avoir **plusieurs processus** en même temps, il doit y avoir un **seul processus à l'écoute sur un port** qui transmet ensuite la communication à chaque processus worker d'une manière ou d'une autre.
### Mémoire par processus { #memory-per-process }
Maintenant, lorsque le programme charge des choses en mémoire, par exemple, un modèle de machine learning dans une variable, ou le contenu d'un gros fichier dans une variable, tout cela **consomme une partie de la mémoire (RAM)** du serveur.
Et plusieurs processus **ne partagent normalement pas de mémoire**. Cela signifie que chaque processus en cours a ses propres éléments, variables et mémoire. Et si vous consommez une grande quantité de mémoire dans votre code, **chaque processus** consommera une quantité équivalente de mémoire.
### Mémoire du serveur { #server-memory }
Par exemple, si votre code charge un modèle de Machine Learning de **1 Go**, lorsque vous exécutez un processus avec votre API, il consommera au moins 1 Go de RAM. Et si vous démarrez **4 processus** (4 workers), chacun consommera 1 Go de RAM. Donc au total, votre API consommera **4 Go de RAM**.
Et si votre serveur distant ou votre machine virtuelle n'a que 3 Go de RAM, essayer de charger plus de 4 Go de RAM posera problème. 🚨
### Multiples processus - Un exemple { #multiple-processes-an-example }
Dans cet exemple, il y a un **processus gestionnaire** qui démarre et contrôle deux **processus workers**.
Ce processus gestionnaire serait probablement celui qui écoute sur le **port** de l'IP. Et il transmettrait toute la communication aux processus workers.
Ces processus workers seraient ceux qui exécutent votre application, ils effectueraient les calculs principaux pour recevoir une **requête** et renvoyer une **réponse**, et ils chargeraient tout ce que vous mettez dans des variables en RAM.
<img src="/img/deployment/concepts/process-ram.drawio.svg">
Et bien sûr, la même machine aurait probablement **d'autres processus** en cours d'exécution également, en plus de votre application.
Un détail intéressant est que le pourcentage de **CPU utilisé** par chaque processus peut **varier** fortement dans le temps, mais la **mémoire (RAM)** reste normalement plus ou moins **stable**.
Si vous avez une API qui effectue une quantité comparable de calculs à chaque fois et que vous avez beaucoup de clients, alors l'**utilisation du CPU** sera probablement *également stable* (au lieu de monter et descendre rapidement en permanence).
### Exemples doutils et de stratégies de réplication { #examples-of-replication-tools-and-strategies }
Il peut y avoir plusieurs approches pour y parvenir, et je vous en dirai plus sur des stratégies spécifiques dans les prochains chapitres, par exemple en parlant de Docker et des conteneurs.
La principale contrainte à considérer est qu'il doit y avoir un **seul** composant gérant le **port** sur l'**IP publique**. Et il doit ensuite avoir un moyen de **transmettre** la communication aux **processus/workers** répliqués.
Voici quelques combinaisons et stratégies possibles :
* **Uvicorn** avec `--workers`
* Un **gestionnaire de processus** Uvicorn écouterait sur l'**IP** et le **port**, et il démarrerait **plusieurs processus workers Uvicorn**.
* **Kubernetes** et autres systèmes **de conteneurs** distribués
* Quelque chose dans la couche **Kubernetes** écouterait sur l'**IP** et le **port**. La réplication se ferait en ayant **plusieurs conteneurs**, chacun avec **un processus Uvicorn** en cours.
* **Services cloud** qui s'en chargent pour vous
* Le service cloud **gérera probablement la réplication pour vous**. Il vous permettra éventuellement de définir **un processus à exécuter**, ou une **image de conteneur** à utiliser ; dans tous les cas, ce sera très probablement **un seul processus Uvicorn**, et le service cloud sera chargé de le répliquer.
/// tip | Astuce
Ne vous inquiétez pas si certains de ces éléments concernant les **conteneurs**, Docker ou Kubernetes ne sont pas encore très clairs.
Je vous en dirai plus sur les images de conteneurs, Docker, Kubernetes, etc. dans un chapitre à venir : [FastAPI dans des conteneurs - Docker](docker.md){.internal-link target=_blank}.
///
## Étapes préalables avant de démarrer { #previous-steps-before-starting }
Il existe de nombreux cas où vous souhaitez effectuer certaines étapes **avant de démarrer** votre application.
Par exemple, vous pourriez vouloir exécuter des **migrations de base de données**.
Mais dans la plupart des cas, vous voudrez effectuer ces étapes **une seule fois**.
Vous voudrez donc avoir un **processus unique** pour effectuer ces **étapes préalables**, avant de démarrer l'application.
Et vous devez vous assurer que c'est un processus unique qui exécute ces étapes préalables *même si*, ensuite, vous démarrez **plusieurs processus** (plusieurs workers) pour l'application ellemême. Si ces étapes étaient exécutées par **plusieurs processus**, ils **dupliqueraient** le travail en l'exécutant **en parallèle**, et si les étapes étaient délicates comme une migration de base de données, elles pourraient entrer en conflit les unes avec les autres.
Bien sûr, il y a des cas où il n'y a aucun problème à exécuter les étapes préalables plusieurs fois ; dans ce cas, c'est beaucoup plus simple à gérer.
/// tip | Astuce
Gardez aussi à l'esprit que selon votre configuration, dans certains cas vous **n'aurez peutêtre même pas besoin d'étapes préalables** avant de démarrer votre application.
Dans ce cas, vous n'auriez pas à vous soucier de tout cela. 🤷
///
### Exemples de stratégies pour les étapes préalables { #examples-of-previous-steps-strategies }
Cela **dépendra fortement** de la manière dont vous **déployez votre système**, et sera probablement lié à votre manière de démarrer les programmes, de gérer les redémarrages, etc.
Voici quelques idées possibles :
* Un « Init Container » dans Kubernetes qui s'exécute avant votre conteneur d'application
* Un script bash qui exécute les étapes préalables puis démarre votre application
* Vous aurez toujours besoin d'un moyen de démarrer/redémarrer *ce* script bash, de détecter les erreurs, etc.
/// tip | Astuce
Je vous donnerai des exemples plus concrets pour faire cela avec des conteneurs dans un chapitre à venir : [FastAPI dans des conteneurs - Docker](docker.md){.internal-link target=_blank}.
///
## Utilisation des ressources { #resource-utilization }
Votre ou vos serveurs constituent une **ressource** que vos programmes peuvent consommer ou **utiliser** : le temps de calcul des CPU et la mémoire RAM disponible.
Quelle quantité des ressources système voulezvous consommer/utiliser ? Il peut être facile de penser « pas beaucoup », mais en réalité, vous voudrez probablement consommer **le plus possible sans planter**.
Si vous payez pour 3 serveurs mais que vous n'utilisez qu'un petit peu de leur RAM et CPU, vous **gaspillez probablement de l'argent** 💸, et **gaspillez probablement l'électricité des serveurs** 🌎, etc.
Dans ce cas, il pourrait être préférable de n'avoir que 2 serveurs et d'utiliser un pourcentage plus élevé de leurs ressources (CPU, mémoire, disque, bande passante réseau, etc.).
À l'inverse, si vous avez 2 serveurs et que vous utilisez **100 % de leur CPU et de leur RAM**, à un moment donné un processus demandera plus de mémoire, et le serveur devra utiliser le disque comme « mémoire » (ce qui peut être des milliers de fois plus lent), voire **planter**. Ou un processus pourrait avoir besoin de faire un calcul et devrait attendre que le CPU soit à nouveau libre.
Dans ce cas, il serait préférable d'obtenir **un serveur supplémentaire** et d'y exécuter certains processus afin qu'ils aient tous **suffisamment de RAM et de temps CPU**.
Il est également possible que, pour une raison quelconque, vous ayez un **pic** d'utilisation de votre API. Peutêtre qu'elle devient virale, ou peutêtre que d'autres services ou bots commencent à l'utiliser. Et vous voudrez peutêtre disposer de ressources supplémentaires pour être en sécurité dans ces cas.
Vous pouvez définir un **chiffre arbitraire** comme cible, par exemple **entre 50 % et 90 %** d'utilisation des ressources. L'idée est que ce sont probablement les principaux éléments que vous voudrez mesurer et utiliser pour ajuster vos déploiements.
Vous pouvez utiliser des outils simples comme `htop` pour voir le CPU et la RAM utilisés sur votre serveur ou la quantité utilisée par chaque processus. Ou vous pouvez utiliser des outils de supervision plus complexes, éventuellement distribués sur plusieurs serveurs, etc.
## Récapitulatif { #recap }
Vous venez de lire ici certains des principaux concepts que vous devrez probablement garder à l'esprit lorsque vous décidez comment déployer votre application :
* Sécurité - HTTPS
* Exécuter au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes préalables avant de démarrer
Comprendre ces idées et comment les appliquer devrait vous donner l'intuition nécessaire pour prendre toutes les décisions lors de la configuration et de l'ajustement de vos déploiements. 🤓
Dans les sections suivantes, je vous donnerai des exemples plus concrets de stratégies possibles à suivre. 🚀

View File

@ -14,7 +14,7 @@ Vous êtes pressé et vous connaissez déjà tout ça ? Allez directement au [`D
<summary>Aperçu du Dockerfile 👀</summary>
```Dockerfile
FROM python:3.9
FROM python:3.14
WORKDIR /code
@ -166,7 +166,7 @@ Maintenant, dans le même répertoire de projet, créez un fichier `Dockerfile`
```{ .dockerfile .annotate }
# (1)!
FROM python:3.9
FROM python:3.14
# (2)!
WORKDIR /code
@ -390,7 +390,7 @@ Si votre FastAPI est un seul fichier, par exemple `main.py` sans répertoire `./
Vous n'auriez alors qu'à changer les chemins correspondants pour copier le fichier dans le `Dockerfile` :
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.9
FROM python:3.14
WORKDIR /code
@ -454,7 +454,7 @@ Sans utiliser de conteneurs, faire en sorte que les applications s'exécutent au
## Réplication - Nombre de processus { #replication-number-of-processes }
Si vous avez un <abbr title="Un groupe de machines configurées pour être connectées et fonctionner ensemble d'une certaine manière.">cluster</abbr> de machines avec **Kubernetes**, Docker Swarm Mode, Nomad, ou un autre système complexe similaire pour gérer des conteneurs distribués sur plusieurs machines, alors vous voudrez probablement **gérer la réplication** au **niveau du cluster** plutôt que d'utiliser un **gestionnaire de processus** (comme Uvicorn avec workers) dans chaque conteneur.
Si vous avez un <dfn title="Groupe de machines configurées pour être connectées et fonctionner ensemble d'une certaine manière.">cluster</dfn> de machines avec **Kubernetes**, Docker Swarm Mode, Nomad, ou un autre système complexe similaire pour gérer des conteneurs distribués sur plusieurs machines, alors vous voudrez probablement **gérer la réplication** au **niveau du cluster** plutôt que d'utiliser un **gestionnaire de processus** (comme Uvicorn avec workers) dans chaque conteneur.
L'un de ces systèmes de gestion de conteneurs distribués comme Kubernetes dispose normalement d'une manière intégrée de gérer la **réplication des conteneurs** tout en supportant l'**équilibrage de charge** des requêtes entrantes. Le tout au **niveau du cluster**.
@ -499,7 +499,7 @@ Bien sûr, il existe des **cas particuliers** où vous pourriez vouloir avoir **
Dans ces cas, vous pouvez utiliser l'option de ligne de commande `--workers` pour définir le nombre de workers que vous souhaitez exécuter :
```{ .dockerfile .annotate }
FROM python:3.9
FROM python:3.14
WORKDIR /code

View File

@ -0,0 +1,65 @@
# FastAPI Cloud { #fastapi-cloud }
Vous pouvez déployer votre application FastAPI sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a> avec une **seule commande**, allez vous inscrire sur la liste dattente si ce nest pas déjà fait. 🚀
## Se connecter { #login }
Vous devez vous assurer que vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste dattente 😉).
Connectez-vous ensuite :
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
## Déployer { #deploy }
Déployez maintenant votre application, avec une **seule commande** :
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
Cest tout ! Vous pouvez maintenant accéder à votre application à cette URL. ✨
## À propos de FastAPI Cloud { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** est développé par le même auteur et la même équipe à lorigine de **FastAPI**.
Cela simplifie le processus de **création**, de **déploiement** et **daccès** à une API avec un effort minimal.
Cela apporte la même **expérience développeur** que pour créer des applications avec FastAPI au **déploiement** dans le cloud. 🎉
Cela prend également en charge la plupart des éléments nécessaires lors du déploiement dune application, notamment :
* HTTPS
* Réplication, avec mise à léchelle automatique basée sur les requêtes
* etc.
FastAPI Cloud est le sponsor principal et le financeur des projets open source *FastAPI and friends*. ✨
## Déployer sur dautres fournisseurs cloud { #deploy-to-other-cloud-providers }
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI sur nimporte quel fournisseur cloud de votre choix.
Suivez les guides de votre fournisseur cloud pour déployer des applications FastAPI avec eux. 🤓
## Déployer votre propre serveur { #deploy-your-own-server }
Je vous expliquerai également plus loin dans ce guide de **Déploiement** tous les détails, afin que vous compreniez ce qui se passe, ce qui doit être fait, et comment déployer des applications FastAPI par vous-même, y compris sur vos propres serveurs. 🤓

View File

@ -65,7 +65,7 @@ Voici un exemple de ce à quoi pourrait ressembler une API HTTPS, étape par ét
Tout commencerait probablement par le fait que vous **acquériez** un **nom de domaine**. Ensuite, vous le configureriez dans un serveur DNS (possiblement le même que votre fournisseur cloud).
Vous obtiendriez probablement un serveur cloud (une machine virtuelle) ou quelque chose de similaire, et il aurait une adresse IP **publique** <abbr title="Qui ne change pas">fixe</abbr>.
Vous obtiendriez probablement un serveur cloud (une machine virtuelle) ou quelque chose de similaire, et il aurait une adresse IP publique <dfn title="Ne change pas dans le temps. Pas dynamique.">fixe</dfn>.
Dans le ou les serveurs DNS, vous configureriez un enregistrement (un « `A record` ») pour faire pointer **votre domaine** vers l'**adresse IP publique de votre serveur**.

View File

@ -0,0 +1,139 @@
# Workers du serveur - Uvicorn avec workers { #server-workers-uvicorn-with-workers }
Reprenons ces concepts de déploiement vus précédemment :
* Sécurité - HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes préalables avant le démarrage
Jusqu'à présent, avec tous les tutoriels dans les documents, vous avez probablement exécuté un programme serveur, par exemple avec la commande `fastapi`, qui lance Uvicorn en exécutant un seul processus.
Lors du déploiement d'applications, vous voudrez probablement avoir une réplication de processus pour tirer parti de plusieurs cœurs et pouvoir gérer davantage de requêtes.
Comme vous l'avez vu dans le chapitre précédent sur les [Concepts de déploiement](concepts.md){.internal-link target=_blank}, il existe plusieurs stratégies possibles.
Ici, je vais vous montrer comment utiliser Uvicorn avec des processus workers en utilisant la commande `fastapi` ou directement la commande `uvicorn`.
/// info | Info
Si vous utilisez des conteneurs, par exemple avec Docker ou Kubernetes, je vous en dirai plus à ce sujet dans le prochain chapitre : [FastAPI dans des conteneurs - Docker](docker.md){.internal-link target=_blank}.
En particulier, lorsque vous exécutez sur Kubernetes, vous ne voudrez probablement pas utiliser de workers et plutôt exécuter un seul processus Uvicorn par conteneur, mais je vous en parlerai plus en détail dans ce chapitre.
///
## Utiliser plusieurs workers { #multiple-workers }
Vous pouvez démarrer plusieurs workers avec l'option de ligne de commande `--workers` :
//// tab | `fastapi`
Si vous utilisez la commande `fastapi` :
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
////
//// tab | `uvicorn`
Si vous préférez utiliser directement la commande `uvicorn` :
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
<font color="#A6E22E">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8080</b> (Press CTRL+C to quit)
<font color="#A6E22E">INFO</font>: Started parent process [<font color="#A1EFE4"><b>27365</b></font>]
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27368</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27369</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27370</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27367</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
```
</div>
////
La seule option nouvelle ici est `--workers` qui indique à Uvicorn de démarrer 4 processus workers.
Vous pouvez aussi voir qu'il affiche le PID de chaque processus, `27365` pour le processus parent (c'est le gestionnaire de processus) et un pour chaque processus worker : `27368`, `27369`, `27370` et `27367`.
## Concepts de déploiement { #deployment-concepts }
Ici, vous avez vu comment utiliser plusieurs workers pour paralléliser l'exécution de l'application, tirer parti de plusieurs cœurs du CPU et être en mesure de servir davantage de requêtes.
Dans la liste des concepts de déploiement ci-dessus, l'utilisation de workers aide principalement à la partie réplication, et un peu aux redémarrages, mais vous devez toujours vous occuper des autres :
* Sécurité - HTTPS
* Exécution au démarrage
* ***Redémarrages***
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes préalables avant le démarrage
## Conteneurs et Docker { #containers-and-docker }
Dans le prochain chapitre sur [FastAPI dans des conteneurs - Docker](docker.md){.internal-link target=_blank}, j'expliquerai quelques stratégies que vous pourriez utiliser pour gérer les autres concepts de déploiement.
Je vous montrerai comment créer votre propre image à partir de zéro pour exécuter un seul processus Uvicorn. C'est un processus simple et c'est probablement ce que vous voudrez faire lorsque vous utilisez un système distribué de gestion de conteneurs comme Kubernetes.
## Récapitulatif { #recap }
Vous pouvez utiliser plusieurs processus workers avec l'option CLI `--workers` des commandes `fastapi` ou `uvicorn` pour tirer parti des CPU multicœurs, et exécuter plusieurs processus en parallèle.
Vous pourriez utiliser ces outils et idées si vous mettez en place votre propre système de déploiement tout en prenant vous-même en charge les autres concepts de déploiement.
Consultez le prochain chapitre pour en savoir plus sur FastAPI avec des conteneurs (par exemple Docker et Kubernetes). Vous verrez que ces outils offrent aussi des moyens simples de résoudre les autres concepts de déploiement. ✨

View File

@ -0,0 +1,298 @@
# Variables d'environnement { #environment-variables }
/// tip | Astuce
Si vous savez déjà ce que sont les « variables d'environnement » et comment les utiliser, vous pouvez passer cette section.
///
Une variable d'environnement (également appelée « env var ») est une variable qui vit en dehors du code Python, dans le système d'exploitation, et qui peut être lue par votre code Python (ou par d'autres programmes également).
Les variables d'environnement peuvent être utiles pour gérer des **paramètres** d'application, dans le cadre de l'**installation** de Python, etc.
## Créer et utiliser des variables d'environnement { #create-and-use-env-vars }
Vous pouvez créer et utiliser des variables d'environnement dans le **shell (terminal)**, sans avoir besoin de Python :
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
// Vous pouvez créer une variable d'environnement MY_NAME avec
$ export MY_NAME="Wade Wilson"
// Vous pouvez ensuite l'utiliser avec d'autres programmes, par exemple
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
// Créer une variable d'environnement MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// L'utiliser avec d'autres programmes, par exemple
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
</div>
////
## Lire des variables d'environnement en Python { #read-env-vars-in-python }
Vous pouvez également créer des variables d'environnement **en dehors** de Python, dans le terminal (ou par tout autre moyen), puis les **lire en Python**.
Par exemple, vous pouvez avoir un fichier `main.py` contenant :
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Astuce
Le deuxième argument de <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> est la valeur par défaut à retourner.
S'il n'est pas fourni, c'est `None` par défaut ; ici, nous fournissons `"World"` comme valeur par défaut à utiliser.
///
Vous pouvez ensuite exécuter ce programme Python :
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
// Ici, nous ne définissons pas encore la variable d'environnement
$ python main.py
// Comme nous ne l'avons pas définie, nous obtenons la valeur par défaut
Hello World from Python
// Mais si nous créons d'abord une variable d'environnement
$ export MY_NAME="Wade Wilson"
// Puis que nous relançons le programme
$ python main.py
// Il peut maintenant lire la variable d'environnement
Hello Wade Wilson from Python
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
// Ici, nous ne définissons pas encore la variable d'environnement
$ python main.py
// Comme nous ne l'avons pas définie, nous obtenons la valeur par défaut
Hello World from Python
// Mais si nous créons d'abord une variable d'environnement
$ $Env:MY_NAME = "Wade Wilson"
// Puis que nous relançons le programme
$ python main.py
// Il peut maintenant lire la variable d'environnement
Hello Wade Wilson from Python
```
</div>
////
Comme les variables d'environnement peuvent être définies en dehors du code, mais lues par le code, et qu'elles n'ont pas besoin d'être stockées (validées dans `git`) avec le reste des fichiers, il est courant de les utiliser pour les configurations ou les **paramètres**.
Vous pouvez également créer une variable d'environnement uniquement pour l'**invocation d'un programme spécifique**, qui ne sera disponible que pour ce programme et uniquement pendant sa durée d'exécution.
Pour cela, créez-la juste avant le programme, sur la même ligne :
<div class="termy">
```console
// Créer en ligne une variable d'environnement MY_NAME pour cet appel de programme
$ MY_NAME="Wade Wilson" python main.py
// Il peut maintenant lire la variable d'environnement
Hello Wade Wilson from Python
// La variable d'environnement n'existe plus ensuite
$ python main.py
Hello World from Python
```
</div>
/// tip | Astuce
Vous pouvez en lire davantage sur <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App : Config</a>.
///
## Gérer les types et la validation { #types-and-validation }
Ces variables d'environnement ne peuvent gérer que des **chaînes de texte**, car elles sont externes à Python et doivent être compatibles avec les autres programmes et le reste du système (et même avec différents systèmes d'exploitation, comme Linux, Windows, macOS).
Cela signifie que **toute valeur** lue en Python à partir d'une variable d'environnement **sera une `str`**, et que toute conversion vers un autre type ou toute validation doit être effectuée dans le code.
Vous en apprendrez davantage sur l'utilisation des variables d'environnement pour gérer les **paramètres d'application** dans le [Guide utilisateur avancé - Paramètres et variables d'environnement](./advanced/settings.md){.internal-link target=_blank}.
## Variable d'environnement `PATH` { #path-environment-variable }
Il existe une **variable d'environnement spéciale** appelée **`PATH`** qui est utilisée par les systèmes d'exploitation (Linux, macOS, Windows) pour trouver les programmes à exécuter.
La valeur de la variable `PATH` est une longue chaîne composée de répertoires séparés par deux-points `:` sous Linux et macOS, et par point-virgule `;` sous Windows.
Par exemple, la variable d'environnement `PATH` peut ressembler à ceci :
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Cela signifie que le système doit rechercher les programmes dans les répertoires :
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Cela signifie que le système doit rechercher les programmes dans les répertoires :
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Lorsque vous tapez une **commande** dans le terminal, le système d'exploitation **cherche** le programme dans **chacun de ces répertoires** listés dans la variable d'environnement `PATH`.
Par exemple, lorsque vous tapez `python` dans le terminal, le système d'exploitation cherche un programme nommé `python` dans le **premier répertoire** de cette liste.
S'il le trouve, alors il **l'utilise**. Sinon, il continue à chercher dans les **autres répertoires**.
### Installer Python et mettre à jour `PATH` { #installing-python-and-updating-the-path }
Lorsque vous installez Python, il est possible que l'on vous demande si vous souhaitez mettre à jour la variable d'environnement `PATH`.
//// tab | Linux, macOS
Supposons que vous installiez Python et qu'il se retrouve dans un répertoire `/opt/custompython/bin`.
Si vous acceptez de mettre à jour la variable d'environnement `PATH`, l'installateur ajoutera `/opt/custompython/bin` à la variable d'environnement `PATH`.
Cela pourrait ressembler à ceci :
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Ainsi, lorsque vous tapez `python` dans le terminal, le système trouvera le programme Python dans `/opt/custompython/bin` (le dernier répertoire) et utilisera celui-là.
////
//// tab | Windows
Supposons que vous installiez Python et qu'il se retrouve dans un répertoire `C:\opt\custompython\bin`.
Si vous acceptez de mettre à jour la variable d'environnement `PATH`, l'installateur ajoutera `C:\opt\custompython\bin` à la variable d'environnement `PATH`.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Ainsi, lorsque vous tapez `python` dans le terminal, le système trouvera le programme Python dans `C:\opt\custompython\bin` (le dernier répertoire) et utilisera celui-là.
////
Ainsi, si vous tapez :
<div class="termy">
```console
$ python
```
</div>
//// tab | Linux, macOS
Le système va **trouver** le programme `python` dans `/opt/custompython/bin` et l'exécuter.
Cela reviendrait à peu près à taper :
<div class="termy">
```console
$ /opt/custompython/bin/python
```
</div>
////
//// tab | Windows
Le système va **trouver** le programme `python` dans `C:\opt\custompython\bin\python` et l'exécuter.
Cela reviendrait à peu près à taper :
<div class="termy">
```console
$ C:\opt\custompython\bin\python
```
</div>
////
Ces informations vous seront utiles lors de l'apprentissage des [Environnements virtuels](virtual-environments.md){.internal-link target=_blank}.
## Conclusion { #conclusion }
Avec cela, vous devriez avoir une compréhension de base de ce que sont les **variables d'environnement** et de la façon de les utiliser en Python.
Vous pouvez également en lire davantage sur la <a href="https://en.wikipedia.org/wiki/Environment_variable" class="external-link" target="_blank">page Wikipédia dédiée aux variables d'environnement</a>.
Dans de nombreux cas, il n'est pas évident de voir immédiatement en quoi les variables d'environnement seraient utiles et applicables. Mais elles réapparaissent dans de nombreux scénarios lorsque vous développez, il est donc bon de les connaître.
Par exemple, vous aurez besoin de ces informations dans la section suivante, sur les [Environnements virtuels](virtual-environments.md).

View File

@ -0,0 +1,75 @@
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** est un programme en ligne de commande que vous pouvez utiliser pour servir votre application FastAPI, gérer votre projet FastAPI, et plus encore.
Lorsque vous installez FastAPI (par exemple avec `pip install "fastapi[standard]"`), cela inclut un package appelé `fastapi-cli` ; ce package fournit la commande `fastapi` dans le terminal.
Pour exécuter votre application FastAPI en développement, vous pouvez utiliser la commande `fastapi dev` :
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
Le programme en ligne de commande nommé `fastapi` est **FastAPI CLI**.
FastAPI CLI prend le chemin vers votre programme Python (par exemple `main.py`), détecte automatiquement linstance `FastAPI` (généralement nommée `app`), détermine la procédure dimportation correcte, puis la sert.
Pour la production, vous utiliserez plutôt `fastapi run`. 🚀
En interne, **FastAPI CLI** utilise <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, un serveur ASGI haute performance, prêt pour la production. 😎
## `fastapi dev` { #fastapi-dev }
Lexécution de `fastapi dev` lance le mode développement.
Par défaut, l**auto-reload** est activé et recharge automatiquement le serveur lorsque vous modifiez votre code. Cela consomme des ressources et peut être moins stable que lorsquil est désactivé. Vous devez lutiliser uniquement pour le développement. Il écoute aussi sur ladresse IP `127.0.0.1`, qui est ladresse IP permettant à votre machine de communiquer uniquement avec ellemême (`localhost`).
## `fastapi run` { #fastapi-run }
Exécuter `fastapi run` démarre FastAPI en mode production par défaut.
Par défaut, l**auto-reload** est désactivé. Il écoute aussi sur ladresse IP `0.0.0.0`, ce qui signifie toutes les adresses IP disponibles ; de cette manière, il sera accessible publiquement à toute personne pouvant communiquer avec la machine. Cest ainsi que vous lexécutez normalement en production, par exemple dans un conteneur.
Dans la plupart des cas, vous avez (et devez avoir) un « termination proxy » audessus qui gère le HTTPS pour vous ; cela dépend de la façon dont vous déployez votre application : votre fournisseur peut le faire pour vous, ou vous devrez le configurer vousmême.
/// tip | Astuce
Vous pouvez en savoir plus à ce sujet dans la [documentation de déploiement](deployment/index.md){.internal-link target=_blank}.
///

View File

@ -1,43 +1,43 @@
# Fonctionnalités
# Fonctionnalités { #features }
## Fonctionnalités de FastAPI
## Fonctionnalités de FastAPI { #fastapi-features }
**FastAPI** vous offre ceci:
**FastAPI** vous offre les éléments suivants :
### Basé sur des standards ouverts
### Basé sur des standards ouverts { #based-on-open-standards }
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> pour la création d'API, incluant la déclaration de <abbr title="en français: routes. Aussi connu sous le nom anglais endpoints ou routes">path</abbr> <abbr title="Aussi connu sous le nom de méthodes HTTP. À savoir POST, GET, PUT, DELETE">operations</abbr>, paramètres, corps de requêtes, sécurité, etc.
* Documentation automatique des modèles de données avec <a href="http://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (comme OpenAPI est aussi basée sur JSON Schema).
* Conçue avec ces standards après une analyse méticuleuse. Plutôt qu'en rajoutant des surcouches après coup.
* Cela permet d'utiliser de la **génération automatique de code client** dans beaucoup de langages.
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> pour la création d'API, incluant la déclaration de <dfn title="aussi connu comme : endpoints, routes">chemin</dfn> <dfn title="aussi connu comme méthodes HTTP, comme POST, GET, PUT, DELETE">opérations</dfn>, paramètres, corps de requêtes, sécurité, etc.
* Documentation automatique des modèles de données avec <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (puisque OpenAPI est lui-même basé sur JSON Schema).
* Conçu autour de ces standards, après une étude méticuleuse. Plutôt qu'une couche ajoutée après coup.
* Cela permet également d'utiliser la **génération automatique de code client** dans de nombreux langages.
### Documentation automatique
### Documentation automatique { #automatic-docs }
Documentation d'API interactive et interface web d'exploration. Comme le framework est basé sur OpenAPI, de nombreuses options sont disponibles. Deux d'entre-elles sont incluses par défaut.
Documentation d'API interactive et interfaces web d'exploration. Comme le framework est basé sur OpenAPI, plusieurs options existent, 2 incluses par défaut.
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, propose une documentation interactive. Vous permet de directement tester l'API depuis votre navigateur.
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>, avec exploration interactive, appelez et testez votre API directement depuis le navigateur.
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Une autre documentation d'API est fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
* Documentation d'API alternative avec <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Faite en python moderne
### Uniquement du Python moderne { #just-modern-python }
Tout est basé sur la déclaration de type standard de **Python 3.8** (grâce à Pydantic). Pas de nouvelles syntaxes à apprendre. Juste du Python standard et moderne.
Tout est basé sur les déclarations de **types Python** standard (grâce à Pydantic). Aucune nouvelle syntaxe à apprendre. Juste du Python moderne standard.
Si vous souhaitez un rappel de 2 minutes sur l'utilisation des types en Python (même si vous ne comptez pas utiliser FastAPI), jetez un oeil au tutoriel suivant: [Python Types](python-types.md){.internal-link target=_blank}.
Si vous avez besoin d'un rappel de 2 minutes sur l'utilisation des types en Python (même si vous n'utilisez pas FastAPI), consultez le court tutoriel : [Types Python](python-types.md){.internal-link target=_blank}.
Vous écrivez du python standard avec des annotations de types:
Vous écrivez du Python standard avec des types :
```Python
from datetime import date
from pydantic import BaseModel
# Déclare une variable comme étant une str
# et profitez de l'aide de votre IDE dans cette fonction
# Déclarez une variable comme étant une str
# et profitez de l'aide de l'éditeur dans cette fonction
def main(user_id: str):
return user_id
@ -48,7 +48,8 @@ class User(BaseModel):
name: str
joined: date
```
Qui peuvent ensuite être utilisés comme cela:
Qui peuvent ensuite être utilisés comme ceci :
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
@ -64,138 +65,137 @@ my_second_user: User = User(**second_user_data)
/// info
`**second_user_data` signifie:
`**second_user_data` signifie :
Utilise les clés et valeurs du dictionnaire `second_user_data` directement comme des arguments clé-valeur. C'est équivalent à: `User(id=4, name="Mary", joined="2018-11-30")`
Passez les clés et valeurs du dictionnaire `second_user_data` directement comme arguments clé-valeur, équivalent à : `User(id=4, name="Mary", joined="2018-11-30")`
///
### Support d'éditeurs
### Support des éditeurs { #editor-support }
Tout le framework a été conçu pour être facile et intuitif d'utilisation, toutes les décisions de design ont été testées sur de nombreux éditeurs avant même de commencer le développement final afin d'assurer la meilleure expérience de développement possible.
Tout le framework a été conçu pour être facile et intuitif à utiliser, toutes les décisions ont été testées sur plusieurs éditeurs avant même de commencer le développement, pour assurer la meilleure expérience de développement.
Dans le dernier sondage effectué auprès de développeurs python il était clair que <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">la fonctionnalité la plus utilisée est "lautocomplétion"</a>.
Dans les enquêtes auprès des développeurs Python, il est clair <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">que lune des fonctionnalités les plus utilisées est « autocomplétion »</a>.
Tout le framework **FastAPI** a été conçu avec cela en tête. L'autocomplétion fonctionne partout.
L'ensemble du framework **FastAPI** est conçu pour satisfaire cela. L'autocomplétion fonctionne partout.
Vous devrez rarement revenir à la documentation.
Vous aurez rarement besoin de revenir aux documents.
Voici comment votre éditeur peut vous aider:
Voici comment votre éditeur peut vous aider :
* dans <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>:
* dans <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a> :
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
* dans <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>:
* dans <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> :
![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png)
Vous aurez des propositions de complétion que vous n'auriez jamais imaginées. Par exemple la clé `prix` dans le corps d'un document JSON (qui est peut-être imbriqué) venant d'une requête.
Vous obtiendrez de l'autocomplétion dans du code que vous auriez pu considérer impossible auparavant. Par exemple, la clé `price` à l'intérieur d'un corps JSON (qui aurait pu être imbriqué) provenant d'une requête.
Plus jamais vous ne vous tromperez en tapant le nom d'une clé, vous ne ferez des aller-retour entre votre code et la documentation ou vous ne scrollerez de haut en bas afin d'enfin savoir si vous devez taper `username` ou `user_name`.
Fini de taper des noms de clés erronés, de faire des allers-retours entre les documents, ou de faire défiler vers le haut et vers le bas pour savoir si vous avez finalement utilisé `username` ou `user_name`.
### Court
### Court { #short }
Des **valeurs par défaut** sont définies pour tout, des configurations optionnelles sont présentent partout. Tous ces paramètres peuvent être ajustés afin de faire ce que vous voulez et définir l'API dont vous avez besoin.
Des **valeurs par défaut** sensées pour tout, avec des configurations optionnelles partout. Tous les paramètres peuvent être ajustés finement pour faire ce dont vous avez besoin et définir l'API dont vous avez besoin.
Mais, **tout fonctionne** par défaut.
Mais par défaut, tout **« just works »**.
### Validation
### Validation { #validation }
* Validation pour la plupart (ou tous?) les **types de données** Python incluant:
* Validation pour la plupart (ou tous ?) des **types de données** Python, y compris :
* objets JSON (`dict`).
* listes JSON (`list`) définissant des types d'éléments.
* Champs String (`str`), définition de longueur minimum ou maximale.
* Nombres (`int`, `float`) avec valeur minimale and maximale, etc.
* tableaux JSON (`list`) définissant les types d'éléments.
* champs String (`str`), définition des longueurs minimale et maximale.
* nombres (`int`, `float`) avec valeurs minimale et maximale, etc.
* Validation pour des types plus exotiques, tel que:
* Validation pour des types plus exotiques, comme :
* URL.
* Email.
* UUID.
* ...et autres.
* ... et autres.
Toutes les validations sont gérées par le bien établi et robuste **Pydantic**.
Toutes les validations sont gérées par le **Pydantic** bien établi et robuste.
### Sécurité et authentification
### Sécurité et authentification { #security-and-authentication }
La sécurité et l'authentification sont intégrées. Sans aucun compromis avec les bases de données ou les modèles de données.
Sécurité et authentification intégrées. Sans aucun compromis avec les bases de données ou les modèles de données.
Tous les protocoles de sécurités sont définis dans OpenAPI, incluant:
Tous les schémas de sécurité définis dans OpenAPI, y compris :
* HTTP Basic.
* **OAuth2** (aussi avec **JWT tokens**). Jetez un oeil au tutoriel [OAuth2 avec JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
* Clés d'API dans:
* Le header.
* Les paramètres de requêtes.
* Les cookies, etc.
* **OAuth2** (également avec des **tokens JWT**). Consultez le tutoriel [OAuth2 avec JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
* Clés d'API dans :
* les en-têtes.
* les paramètres de requête.
* les cookies, etc.
Plus toutes les fonctionnalités de sécurités venant de Starlette (incluant les **cookies de sessions**).
Plus toutes les fonctionnalités de sécurité de Starlette (y compris les **cookies de session**).
Le tout conçu en composant réutilisable facilement intégrable à vos systèmes, data stores, base de données relationnelle ou NoSQL, etc.
Le tout construit comme des outils et composants réutilisables, faciles à intégrer à vos systèmes, magasins de données, bases de données relationnelles et NoSQL, etc.
### Injection de dépendances
### Injection de dépendances { #dependency-injection }
FastAPI contient un système simple mais extrêmement puissant d'<abbr title='aussi connus sous le nom de "composants", "ressources", "services", "providers"'><strong>Injection de Dépendances</strong></abbr>.
FastAPI inclut un système d<dfn title='aussi connu sous le nom de « composants », « ressources », « services », « fournisseurs »'><strong>Injection de dépendances</strong></dfn> extrêmement simple à utiliser, mais extrêmement puissant.
* Même les dépendances peuvent avoir des dépendances, créant une hiérarchie ou un **"graph" de dépendances**
* Tout est **automatiquement géré** par le framework
* Toutes les dépendances peuvent exiger des données d'une requêtes et **Augmenter les contraintes d'un path operation** et de la documentation automatique.
* **Validation automatique** même pour les paramètres de *path operation* définis dans les dépendances.
* Supporte les systèmes d'authentification d'utilisateurs complexes, les **connexions de base de données**, etc.
* **Aucun compromis** avec les bases de données, les frontends, etc. Mais une intégration facile avec n'importe lequel d'entre eux.
* Même les dépendances peuvent avoir des dépendances, créant une hiérarchie ou un **« graphe » de dépendances**.
* Le tout **géré automatiquement** par le framework.
* Toutes les dépendances peuvent exiger des données des requêtes et **augmenter les contraintes du chemin d'accès** ainsi que la documentation automatique.
* **Validation automatique** même pour les paramètres de *chemin d'accès* définis dans les dépendances.
* Prise en charge des systèmes d'authentification d'utilisateurs complexes, des **connexions de base de données**, etc.
* **Aucun compromis** avec les bases de données, les frontends, etc. Mais une intégration facile avec tous.
### "Plug-ins" illimités
### « Plug-ins » illimités { #unlimited-plug-ins }
Ou, en d'autres termes, pas besoin d'eux, importez le code que vous voulez et utilisez le.
Ou, autrement dit, pas besoin d'eux, importez et utilisez le code dont vous avez besoin.
Tout intégration est conçue pour être si simple à utiliser (avec des dépendances) que vous pouvez créer un "plug-in" pour votre application en deux lignes de code utilisant la même syntaxe que celle de vos *path operations*
Toute intégration est conçue pour être si simple à utiliser (avec des dépendances) que vous pouvez créer un « plug-in » pour votre application en 2 lignes de code en utilisant la même structure et la même syntaxe que pour vos *chemins d'accès*.
### Testé
### Testé { #tested }
* 100% <abbr title="La quantité de code qui est testé automatiquement">de couverture de test</abbr>.
* 100% <abbr title="Annotation de types Python, avec cela votre éditeur et autres outils externes peuvent vous fournir un meilleur support">d'annotations de type</abbr> dans le code.
* Utilisé dans des applications mises en production.
* 100 % de <dfn title="La quantité de code testée automatiquement">couverture de test</dfn>.
* 100 % de base de code <dfn title="Annotations de type Python ; avec cela votre éditeur et les outils externes peuvent vous offrir un meilleur support">annotée avec des types</dfn>.
* Utilisé dans des applications en production.
## Fonctionnalités de Starlette
## Fonctionnalités de Starlette { #starlette-features }
**FastAPI** est complètement compatible (et basé sur) <a href="https://www.starlette.dev/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Le code utilisant Starlette que vous ajouterez fonctionnera donc aussi.
**FastAPI** est entièrement compatible avec (et basé sur) <a href="https://www.starlette.dev/" class="external-link" target="_blank"><strong>Starlette</strong></a>. Donc, tout code Starlette additionnel que vous avez fonctionnera aussi.
En fait, `FastAPI` est un sous composant de `Starlette`. Donc, si vous savez déjà comment utiliser Starlette, la plupart des fonctionnalités fonctionneront de la même manière.
`FastAPI` est en fait une sous-classe de `Starlette`. Ainsi, si vous connaissez ou utilisez déjà Starlette, la plupart des fonctionnalités fonctionneront de la même manière.
Avec **FastAPI** vous aurez toutes les fonctionnalités de **Starlette** (FastAPI est juste Starlette sous stéroïdes):
Avec **FastAPI** vous obtenez toutes les fonctionnalités de **Starlette** (puisque FastAPI est juste Starlette sous stéroïdes) :
* Des performances vraiment impressionnantes. C'est l'<a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">un des framework Python les plus rapide, à égalité avec **NodeJS** et **GO**</a>.
* Le support des **WebSockets**.
* Le support de **GraphQL**.
* Les <abbr title="En anglais: In-process background tasks">tâches d'arrière-plan.</abbr>
* Des évènements de démarrages et d'arrêt.
* Un client de test basé sur `request`
* **CORS**, GZip, Static Files, Streaming responses.
* Le support des **Sessions et Cookies**.
* Une couverture de test à 100 %.
* 100 % de la base de code avec des annotations de type.
* Des performances vraiment impressionnantes. C'est <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">lun des frameworks Python les plus rapides disponibles, à légal de **NodeJS** et **Go**</a>.
* Prise en charge des **WebSocket**.
* Tâches d'arrière-plan dans le processus.
* Évènements de démarrage et d'arrêt.
* Client de test basé sur HTTPX.
* **CORS**, GZip, fichiers statiques, réponses en streaming.
* Prise en charge des **Sessions et Cookies**.
* Couverture de test à 100 %.
* Base de code annotée à 100 % avec des types.
## Fonctionnalités de Pydantic
## Fonctionnalités de Pydantic { #pydantic-features }
**FastAPI** est totalement compatible avec (et basé sur) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Le code utilisant Pydantic que vous ajouterez fonctionnera donc aussi.
**FastAPI** est entièrement compatible avec (et basé sur) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Donc, tout code Pydantic additionnel que vous avez fonctionnera aussi.
Inclus des librairies externes basées, aussi, sur Pydantic, servent d'<abbr title="Object-Relational Mapper">ORM</abbr>s, <abbr title="Object-Document Mapper">ODM</abbr>s pour les bases de données.
Y compris des bibliothèques externes également basées sur Pydantic, servant d<abbr title="Object-Relational Mapper - Mappeur objet-relationnel">ORM</abbr>, d<abbr title="Object-Document Mapper - Mappeur objet-document">ODM</abbr> pour les bases de données.
Cela signifie aussi que, dans la plupart des cas, vous pouvez fournir l'objet reçu d'une requête **directement à la base de données**, comme tout est validé automatiquement.
Cela signifie également que, dans de nombreux cas, vous pouvez passer l'objet que vous recevez d'une requête **directement à la base de données**, puisque tout est validé automatiquement.
Inversement, dans la plupart des cas vous pourrez juste envoyer l'objet récupéré de la base de données **directement au client**
Linverse est également vrai, dans de nombreux cas, vous pouvez simplement passer l'objet que vous récupérez de la base de données **directement au client**.
Avec **FastAPI** vous aurez toutes les fonctionnalités de **Pydantic** (comme FastAPI est basé sur Pydantic pour toutes les manipulations de données):
Avec **FastAPI** vous obtenez toutes les fonctionnalités de **Pydantic** (puisque FastAPI est basé sur Pydantic pour toute la gestion des données) :
* **Pas de prise de tête**:
* Pas de nouveau langage de définition de schéma à apprendre.
* Si vous connaissez le typage en python vous savez comment utiliser Pydantic.
* Aide votre **<abbr title="Integrated Development Environment, il s'agit de votre éditeur de code">IDE</abbr>/<abbr title="Programme qui analyse le code à la recherche d'erreurs">linter</abbr>/cerveau**:
* Parce que les structures de données de pydantic consistent seulement en une instance de classe que vous définissez; l'auto-complétion, le linting, mypy et votre intuition devrait être largement suffisante pour valider vos données.
* Valide les **structures complexes**:
* Utilise les modèles hiérarchique de Pydantic, le `typage` Python pour les `Lists`, `Dict`, etc.
* Et les validateurs permettent aux schémas de données complexes d'être clairement et facilement définis, validés et documentés sous forme d'un schéma JSON.
* Vous pouvez avoir des objets **JSON fortement imbriqués** tout en ayant, pour chacun, de la validation et des annotations.
* **Renouvelable**:
* Pydantic permet de définir de nouveaux types de données ou vous pouvez étendre la validation avec des méthodes sur un modèle décoré avec le<abbr title="en anglais: validator decorator"> décorateur de validation</abbr>
* 100% de couverture de test.
* **Pas de prise de tête** :
* Pas de micro-langage de définition de schéma à apprendre.
* Si vous connaissez les types Python vous savez utiliser Pydantic.
* Fonctionne bien avec votre **<abbr title="Integrated Development Environment - Environnement de développement intégré: similaire à un éditeur de code">IDE</abbr>/<dfn title="Programme qui vérifie les erreurs de code">linter</dfn>/cerveau** :
* Parce que les structures de données de Pydantic sont simplement des instances de classes que vous définissez ; l'autocomplétion, le linting, mypy et votre intuition devraient tous bien fonctionner avec vos données validées.
* Valider des **structures complexes** :
* Utilisation de modèles Pydantic hiérarchiques, de `List` et `Dict` du `typing` Python, etc.
* Et les validateurs permettent de définir, vérifier et documenter clairement et facilement des schémas de données complexes en tant que JSON Schema.
* Vous pouvez avoir des objets **JSON fortement imbriqués** et les faire tous valider et annoter.
* **Extensible** :
* Pydantic permet de définir des types de données personnalisés ou vous pouvez étendre la validation avec des méthodes sur un modèle décoré avec le décorateur de validation.
* Couverture de test à 100 %.

View File

@ -1,103 +1,255 @@
# Help FastAPI - Obtenir de l'aide
# Aider FastAPI - Obtenir de l'aide { #help-fastapi-get-help }
Aimez-vous **FastAPI** ?
Vous souhaitez aider FastAPI, les autres utilisateurs et l'auteur ?
Souhaitez-vous aider FastAPI, les autres utilisateurs et l'auteur ?
Ou souhaitez-vous obtenir de l'aide avec le **FastAPI** ?
Ou souhaitez-vous obtenir de l'aide avec **FastAPI** ?
Il existe des moyens très simples d'aider (plusieurs ne nécessitent qu'un ou deux clics).
Il existe également plusieurs façons d'obtenir de l'aide.
Et il existe aussi plusieurs façons d'obtenir de l'aide.
## Star **FastAPI** sur GitHub
## S'abonner à la newsletter { #subscribe-to-the-newsletter }
Vous pouvez "star" FastAPI dans GitHub (en cliquant sur le bouton étoile en haut à droite) : <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
Vous pouvez vous abonner à la (peu fréquente) [newsletter **FastAPI and friends**](newsletter.md){.internal-link target=_blank} pour rester informé à propos :
En ajoutant une étoile, les autres utilisateurs pourront la trouver plus facilement et constater qu'elle a déjà été utile à d'autres.
* Nouvelles sur FastAPI et ses amis 🚀
* Guides 📝
* Fonctionnalités ✨
* Changements majeurs 🚨
* Astuces et conseils ✅
## Watch le dépôt GitHub pour les releases
## Suivre FastAPI sur X (Twitter) { #follow-fastapi-on-x-twitter }
Vous pouvez "watch" FastAPI dans GitHub (en cliquant sur le bouton "watch" en haut à droite) : <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
<a href="https://x.com/fastapi" class="external-link" target="_blank">Suivez @fastapi sur **X (Twitter)**</a> pour obtenir les dernières nouvelles sur **FastAPI**. 🐦
Vous pouvez y sélectionner "Releases only".
## Mettre une étoile à **FastAPI** sur GitHub { #star-fastapi-in-github }
Ainsi, vous recevrez des notifications (dans votre courrier électronique) chaque fois qu'il y aura une nouvelle version de **FastAPI** avec des corrections de bugs et de nouvelles fonctionnalités.
Vous pouvez « star » FastAPI sur GitHub (en cliquant sur le bouton étoile en haut à droite) : <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. ⭐️
## Se rapprocher de l'auteur
En ajoutant une étoile, les autres utilisateurs pourront le trouver plus facilement et voir qu'il a déjà été utile à d'autres.
Vous pouvez vous rapprocher de <a href="https://tiangolo.com" class="external-link" target="_blank">moi (Sebastián Ramírez / `tiangolo`)</a>, l'auteur.
## Suivre le dépôt GitHub pour les releases { #watch-the-github-repository-for-releases }
Vous pouvez :
Vous pouvez « watch » FastAPI sur GitHub (en cliquant sur le bouton « watch » en haut à droite) : <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Vous pouvez y sélectionner « Releases only ».
Ainsi, vous recevrez des notifications (par email) chaque fois qu'il y aura une nouvelle release (une nouvelle version) de **FastAPI** avec des corrections de bugs et de nouvelles fonctionnalités.
## Entrer en contact avec l'auteur { #connect-with-the-author }
Vous pouvez entrer en contact avec <a href="https://tiangolo.com" class="external-link" target="_blank">moi (Sebastián Ramírez / `tiangolo`)</a>, l'auteur.
Vous pouvez :
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">Me suivre sur **GitHub**</a>.
* Voir d'autres projets Open Source que j'ai créés et qui pourraient vous aider.
* Suivez-moi pour voir quand je crée un nouveau projet Open Source.
* <a href="https://x.com/tiangolo" class="external-link" target="_blank">Me suivre sur **X (Twitter)**</a>.
* Dites-moi comment vous utilisez FastAPI (j'adore entendre ça).
* Entendre quand je fais des annonces ou que je lance de nouveaux outils.
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Vous connectez à moi sur **LinkedIn**</a>.
* Etre notifié quand je fais des annonces ou que je lance de nouveaux outils (bien que j'utilise plus souvent X (Twitter) 🤷‍♂).
* Lire ce que jécris (ou me suivre) sur <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> ou <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
* Lire d'autres idées, articles, et sur les outils que j'ai créés.
* Suivez-moi pour lire quand je publie quelque chose de nouveau.
* Me suivre pour voir quand je crée un nouveau projet Open Source.
* <a href="https://x.com/tiangolo" class="external-link" target="_blank">Me suivre sur **X (Twitter)**</a> ou sur <a href="https://fosstodon.org/@tiangolo" class="external-link" target="_blank">Mastodon</a>.
* Me dire comment vous utilisez FastAPI (j'adore l'entendre).
* Être informé quand je fais des annonces ou publie de nouveaux outils.
* Vous pouvez aussi <a href="https://x.com/fastapi" class="external-link" target="_blank">suivre @fastapi sur X (Twitter)</a> (un compte séparé).
* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Me suivre sur **LinkedIn**</a>.
* Être informé quand je fais des annonces ou publie de nouveaux outils (même si j'utilise plus souvent X (Twitter) 🤷‍♂).
* Lire ce que j'écris (ou me suivre) sur <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> ou <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
* Lire d'autres idées, des articles, et découvrir des outils que j'ai créés.
* Me suivre pour lire quand je publie quelque chose de nouveau.
## Tweeter sur **FastAPI**
## Tweeter à propos de **FastAPI** { #tweet-about-fastapi }
<a href="https://x.com/compose/tweet?text=I'm loving FastAPI because... https://github.com/fastapi/fastapi cc @tiangolo" class="external-link" target="_blank">Tweetez à propos de **FastAPI**</a> et faites-moi savoir, ainsi qu'aux autres, pourquoi vous aimez ça. 🎉
<a href="https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi" class="external-link" target="_blank">Tweetez à propos de **FastAPI**</a> et faites savoir à moi et aux autres pourquoi vous l'appréciez. 🎉
J'aime entendre parler de l'utilisation du **FastAPI**, de ce que vous avez aimé dedans, dans quel projet/entreprise l'utilisez-vous, etc.
J'adore entendre comment **FastAPI** est utilisé, ce que vous avez aimé, dans quel projet/quelle entreprise vous l'utilisez, etc.
## Voter pour FastAPI
## Voter pour FastAPI { #vote-for-fastapi }
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Votez pour **FastAPI** sur Slant</a>.
* <a href="https://alternativeto.net/software/fastapi/" class="external-link" target="_blank">Votez pour **FastAPI** sur AlternativeTo</a>.
* <a href="https://github.com/marmelab/awesome-rest/pull/93" class="external-link" target="_blank">Votez pour **FastAPI** sur awesome-rest</a>.
* <a href="https://alternativeto.net/software/fastapi/about/" class="external-link" target="_blank">Votez pour **FastAPI** sur AlternativeTo</a>.
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">Indiquez que vous utilisez **FastAPI** sur StackShare</a>.
## Aider les autres à résoudre les problèmes dans GitHub
## Aider les autres avec des questions sur GitHub { #help-others-with-questions-in-github }
Vous pouvez voir <a href="https://github.com/fastapi/fastapi/issues" class="external-link" target="_blank">les problèmes existants</a> et essayer d'aider les autres, la plupart du temps il s'agit de questions dont vous connaissez peut-être déjà la réponse. 🤓
Vous pouvez essayer d'aider les autres avec leurs questions dans :
## Watch le dépôt GitHub
* <a href="https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered" class="external-link" target="_blank">GitHub Discussions</a>
* <a href="https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+" class="external-link" target="_blank">GitHub Issues</a>
Vous pouvez "watch" FastAPI dans GitHub (en cliquant sur le bouton "watch" en haut à droite) : <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Dans de nombreux cas, vous connaissez peut-être déjà la réponse à ces questions. 🤓
Si vous sélectionnez "Watching" au lieu de "Releases only", vous recevrez des notifications lorsque quelqu'un crée une nouvelle Issue.
Si vous aidez beaucoup de personnes avec leurs questions, vous deviendrez un [Expert FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank} officiel. 🎉
Vous pouvez alors essayer de les aider à résoudre ces problèmes.
N'oubliez pas, le point le plus important est : essayez d'être aimable. Les gens viennent avec leurs frustrations et, dans bien des cas, ne posent pas la question de la meilleure façon, mais faites de votre mieux pour rester aimable. 🤗
## Créer une Issue
L'idée est que la communauté **FastAPI** soit bienveillante et accueillante. En même temps, n'acceptez pas l'intimidation ni les comportements irrespectueux envers les autres. Nous devons prendre soin les uns des autres.
Vous pouvez <a href="https://github.com/fastapi/fastapi/issues/new/choose" class="external-link" target="_blank">créer une Issue</a> dans le dépôt GitHub, par exemple pour :
---
* Poser une question ou s'informer sur un problème.
* Suggérer une nouvelle fonctionnalité.
Voici comment aider les autres avec des questions (dans les discussions ou les issues) :
**Note** : si vous créez un problème, alors je vais vous demander d'aider aussi les autres. 😉
### Comprendre la question { #understand-the-question }
## Créer une Pull Request
* Vérifiez si vous comprenez quel est l**objectif** et le cas d'utilisation de la personne qui pose la question.
Vous pouvez <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">créer une Pull Request</a>, par exemple :
* Ensuite, vérifiez si la question (la grande majorité sont des questions) est **claire**.
* Pour corriger une faute de frappe que vous avez trouvée sur la documentation.
* Dans de nombreux cas, la question porte sur une solution imaginaire de l'utilisateur, mais il pourrait y en avoir une **meilleure**. Si vous comprenez mieux le problème et le cas d'utilisation, vous pourriez suggérer une **solution alternative** plus adaptée.
* Si vous ne comprenez pas la question, demandez plus de **détails**.
### Reproduire le problème { #reproduce-the-problem }
Dans la plupart des cas et pour la plupart des questions, il y a quelque chose lié au **code original** de la personne.
Dans de nombreux cas, elle ne copiera qu'un fragment de code, mais ce n'est pas suffisant pour **reproduire le problème**.
* Vous pouvez leur demander de fournir un <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">exemple minimal, complet et vérifiable</a>, que vous pouvez **copiercoller** et exécuter localement pour voir la même erreur ou le même comportement qu'ils observent, ou pour mieux comprendre leur cas d'utilisation.
* Si vous vous sentez très généreux, vous pouvez essayer de **créer un tel exemple** vousmême, simplement à partir de la description du problème. Gardez simplement à l'esprit que cela peut prendre beaucoup de temps et qu'il peut être préférable de leur demander d'abord de clarifier le problème.
### Suggérer des solutions { #suggest-solutions }
* Après avoir compris la question, vous pouvez leur donner une **réponse** possible.
* Dans de nombreux cas, il est préférable de comprendre leur **problème sousjacent ou cas d'utilisation**, car il pourrait exister une meilleure façon de le résoudre que ce qu'ils essaient de faire.
### Demander la clôture { #ask-to-close }
S'ils répondent, il y a de fortes chances que vous ayez résolu leur problème, bravo, **vous êtes un héros** ! 🦸
* Maintenant, si cela a résolu leur problème, vous pouvez leur demander de :
* Dans GitHub Discussions : marquer le commentaire comme **réponse**.
* Dans GitHub Issues : **fermer** l'issue.
## Suivre le dépôt GitHub { #watch-the-github-repository }
Vous pouvez « watch » FastAPI sur GitHub (en cliquant sur le bouton « watch » en haut à droite) : <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">https://github.com/fastapi/fastapi</a>. 👀
Si vous sélectionnez « Watching » au lieu de « Releases only », vous recevrez des notifications lorsque quelqu'un crée une nouvelle issue ou question. Vous pouvez aussi préciser que vous ne souhaitez être notifié que pour les nouvelles issues, ou les discussions, ou les PR, etc.
Vous pouvez alors essayer de les aider à résoudre ces questions.
## Poser des questions { #ask-questions }
Vous pouvez <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">créer une nouvelle question</a> dans le dépôt GitHub, par exemple pour :
* Poser une **question** ou demander à propos d'un **problème**.
* Suggérer une nouvelle **fonctionnalité**.
**Remarque** : si vous le faites, je vais vous demander d'aider aussi les autres. 😉
## Relire des Pull Requests { #review-pull-requests }
Vous pouvez m'aider à relire les pull requests des autres.
Encore une fois, essayez autant que possible d'être aimable. 🤗
---
Voici ce à garder à l'esprit et comment relire une pull request :
### Comprendre le problème { #understand-the-problem }
* D'abord, assurezvous de **comprendre le problème** que la pull request essaie de résoudre. Il peut y avoir une discussion plus longue dans une GitHub Discussion ou une issue.
* Il y a aussi de bonnes chances que la pull request ne soit pas réellement nécessaire parce que le problème peut être résolu d'une **autre manière**. Vous pouvez alors le suggérer ou poser la question.
### Ne pas s'inquiéter du style { #dont-worry-about-style }
* Ne vous souciez pas trop des choses comme les styles de messages de commit, je ferai un squash and merge en personnalisant le commit manuellement.
* Ne vous inquiétez pas non plus des règles de style, il existe déjà des outils automatisés qui vérifient cela.
Et s'il y a d'autres besoins de style ou de cohérence, je le demanderai directement, ou j'ajouterai des commits pardessus avec les changements nécessaires.
### Vérifier le code { #check-the-code }
* Vérifiez et lisez le code, voyez s'il a du sens, **exécutezle localement** et voyez s'il résout effectivement le problème.
* Ensuite, **commentez** en disant que vous l'avez fait, c'est ainsi que je saurai que vous l'avez vraiment vérifié.
/// info
Malheureusement, je ne peux pas simplement faire confiance aux PR qui ont juste plusieurs approbations.
Plusieurs fois, il est arrivé qu'il y ait des PR avec 3, 5 ou plus approbations, probablement parce que la description est attrayante, mais lorsque je vérifie les PR, elles sont en fait cassées, ont un bug, ou ne résolvent pas le problème qu'elles prétendent résoudre. 😅
Donc, il est vraiment important que vous lisiez et exécutiez le code, et que vous me le disiez dans les commentaires. 🤓
///
* Si la PR peut être simplifiée d'une certaine manière, vous pouvez le demander, mais il n'est pas nécessaire d'être trop pointilleux, il peut y avoir beaucoup de points de vue subjectifs (et j'aurai les miens aussi 🙈), donc il est préférable de vous concentrer sur les choses fondamentales.
### Tests { #tests }
* Aidezmoi à vérifier que la PR a des **tests**.
* Vérifiez que les tests **échouent** avant la PR. 🚨
* Puis vérifiez que les tests **réussissent** après la PR. ✅
* Beaucoup de PR n'ont pas de tests, vous pouvez leur **rappeler** d'ajouter des tests, ou même **suggérer** des tests vousmême. C'est l'une des choses qui consomment le plus de temps et vous pouvez beaucoup aider.
* Commentez aussi ce que vous avez essayé, ainsi je saurai que vous l'avez vérifié. 🤓
## Créer une Pull Request { #create-a-pull-request }
Vous pouvez [contribuer](contributing.md){.internal-link target=_blank} au code source avec des Pull Requests, par exemple :
* Corriger une coquille que vous avez trouvée dans la documentation.
* Partager un article, une vidéo ou un podcast que vous avez créé ou trouvé à propos de FastAPI en <a href="https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">modifiant ce fichier</a>.
* Vous devez vous assurer d'ajouter votre lien au début de la section correspondante.
* Aider à [traduire la documentation](contributing.md#translations){.internal-link target=_blank} dans votre langue.
* Vous pouvez aussi aider à relire les traductions créées par d'autres.
* Proposer de nouvelles sections de documentation.
* Pour corriger une Issue/Bug existant.
* Pour ajouter une nouvelle fonctionnalité.
* Corriger une issue/un bug existant.
* Vous devez ajouter des tests.
* Ajouter une nouvelle fonctionnalité.
* Vous devez ajouter des tests.
* Vous devez ajouter de la documentation si c'est pertinent.
## Parrainer l'auteur
## Aider à maintenir FastAPI { #help-maintain-fastapi }
Vous pouvez également soutenir financièrement l'auteur (moi) via <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>.
Aidezmoi à maintenir **FastAPI** ! 🤓
Là, vous pourriez m'offrir un café ☕️ pour me remercier 😄.
Il y a beaucoup de travail à faire, et pour la plupart, **VOUS** pouvez le faire.
## Sponsoriser les outils qui font fonctionner FastAPI
Les principales tâches que vous pouvez faire dès maintenant sont :
Comme vous l'avez vu dans la documentation, FastAPI se tient sur les épaules des géants, Starlette et Pydantic.
* [Aider les autres avec des questions sur GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (voir la section cidessus).
* [Relire des Pull Requests](#review-pull-requests){.internal-link target=_blank} (voir la section cidessus).
Vous pouvez également parrainer :
Ces deux tâches sont celles qui **consomment le plus de temps**. C'est le travail principal de la maintenance de FastAPI.
* <a href="https://github.com/sponsors/samuelcolvin" class="external-link" target="_blank">Samuel Colvin (Pydantic)</a>
* <a href="https://github.com/sponsors/encode" class="external-link" target="_blank">Encode (Starlette, Uvicorn)</a>
Si vous pouvez m'aider avec cela, **vous m'aidez à maintenir FastAPI** et à vous assurer qu'il continue **d'avancer plus vite et mieux**. 🚀
## Rejoindre le chat { #join-the-chat }
Rejoignez le 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">serveur Discord</a> 👥 et échangez avec d'autres membres de la communauté FastAPI.
/// tip | Astuce
Pour les questions, posezles dans <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions</a>, vous avez bien plus de chances de recevoir de l'aide par les [Experts FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}.
Utilisez le chat uniquement pour d'autres conversations générales.
///
### N'utilisez pas le chat pour les questions { #dont-use-the-chat-for-questions }
Gardez à l'esprit que, comme les chats permettent une « conversation libre », il est facile de poser des questions trop générales et plus difficiles à répondre ; vous pourriez donc ne pas recevoir de réponses.
Sur GitHub, le modèle vous guidera pour rédiger la bonne question afin que vous puissiez plus facilement obtenir une bonne réponse, ou même résoudre le problème vousmême avant de demander. Et sur GitHub, je peux m'assurer de toujours tout répondre, même si cela prend du temps. Je ne peux pas personnellement faire cela avec les systèmes de chat. 😅
Les conversations dans les systèmes de chat ne sont pas non plus aussi facilement recherchables que sur GitHub, donc les questions et réponses peuvent se perdre dans la conversation. Et seules celles sur GitHub comptent pour devenir un [Expert FastAPI](fastapi-people.md#fastapi-experts){.internal-link target=_blank}, vous aurez donc très probablement plus d'attention sur GitHub.
D'un autre côté, il y a des milliers d'utilisateurs dans les systèmes de chat, il y a donc de fortes chances que vous trouviez presque toujours quelqu'un avec qui parler. 😄
## Sponsoriser l'auteur { #sponsor-the-author }
Si votre **produit/entreprise** dépend de **FastAPI** ou y est lié et que vous souhaitez atteindre ses utilisateurs, vous pouvez sponsoriser l'auteur (moi) via <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>. Selon le niveau, vous pourriez obtenir des avantages supplémentaires, comme un badge dans la documentation. 🎁
---

View File

@ -1,4 +1,4 @@
# Histoire, conception et avenir
# Histoire, conception et avenir { #history-design-and-future }
Il y a quelque temps, <a href="https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">un utilisateur de **FastAPI** a demandé</a> :
@ -6,7 +6,7 @@ Il y a quelque temps, <a href="https://github.com/fastapi/fastapi/issues/3#issue
Voici un petit bout de cette histoire.
## Alternatives
## Alternatives { #alternatives }
Je crée des API avec des exigences complexes depuis plusieurs années (Machine Learning, systèmes distribués, jobs asynchrones, bases de données NoSQL, etc), en dirigeant plusieurs équipes de développeurs.
@ -14,7 +14,7 @@ Dans ce cadre, j'ai dû étudier, tester et utiliser de nombreuses alternatives.
L'histoire de **FastAPI** est en grande partie l'histoire de ses prédécesseurs.
Comme dit dans la section [Alternatives](alternatives.md){.internal-link target=\_blank} :
Comme dit dans la section [Alternatives](alternatives.md){.internal-link target=_blank} :
<blockquote markdown="1">
@ -28,7 +28,7 @@ Mais à un moment donné, il n'y avait pas d'autre option que de créer quelque
</blockquote>
## Recherche
## Recherche { #investigation }
En utilisant toutes les alternatives précédentes, j'ai eu la chance d'apprendre de toutes, de prendre des idées, et de les combiner de la meilleure façon que j'ai pu trouver pour moi-même et les équipes de développeurs avec lesquelles j'ai travaillé.
@ -38,9 +38,9 @@ De plus, la meilleure approche était d'utiliser des normes déjà existantes.
Ainsi, avant même de commencer à coder **FastAPI**, j'ai passé plusieurs mois à étudier les spécifications d'OpenAPI, JSON Schema, OAuth2, etc. Comprendre leurs relations, leurs similarités et leurs différences.
## Conception
## Conception { #design }
Ensuite, j'ai passé du temps à concevoir l'"API" de développeur que je voulais avoir en tant qu'utilisateur (en tant que développeur utilisant FastAPI).
Ensuite, j'ai passé du temps à concevoir l'« API » de développeur que je voulais avoir en tant qu'utilisateur (en tant que développeur utilisant FastAPI).
J'ai testé plusieurs idées dans les éditeurs Python les plus populaires : PyCharm, VS Code, les éditeurs basés sur Jedi.
@ -48,11 +48,11 @@ D'après la dernière <a href="https://www.jetbrains.com/research/python-develop
Cela signifie que **FastAPI** a été spécifiquement testé avec les éditeurs utilisés par 80% des développeurs Python. Et comme la plupart des autres éditeurs ont tendance à fonctionner de façon similaire, tous ses avantages devraient fonctionner pour pratiquement tous les éditeurs.
Ainsi, j'ai pu trouver les meilleurs moyens de réduire autant que possible la duplication du code, d'avoir la complétion partout, les contrôles de type et d'erreur, etc.
Ainsi, j'ai pu trouver les meilleurs moyens de réduire autant que possible la duplication du code, d'avoir l'autocomplétion partout, les contrôles de type et d'erreur, etc.
Le tout de manière à offrir la meilleure expérience de développement à tous les développeurs.
## Exigences
## Exigences { #requirements }
Après avoir testé plusieurs alternatives, j'ai décidé que j'allais utiliser <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">**Pydantic**</a> pour ses avantages.
@ -60,11 +60,11 @@ J'y ai ensuite contribué, pour le rendre entièrement compatible avec JSON Sche
Pendant le développement, j'ai également contribué à <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>, l'autre exigence clé.
## Développement
## Développement { #development }
Au moment où j'ai commencé à créer **FastAPI** lui-même, la plupart des pièces étaient déjà en place, la conception était définie, les exigences et les outils étaient prêts, et la connaissance des normes et des spécifications était claire et fraîche.
## Futur
## Futur { #future }
À ce stade, il est déjà clair que **FastAPI** et ses idées sont utiles pour de nombreuses personnes.
@ -76,4 +76,4 @@ Mais il y a encore de nombreuses améliorations et fonctionnalités à venir.
**FastAPI** a un grand avenir devant lui.
Et [votre aide](help-fastapi.md){.internal-link target=\_blank} est grandement appréciée.
Et [votre aide](help-fastapi.md){.internal-link target=_blank} est grandement appréciée.

View File

@ -0,0 +1,17 @@
# Utiliser les anciens codes d'erreur d'authentification 403 { #use-old-403-authentication-error-status-codes }
Avant FastAPI version `0.122.0`, lorsque les utilitaires de sécurité intégrés renvoyaient une erreur au client après un échec d'authentification, ils utilisaient le code d'état HTTP `403 Forbidden`.
À partir de FastAPI version `0.122.0`, ils utilisent le code d'état HTTP plus approprié `401 Unauthorized`, et renvoient un en-tête `WWW-Authenticate` pertinent dans la réponse, conformément aux spécifications HTTP, <a href="https://datatracker.ietf.org/doc/html/rfc7235#section-3.1" class="external-link" target="_blank">RFC 7235</a>, <a href="https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized" class="external-link" target="_blank">RFC 9110</a>.
Mais si, pour une raison quelconque, vos clients dépendent de l'ancien comportement, vous pouvez y revenir en surchargeant la méthode `make_not_authenticated_error` dans vos classes de sécurité.
Par exemple, vous pouvez créer une sous-classe de `HTTPBearer` qui renvoie une erreur `403 Forbidden` au lieu de l'erreur par défaut `401 Unauthorized` :
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Astuce
Remarquez que la fonction renvoie l'instance de l'exception, elle ne la lève pas. La levée est effectuée dans le reste du code interne.
///

View File

@ -0,0 +1,56 @@
# Configurer OpenAPI de manière conditionnelle { #conditional-openapi }
Si nécessaire, vous pouvez utiliser des paramètres et des variables d'environnement pour configurer OpenAPI de manière conditionnelle selon l'environnement, et même le désactiver complètement.
## À propos de la sécurité, des API et de la documentation { #about-security-apis-and-docs }
Masquer vos interfaces utilisateur de la documentation en production ne devrait pas être la manière de protéger votre API.
Cela n'ajoute aucune sécurité supplémentaire à votre API, les *chemins d'accès* resteront disponibles là où ils se trouvent.
S'il y a une faille de sécurité dans votre code, elle existera toujours.
Masquer la documentation rend simplement plus difficile la compréhension de la manière d'interagir avec votre API et pourrait aussi rendre son débogage en production plus difficile. Cela pourrait être considéré simplement comme une forme de <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">Sécurité par l'obscurité</a>.
Si vous voulez sécuriser votre API, il y a plusieurs meilleures approches possibles, par exemple :
* Vous devez vous assurer d'avoir des modèles Pydantic bien définis pour le corps de la requête et la réponse.
* Configurez toutes les autorisations et tous les rôles nécessaires à l'aide de dépendances.
* Ne stockez jamais de mots de passe en clair, seulement des hachages de mots de passe.
* Implémentez et utilisez des outils cryptographiques reconnus, comme pwdlib et des jetons JWT, ... etc.
* Ajoutez des contrôles d'autorisation plus granulaires avec des scopes OAuth2 lorsque nécessaire.
* ... etc.
Néanmoins, vous pourriez avoir un cas d'utilisation très spécifique où vous devez vraiment désactiver la documentation de l'API pour un certain environnement (par exemple pour la production) ou selon des configurations provenant de variables d'environnement.
## Configurer OpenAPI de manière conditionnelle avec des paramètres et des variables d'environnement { #conditional-openapi-from-settings-and-env-vars }
Vous pouvez facilement utiliser les mêmes paramètres Pydantic pour configurer votre OpenAPI généré et les interfaces utilisateur de la documentation.
Par exemple :
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Ici nous déclarons le paramètre `openapi_url` avec la même valeur par défaut `"/openapi.json"`.
Nous l'utilisons ensuite lors de la création de l'application `FastAPI`.
Vous pouvez alors désactiver OpenAPI (y compris les interfaces utilisateur de la documentation) en définissant la variable d'environnement `OPENAPI_URL` sur la chaîne vide, comme ceci :
<div class="termy">
```console
$ OPENAPI_URL= uvicorn main:app
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Ensuite, si vous allez aux URL `/openapi.json`, `/docs` ou `/redoc`, vous obtiendrez simplement une erreur `404 Not Found` comme :
```JSON
{
"detail": "Not Found"
}
```

View File

@ -0,0 +1,70 @@
# Configurer Swagger UI { #configure-swagger-ui }
Vous pouvez configurer des <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">paramètres supplémentaires de Swagger UI</a>.
Pour les configurer, passez l'argument `swagger_ui_parameters` lors de la création de l'objet d'application `FastAPI()` ou à la fonction `get_swagger_ui_html()`.
`swagger_ui_parameters` reçoit un dictionnaire avec les configurations passées directement à Swagger UI.
FastAPI convertit les configurations en **JSON** pour les rendre compatibles avec JavaScript, car c'est ce dont Swagger UI a besoin.
## Désactiver la coloration syntaxique { #disable-syntax-highlighting }
Par exemple, vous pourriez désactiver la coloration syntaxique dans Swagger UI.
Sans modifier les paramètres, la coloration syntaxique est activée par défaut :
<img src="/img/tutorial/extending-openapi/image02.png">
Mais vous pouvez la désactiver en définissant `syntaxHighlight` à `False` :
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
... et ensuite Swagger UI n'affichera plus la coloration syntaxique :
<img src="/img/tutorial/extending-openapi/image03.png">
## Modifier le thème { #change-the-theme }
De la même manière, vous pouvez définir le thème de la coloration syntaxique avec la clé « syntaxHighlight.theme » (remarquez le point au milieu) :
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Cette configuration modifierait le thème de couleurs de la coloration syntaxique :
<img src="/img/tutorial/extending-openapi/image04.png">
## Modifier les paramètres Swagger UI par défaut { #change-default-swagger-ui-parameters }
FastAPI inclut des paramètres de configuration par défaut adaptés à la plupart des cas d'utilisation.
Il inclut ces configurations par défaut :
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
Vous pouvez remplacer n'importe lequel d'entre eux en définissant une valeur différente dans l'argument `swagger_ui_parameters`.
Par exemple, pour désactiver `deepLinking`, vous pourriez passer ces paramètres à `swagger_ui_parameters` :
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Autres paramètres de Swagger UI { #other-swagger-ui-parameters }
Pour voir toutes les autres configurations possibles que vous pouvez utiliser, lisez la <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">documentation officielle des paramètres de Swagger UI</a>.
## Paramètres JavaScript uniquement { #javascript-only-settings }
Swagger UI permet également d'autres configurations qui sont des objets réservés à JavaScript (par exemple, des fonctions JavaScript).
FastAPI inclut aussi ces paramètres `presets` réservés à JavaScript :
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Ce sont des objets **JavaScript**, pas des chaînes, vous ne pouvez donc pas les passer directement depuis du code Python.
Si vous devez utiliser des configurations réservées à JavaScript comme celles-ci, vous pouvez utiliser l'une des méthodes ci-dessus. Surchargez entièrement le *chemin d'accès* Swagger UI et écrivez manuellement tout JavaScript nécessaire.

View File

@ -0,0 +1,185 @@
# Héberger en propre les ressources statiques de lUI des docs personnalisées { #custom-docs-ui-static-assets-self-hosting }
Les documents de lAPI utilisent **Swagger UI** et **ReDoc**, et chacune nécessite des fichiers JavaScript et CSS.
Par défaut, ces fichiers sont servis depuis un <abbr title="Content Delivery Network - Réseau de diffusion de contenu: Un service, normalement composé de plusieurs serveurs, qui fournit des fichiers statiques, comme JavaScript et CSS. Il est couramment utilisé pour servir ces fichiers depuis le serveur le plus proche du client, améliorant la performance.">CDN</abbr>.
Mais il est possible de le personnaliser : vous pouvez définir un CDN spécifique, ou servir vousmême les fichiers.
## Configurer un CDN personnalisé pour JavaScript et CSS { #custom-cdn-for-javascript-and-css }
Supposons que vous souhaitiez utiliser un autre <abbr title="Content Delivery Network - Réseau de diffusion de contenu">CDN</abbr>, par exemple vous voulez utiliser `https://unpkg.com/`.
Cela peut être utile si, par exemple, vous vivez dans un pays qui restreint certaines URL.
### Désactiver les docs automatiques { #disable-the-automatic-docs }
La première étape consiste à désactiver les docs automatiques, car par défaut elles utilisent le CDN par défaut.
Pour les désactiver, définissez leurs URL sur `None` lors de la création de votre application `FastAPI` :
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Inclure les docs personnalisées { #include-the-custom-docs }
Vous pouvez maintenant créer les chemins d'accès pour les docs personnalisées.
Vous pouvez réutiliser les fonctions internes de FastAPI pour créer les pages HTML de la documentation et leur passer les arguments nécessaires :
- `openapi_url` : lURL où la page HTML des docs peut récupérer le schéma OpenAPI de votre API. Vous pouvez utiliser ici lattribut `app.openapi_url`.
- `title` : le titre de votre API.
- `oauth2_redirect_url` : vous pouvez utiliser `app.swagger_ui_oauth2_redirect_url` ici pour utiliser la valeur par défaut.
- `swagger_js_url` : lURL où la page HTML de Swagger UI peut récupérer le fichier **JavaScript**. Cest lURL du CDN personnalisé.
- `swagger_css_url` : lURL où la page HTML de Swagger UI peut récupérer le fichier **CSS**. Cest lURL du CDN personnalisé.
Et de même pour ReDoc ...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Astuce
Le chemin d'accès pour `swagger_ui_redirect` est un assistant lorsque vous utilisez OAuth2.
Si vous intégrez votre API à un fournisseur OAuth2, vous pourrez vous authentifier et revenir aux docs de lAPI avec les identifiants acquis. Et interagir avec elle en utilisant la véritable authentification OAuth2.
Swagger UI sen chargera en arrièreplan pour vous, mais il a besoin de cet assistant « redirect ».
///
### Créer un chemin d'accès pour tester { #create-a-path-operation-to-test-it }
Maintenant, pour pouvoir vérifier que tout fonctionne, créez un chemin d'accès :
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Tester { #test-it }
Vous devriez maintenant pouvoir aller à vos docs sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, puis recharger la page : elle chargera ces ressources depuis le nouveau CDN.
## Héberger en propre JavaScript et CSS pour les docs { #self-hosting-javascript-and-css-for-docs }
Héberger vousmême le JavaScript et le CSS peut être utile si, par exemple, votre application doit continuer de fonctionner même hors ligne, sans accès Internet ouvert, ou sur un réseau local.
Vous verrez ici comment servir ces fichiers vousmême, dans la même application FastAPI, et configurer les docs pour les utiliser.
### Structure des fichiers du projet { #project-file-structure }
Supposons que la structure de vos fichiers de projet ressemble à ceci :
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Créez maintenant un répertoire pour stocker ces fichiers statiques.
Votre nouvelle structure de fichiers pourrait ressembler à ceci :
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static/
```
### Télécharger les fichiers { #download-the-files }
Téléchargez les fichiers statiques nécessaires aux docs et placezles dans ce répertoire `static/`.
Vous pouvez probablement cliquer avec le bouton droit sur chaque lien et choisir une option similaire à « Enregistrer le lien sous ... ».
**Swagger UI** utilise les fichiers :
- <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a>
- <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a>
Et **ReDoc** utilise le fichier :
- <a href="https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a>
Après cela, votre structure de fichiers pourrait ressembler à :
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Servir les fichiers statiques { #serve-the-static-files }
- Importer `StaticFiles`.
- « Monter » une instance `StaticFiles()` sur un chemin spécifique.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Tester les fichiers statiques { #test-the-static-files }
Démarrez votre application et rendezvous sur <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>.
Vous devriez voir un très long fichier JavaScript pour **ReDoc**.
Il pourrait commencer par quelque chose comme :
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Cela confirme que vous parvenez à servir des fichiers statiques depuis votre application et que vous avez placé les fichiers statiques des docs au bon endroit.
Nous pouvons maintenant configurer lapplication pour utiliser ces fichiers statiques pour les docs.
### Désactiver les docs automatiques pour les fichiers statiques { #disable-the-automatic-docs-for-static-files }
Comme lors de lutilisation dun CDN personnalisé, la première étape consiste à désactiver les docs automatiques, car elles utilisent un CDN par défaut.
Pour les désactiver, définissez leurs URL sur `None` lors de la création de votre application `FastAPI` :
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Inclure les docs personnalisées pour les fichiers statiques { #include-the-custom-docs-for-static-files }
Et comme avec un CDN personnalisé, vous pouvez maintenant créer les chemins d'accès pour les docs personnalisées.
Là encore, vous pouvez réutiliser les fonctions internes de FastAPI pour créer les pages HTML de la documentation et leur passer les arguments nécessaires :
- `openapi_url` : lURL où la page HTML des docs peut récupérer le schéma OpenAPI de votre API. Vous pouvez utiliser ici lattribut `app.openapi_url`.
- `title` : le titre de votre API.
- `oauth2_redirect_url` : vous pouvez utiliser `app.swagger_ui_oauth2_redirect_url` ici pour utiliser la valeur par défaut.
- `swagger_js_url` : lURL où la page HTML de Swagger UI peut récupérer le fichier **JavaScript**. **Cest celui que votre propre application sert désormais**.
- `swagger_css_url` : lURL où la page HTML de Swagger UI peut récupérer le fichier **CSS**. **Cest celui que votre propre application sert désormais**.
Et de même pour ReDoc ...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Astuce
Le chemin d'accès pour `swagger_ui_redirect` est un assistant lorsque vous utilisez OAuth2.
Si vous intégrez votre API à un fournisseur OAuth2, vous pourrez vous authentifier et revenir aux docs de lAPI avec les identifiants acquis. Et interagir avec elle en utilisant la véritable authentification OAuth2.
Swagger UI sen chargera en arrièreplan pour vous, mais il a besoin de cet assistant « redirect ».
///
### Créer un chemin d'accès pour tester les fichiers statiques { #create-a-path-operation-to-test-static-files }
Maintenant, pour pouvoir vérifier que tout fonctionne, créez un chemin d'accès :
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Tester lUI avec des fichiers statiques { #test-static-files-ui }
Vous devriez maintenant pouvoir couper votre WiFi, aller à vos docs sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> et recharger la page.
Et même sans Internet, vous pourrez voir la documentation de votre API et interagir avec elle.

View File

@ -0,0 +1,109 @@
# Personnaliser les classes Request et APIRoute { #custom-request-and-apiroute-class }
Dans certains cas, vous pouvez vouloir surcharger la logique utilisée par les classes `Request` et `APIRoute`.
En particulier, cela peut être une bonne alternative à une logique dans un middleware.
Par exemple, si vous voulez lire ou manipuler le corps de la requête avant qu'il ne soit traité par votre application.
/// danger | Danger
Ceci est une fonctionnalité « avancée ».
Si vous débutez avec **FastAPI**, vous pouvez ignorer cette section.
///
## Cas d'utilisation { #use-cases }
Voici quelques cas d'utilisation :
* Convertir des corps de requête non JSON en JSON (par exemple <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>).
* Décompresser des corps de requête compressés en gzip.
* Journaliser automatiquement tous les corps de requête.
## Gérer les encodages personnalisés du corps de la requête { #handling-custom-request-body-encodings }
Voyons comment utiliser une sous-classe personnalisée de `Request` pour décompresser des requêtes gzip.
Et une sous-classe d'`APIRoute` pour utiliser cette classe de requête personnalisée.
### Créer une classe `GzipRequest` personnalisée { #create-a-custom-gziprequest-class }
/// tip | Astuce
Il s'agit d'un exemple simplifié pour montrer le fonctionnement ; si vous avez besoin de la prise en charge de Gzip, vous pouvez utiliser le [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank} fourni.
///
Commencez par créer une classe `GzipRequest`, qui va surcharger la méthode `Request.body()` pour décompresser le corps en présence d'un en-tête approprié.
S'il n'y a pas `gzip` dans l'en-tête, elle n'essaiera pas de décompresser le corps.
De cette manière, la même classe de route peut gérer des requêtes gzip compressées ou non compressées.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Créer une classe `GzipRoute` personnalisée { #create-a-custom-gziproute-class }
Ensuite, nous créons une sous-classe personnalisée de `fastapi.routing.APIRoute` qui utilisera `GzipRequest`.
Cette fois, elle va surcharger la méthode `APIRoute.get_route_handler()`.
Cette méthode renvoie une fonction. Et c'est cette fonction qui recevra une requête et retournera une réponse.
Ici, nous l'utilisons pour créer une `GzipRequest` à partir de la requête originale.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Détails techniques
Un `Request` possède un attribut `request.scope`, qui n'est qu'un `dict` Python contenant les métadonnées liées à la requête.
Un `Request` a également un `request.receive`, qui est une fonction pour « recevoir » le corps de la requête.
Le `dict` `scope` et la fonction `receive` font tous deux partie de la spécification ASGI.
Et ces deux éléments, `scope` et `receive`, sont ce dont on a besoin pour créer une nouvelle instance de `Request`.
Pour en savoir plus sur `Request`, consultez <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">la documentation de Starlette sur les requêtes</a>.
///
La seule chose que fait différemment la fonction renvoyée par `GzipRequest.get_route_handler`, c'est de convertir la `Request` en `GzipRequest`.
Ce faisant, notre `GzipRequest` se chargera de décompresser les données (si nécessaire) avant de les transmettre à nos *chemins d'accès*.
Après cela, toute la logique de traitement est identique.
Mais grâce à nos modifications dans `GzipRequest.body`, le corps de la requête sera automatiquement décompressé lorsque **FastAPI** le chargera, si nécessaire.
## Accéder au corps de la requête dans un gestionnaire d'exceptions { #accessing-the-request-body-in-an-exception-handler }
/// tip | Astuce
Pour résoudre ce même problème, il est probablement beaucoup plus simple d'utiliser `body` dans un gestionnaire personnalisé pour `RequestValidationError` ([Gérer les erreurs](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
Mais cet exemple reste valable et montre comment interagir avec les composants internes.
///
Nous pouvons également utiliser cette même approche pour accéder au corps de la requête dans un gestionnaire d'exceptions.
Il suffit de traiter la requête dans un bloc `try`/`except` :
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Si une exception se produit, l'instance de `Request` sera toujours dans la portée, ce qui nous permet de lire et d'utiliser le corps de la requête lors du traitement de l'erreur :
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Utiliser une classe `APIRoute` personnalisée dans un routeur { #custom-apiroute-class-in-a-router }
Vous pouvez également définir le paramètre `route_class` d'un `APIRouter` :
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
Dans cet exemple, les *chemins d'accès* sous le `router` utiliseront la classe personnalisée `TimedRoute`, et auront un en-tête supplémentaire `X-Response-Time` dans la réponse avec le temps nécessaire pour générer la réponse :
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}

View File

@ -0,0 +1,80 @@
# Étendre OpenAPI { #extending-openapi }
Il existe des cas où vous pouvez avoir besoin de modifier le schéma OpenAPI généré.
Dans cette section, vous verrez comment faire.
## Le processus normal { #the-normal-process }
Le processus normal (par défaut) est le suivant.
Une application (instance) `FastAPI` a une méthode `.openapi()` censée retourner le schéma OpenAPI.
Lors de la création de l'objet application, un *chemin d'accès* pour `/openapi.json` (ou pour l'URL que vous avez définie dans votre `openapi_url`) est enregistré.
Il renvoie simplement une réponse JSON avec le résultat de la méthode `.openapi()` de l'application.
Par défaut, la méthode `.openapi()` vérifie la propriété `.openapi_schema` pour voir si elle contient des données et les renvoie.
Sinon, elle les génère à l'aide de la fonction utilitaire `fastapi.openapi.utils.get_openapi`.
Et cette fonction `get_openapi()` reçoit comme paramètres :
* `title` : Le titre OpenAPI, affiché dans les documents.
* `version` : La version de votre API, p. ex. `2.5.0`.
* `openapi_version` : La version de la spécification OpenAPI utilisée. Par défaut, la plus récente : `3.1.0`.
* `summary` : Un court résumé de l'API.
* `description` : La description de votre API ; elle peut inclure du markdown et sera affichée dans la documentation.
* `routes` : Une liste de routes ; chacune correspond à un *chemin d'accès* enregistré. Elles sont extraites de `app.routes`.
/// info
Le paramètre `summary` est disponible à partir d'OpenAPI 3.1.0, pris en charge par FastAPI 0.99.0 et versions ultérieures.
///
## Remplacer les valeurs par défaut { #overriding-the-defaults }
En vous appuyant sur les informations ci-dessus, vous pouvez utiliser la même fonction utilitaire pour générer le schéma OpenAPI et remplacer chaque partie dont vous avez besoin.
Par exemple, ajoutons <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">lextension OpenAPI de ReDoc pour inclure un logo personnalisé</a>.
### **FastAPI** normal { #normal-fastapi }
Tout dabord, écrivez votre application **FastAPI** comme dhabitude :
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Générer le schéma OpenAPI { #generate-the-openapi-schema }
Ensuite, utilisez la même fonction utilitaire pour générer le schéma OpenAPI, dans une fonction `custom_openapi()` :
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Modifier le schéma OpenAPI { #modify-the-openapi-schema }
Vous pouvez maintenant ajouter lextension ReDoc, en ajoutant un `x-logo` personnalisé à l« objet » `info` dans le schéma OpenAPI :
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Mettre en cache le schéma OpenAPI { #cache-the-openapi-schema }
Vous pouvez utiliser la propriété `.openapi_schema` comme « cache » pour stocker votre schéma généré.
Ainsi, votre application naura pas à générer le schéma à chaque fois quun utilisateur ouvre les documents de votre API.
Il ne sera généré quune seule fois, puis le même schéma en cache sera utilisé pour les requêtes suivantes.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Remplacer la méthode { #override-the-method }
Vous pouvez maintenant remplacer la méthode `.openapi()` par votre nouvelle fonction.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Vérifier { #check-it }
Une fois que vous allez sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>, vous verrez que vous utilisez votre logo personnalisé (dans cet exemple, le logo de **FastAPI**) :
<img src="/img/tutorial/extending-openapi/image01.png">

View File

@ -0,0 +1,39 @@
# Général - Guides pratiques - Recettes { #general-how-to-recipes }
Voici plusieurs renvois vers d'autres endroits de la documentation, pour des questions générales ou fréquentes.
## Filtrer des données - Sécurité { #filter-data-security }
Pour vous assurer que vous ne renvoyez pas plus de données que nécessaire, lisez la documentation [Tutoriel - Modèle de réponse - Type de retour](../tutorial/response-model.md){.internal-link target=_blank}.
## Étiquettes de documentation - OpenAPI { #documentation-tags-openapi }
Pour ajouter des étiquettes à vos *chemins d'accès* et les regrouper dans l'interface utilisateur de la documentation, lisez la documentation [Tutoriel - Configurations de chemin d'accès - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
## Résumé et description de la documentation - OpenAPI { #documentation-summary-and-description-openapi }
Pour ajouter un résumé et une description à vos *chemins d'accès* et les afficher dans l'interface utilisateur de la documentation, lisez la documentation [Tutoriel - Configurations de chemin d'accès - Résumé et description](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
## Description de la réponse dans la documentation - OpenAPI { #documentation-response-description-openapi }
Pour définir la description de la réponse, affichée dans l'interface utilisateur de la documentation, lisez la documentation [Tutoriel - Configurations de chemin d'accès - Description de la réponse](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
## Déprécier un *chemin d'accès* dans la documentation - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Pour déprécier un *chemin d'accès* et l'indiquer dans l'interface utilisateur de la documentation, lisez la documentation [Tutoriel - Configurations de chemin d'accès - Déprécier un chemin d'accès](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
## Convertir n'importe quelles données au format compatible JSON { #convert-any-data-to-json-compatible }
Pour convertir des données vers un format compatible JSON, lisez la documentation [Tutoriel - Encodeur compatible JSON](../tutorial/encoder.md){.internal-link target=_blank}.
## Métadonnées OpenAPI - Documentation { #openapi-metadata-docs }
Pour ajouter des métadonnées à votre schéma OpenAPI, y compris une licence, une version, un contact, etc., lisez la documentation [Tutoriel - Métadonnées et URLs de la documentation](../tutorial/metadata.md){.internal-link target=_blank}.
## URL OpenAPI personnalisée { #openapi-custom-url }
Pour personnaliser l'URL OpenAPI (ou la supprimer), lisez la documentation [Tutoriel - Métadonnées et URLs de la documentation](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
## URL de la documentation OpenAPI { #openapi-docs-urls }
Pour mettre à jour les URL utilisées pour les interfaces utilisateur de documentation générées automatiquement, lisez la documentation [Tutoriel - Métadonnées et URLs de la documentation](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.

View File

@ -0,0 +1,60 @@
# GraphQL { #graphql }
Comme **FastAPI** est basé sur la norme **ASGI**, il est très facile d'intégrer toute bibliothèque **GraphQL** également compatible avec ASGI.
Vous pouvez combiner des *chemins d'accès* FastAPI classiques avec GraphQL dans la même application.
/// tip | Astuce
**GraphQL** résout des cas d'utilisation très spécifiques.
Il présente des **avantages** et des **inconvénients** par rapport aux **API web** classiques.
Assurez-vous d'évaluer si les **bénéfices** pour votre cas d'utilisation compensent les **inconvénients**. 🤓
///
## Bibliothèques GraphQL { #graphql-libraries }
Voici quelques bibliothèques **GraphQL** qui prennent en charge **ASGI**. Vous pouvez les utiliser avec **FastAPI** :
* <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a> 🍓
* Avec <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">la documentation pour FastAPI</a>
* <a href="https://ariadnegraphql.org/" class="external-link" target="_blank">Ariadne</a>
* Avec <a href="https://ariadnegraphql.org/docs/fastapi-integration" class="external-link" target="_blank">la documentation pour FastAPI</a>
* <a href="https://tartiflette.io/" class="external-link" target="_blank">Tartiflette</a>
* Avec <a href="https://tartiflette.github.io/tartiflette-asgi/" class="external-link" target="_blank">Tartiflette ASGI</a> pour fournir l'intégration ASGI
* <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>
* Avec <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>
## GraphQL avec Strawberry { #graphql-with-strawberry }
Si vous avez besoin ou souhaitez travailler avec **GraphQL**, <a href="https://strawberry.rocks/" class="external-link" target="_blank">**Strawberry**</a> est la bibliothèque **recommandée** car sa conception est la plus proche de celle de **FastAPI**, tout est basé sur des **annotations de type**.
Selon votre cas d'utilisation, vous pourriez préférer une autre bibliothèque, mais si vous me le demandiez, je vous suggérerais probablement d'essayer **Strawberry**.
Voici un petit aperçu de la manière dont vous pouvez intégrer Strawberry avec FastAPI :
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Vous pouvez en apprendre davantage sur Strawberry dans la <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentation de Strawberry</a>.
Et également la documentation sur <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">Strawberry avec FastAPI</a>.
## Ancien `GraphQLApp` de Starlette { #older-graphqlapp-from-starlette }
Les versions précédentes de Starlette incluaient une classe `GraphQLApp` pour s'intégrer à <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>.
Elle a été dépréciée dans Starlette, mais si vous avez du code qui l'utilisait, vous pouvez facilement **migrer** vers <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>, qui couvre le même cas d'utilisation et propose une **interface presque identique**.
/// tip | Astuce
Si vous avez besoin de GraphQL, je vous recommande tout de même de regarder <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a>, car il est basé sur des annotations de type plutôt que sur des classes et types personnalisés.
///
## En savoir plus { #learn-more }
Vous pouvez en apprendre davantage sur **GraphQL** dans la <a href="https://graphql.org/" class="external-link" target="_blank">documentation officielle de GraphQL</a>.
Vous pouvez également en lire davantage sur chacune des bibliothèques décrites ci-dessus via leurs liens.

View File

@ -0,0 +1,13 @@
# Comment faire - Recettes { #how-to-recipes }
Vous trouverez ici différentes recettes ou des guides « comment faire » pour **plusieurs sujets**.
La plupart de ces idées sont plus ou moins **indépendantes**, et dans la plupart des cas vous n'avez besoin de les étudier que si elles s'appliquent directement à **votre projet**.
Si quelque chose vous paraît intéressant et utile pour votre projet, allez-y et consultez-le ; sinon, vous pouvez probablement simplement les ignorer.
/// tip | Astuce
Si vous voulez **apprendre FastAPI** de façon structurée (recommandé), allez lire le [Tutoriel - Guide utilisateur](../tutorial/index.md){.internal-link target=_blank} chapitre par chapitre à la place.
///

View File

@ -0,0 +1,135 @@
# Migrer de Pydantic v1 à Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
Si vous avez une ancienne application FastAPI, vous utilisez peut-être Pydantic version 1.
FastAPI version 0.100.0 prenait en charge soit Pydantic v1 soit v2. Il utilisait celle que vous aviez installée.
FastAPI version 0.119.0 a introduit une prise en charge partielle de Pydantic v1 depuis l'intérieur de Pydantic v2 (comme `pydantic.v1`), pour faciliter la migration vers v2.
FastAPI 0.126.0 a supprimé la prise en charge de Pydantic v1, tout en continuant à prendre en charge `pydantic.v1` pendant un certain temps.
/// warning | Alertes
L'équipe Pydantic a arrêté la prise en charge de Pydantic v1 pour les dernières versions de Python, à partir de Python 3.14.
Cela inclut `pydantic.v1`, qui n'est plus pris en charge à partir de Python 3.14.
Si vous souhaitez utiliser les dernières fonctionnalités de Python, vous devez vous assurer que vous utilisez Pydantic v2.
///
Si vous avez une ancienne application FastAPI avec Pydantic v1, je vais vous montrer comment la migrer vers Pydantic v2, et les fonctionnalités de FastAPI 0.119.0 pour vous aider à une migration progressive.
## Guide officiel { #official-guide }
Pydantic propose un <a href="https://docs.pydantic.dev/latest/migration/" class="external-link" target="_blank">Guide de migration</a> officiel de la v1 à la v2.
Il inclut aussi ce qui a changé, comment les validations sont désormais plus correctes et strictes, les pièges possibles, etc.
Vous pouvez le lire pour mieux comprendre ce qui a changé.
## Tests { #tests }
Vous devez vous assurer d'avoir des [tests](../tutorial/testing.md){.internal-link target=_blank} pour votre application et de les exécuter en intégration continue (CI).
De cette façon, vous pouvez effectuer la mise à niveau et vous assurer que tout fonctionne toujours comme prévu.
## `bump-pydantic` { #bump-pydantic }
Dans de nombreux cas, lorsque vous utilisez des modèles Pydantic classiques sans personnalisations, vous pourrez automatiser la majeure partie du processus de migration de Pydantic v1 à Pydantic v2.
Vous pouvez utiliser <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a> de la même équipe Pydantic.
Cet outil vous aidera à modifier automatiquement la majeure partie du code à adapter.
Après cela, vous pouvez exécuter les tests et vérifier que tout fonctionne. Si c'est le cas, vous avez terminé. 😎
## Pydantic v1 dans v2 { #pydantic-v1-in-v2 }
Pydantic v2 inclut tout Pydantic v1 sous la forme du sous-module `pydantic.v1`. Mais cela n'est plus pris en charge dans les versions au-delà de Python 3.13.
Cela signifie que vous pouvez installer la dernière version de Pydantic v2 et importer/utiliser les anciens composants de Pydantic v1 depuis ce sous-module, comme si vous aviez l'ancien Pydantic v1 installé.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### Prise en charge de FastAPI pour Pydantic v1 dans v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Depuis FastAPI 0.119.0, il existe également une prise en charge partielle de Pydantic v1 depuis l'intérieur de Pydantic v2, pour faciliter la migration vers v2.
Vous pouvez donc mettre à niveau Pydantic vers la dernière version 2 et modifier les imports pour utiliser le sous-module `pydantic.v1`, et dans de nombreux cas cela fonctionnera tel quel.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Alertes
Gardez à l'esprit que, puisque l'équipe Pydantic ne prend plus en charge Pydantic v1 dans les versions récentes de Python à partir de Python 3.14, l'utilisation de `pydantic.v1` n'est pas non plus prise en charge à partir de Python 3.14.
///
### Pydantic v1 et v2 dans la même application { #pydantic-v1-and-v2-on-the-same-app }
Pydantic ne prend pas en charge le fait d'avoir un modèle Pydantic v2 contenant des champs eux-mêmes définis comme des modèles Pydantic v1, et inversement.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
... mais vous pouvez avoir des modèles séparés utilisant Pydantic v1 et v2 dans la même application.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
Dans certains cas, il est même possible d'avoir des modèles Pydantic v1 et v2 dans le même chemin d'accès de votre application FastAPI :
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
Dans l'exemple ci-dessus, le modèle d'entrée est un modèle Pydantic v1 et le modèle de sortie (défini dans `response_model=ItemV2`) est un modèle Pydantic v2.
### Paramètres Pydantic v1 { #pydantic-v1-parameters }
Si vous devez utiliser certains des outils spécifiques à FastAPI pour les paramètres comme `Body`, `Query`, `Form`, etc., avec des modèles Pydantic v1, vous pouvez les importer depuis `fastapi.temp_pydantic_v1_params` le temps de terminer la migration vers Pydantic v2 :
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Migrer par étapes { #migrate-in-steps }
/// tip | Astuce
Essayez d'abord avec `bump-pydantic` ; si vos tests passent et que cela fonctionne, vous avez tout terminé en une seule commande. ✨
///
Si `bump-pydantic` ne fonctionne pas pour votre cas d'usage, vous pouvez utiliser la prise en charge des modèles Pydantic v1 et v2 dans la même application pour effectuer la migration vers Pydantic v2 progressivement.
Vous pouvez d'abord mettre à niveau Pydantic pour utiliser la dernière version 2 et modifier les imports pour utiliser `pydantic.v1` pour tous vos modèles.
Ensuite, vous pouvez commencer à migrer vos modèles de Pydantic v1 vers v2 par groupes, par étapes progressives. 🚶

View File

@ -0,0 +1,102 @@
# Séparer les schémas OpenAPI pour l'entrée et la sortie ou non { #separate-openapi-schemas-for-input-and-output-or-not }
Depuis la sortie de **Pydantic v2**, l'OpenAPI généré est un peu plus précis et **correct** qu'avant. 😎
En fait, dans certains cas, il y aura même **deux schémas JSON** dans OpenAPI pour le même modèle Pydantic, pour l'entrée et pour la sortie, selon s'ils ont des **valeurs par défaut**.
Voyons comment cela fonctionne et comment le modifier si vous devez le faire.
## Utiliser des modèles Pydantic pour l'entrée et la sortie { #pydantic-models-for-input-and-output }
Supposons que vous ayez un modèle Pydantic avec des valeurs par défaut, comme celuici :
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Modèle pour l'entrée { #model-for-input }
Si vous utilisez ce modèle en entrée comme ici :
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
... alors, le champ `description` ne sera **pas requis**. Parce qu'il a une valeur par défaut de `None`.
### Modèle d'entrée dans les documents { #input-model-in-docs }
Vous pouvez le confirmer dans les documents, le champ `description` n'a pas d'**astérisque rouge**, il n'est pas indiqué comme requis :
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image01.png">
</div>
### Modèle pour la sortie { #model-for-output }
Mais si vous utilisez le même modèle en sortie, comme ici :
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
... alors, comme `description` a une valeur par défaut, si vous ne retournez rien pour ce champ, il aura tout de même cette **valeur par défaut**.
### Modèle pour les données de réponse en sortie { #model-for-output-response-data }
Si vous interagissez avec les documents et vérifiez la réponse, même si le code n'a rien ajouté dans l'un des champs `description`, la réponse JSON contient la valeur par défaut (`null`) :
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image02.png">
</div>
Cela signifie qu'il aura **toujours une valeur**, simplement, parfois la valeur pourra être `None` (ou `null` en JSON).
Cela signifie que les clients utilisant votre API n'ont pas à vérifier si la valeur existe ou non, ils peuvent **supposer que le champ sera toujours présent**, mais que, dans certains cas, il aura la valeur par défaut `None`.
La manière de décrire cela dans OpenAPI est de marquer ce champ comme **requis**, car il sera toujours présent.
Pour cette raison, le schéma JSON d'un modèle peut être différent selon qu'il est utilisé pour **l'entrée ou la sortie** :
- pour **l'entrée**, `description` ne sera **pas requis**
- pour **la sortie**, il sera **requis** (et éventuellement `None`, ou en termes JSON, `null`)
### Modèle de sortie dans les documents { #model-for-output-in-docs }
Vous pouvez également vérifier le modèle de sortie dans les documents, **à la fois** `name` et `description` sont marqués comme **requis** avec un **astérisque rouge** :
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image03.png">
</div>
### Modèle pour l'entrée et la sortie dans les documents { #model-for-input-and-output-in-docs }
Et si vous consultez tous les schémas disponibles (schémas JSON) dans OpenAPI, vous verrez qu'il y en a deux, un `Item-Input` et un `Item-Output`.
Pour `Item-Input`, `description` n'est **pas requis**, il n'a pas d'astérisque rouge.
Mais pour `Item-Output`, `description` est **requis**, il a un astérisque rouge.
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image04.png">
</div>
Avec cette fonctionnalité de **Pydantic v2**, la documentation de votre API est plus **précise**, et si vous avez des clients et SDKs générés automatiquement, ils seront eux aussi plus précis, avec une meilleure **expérience développeur** et davantage de cohérence. 🎉
## Ne pas séparer les schémas { #do-not-separate-schemas }
Il existe des cas où vous pourriez vouloir avoir le **même schéma pour l'entrée et la sortie**.
Le cas d'usage principal est probablement que vous avez déjà du code client/SDKs générés automatiquement et que vous ne souhaitez pas encore mettre à jour tout ce code client/ces SDKs générés automatiquement ; vous le ferez sans doute à un moment donné, mais peutêtre pas tout de suite.
Dans ce cas, vous pouvez désactiver cette fonctionnalité dans **FastAPI**, avec le paramètre `separate_input_output_schemas=False`.
/// info | info
La prise en charge de `separate_input_output_schemas` a été ajoutée dans FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Utiliser le même schéma pour les modèles d'entrée et de sortie dans les documents { #same-schema-for-input-and-output-models-in-docs }
Désormais, il n'y aura qu'un seul schéma pour l'entrée et la sortie du modèle, uniquement `Item`, et `description` ne sera pas requis :
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
</div>

View File

@ -0,0 +1,7 @@
# Tester une base de données { #testing-a-database }
Vous pouvez étudier les bases de données, SQL et SQLModel dans <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">les documents SQLModel</a>. 🤓
Il existe un mini <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">tutoriel sur l'utilisation de SQLModel avec FastAPI</a>. ✨
Ce tutoriel comprend une section sur <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">les tests des bases de données SQL</a>. 😎

View File

@ -40,7 +40,7 @@ Les principales fonctionnalités sont :
* **Rapide** : très hautes performances, au niveau de **NodeJS** et **Go** (grâce à Starlette et Pydantic). [L'un des frameworks Python les plus rapides](#performance).
* **Rapide à coder** : augmente la vitesse de développement des fonctionnalités d'environ 200 % à 300 %. *
* **Moins de bugs** : réduit d'environ 40 % les erreurs induites par le développeur. *
* **Intuitif** : excellente compatibilité avec les éditeurs. <abbr title="également appelé autocomplétion, IntelliSense">Autocomplétion</abbr> partout. Moins de temps passé à déboguer.
* **Intuitif** : excellente compatibilité avec les éditeurs. <dfn title="également connu sous le nom de : auto-complétion, autocomplétion, IntelliSense">Autocomplétion</dfn> partout. Moins de temps passé à déboguer.
* **Facile** : conçu pour être facile à utiliser et à apprendre. Moins de temps passé à lire les documents.
* **Concis** : diminue la duplication de code. Plusieurs fonctionnalités à partir de chaque déclaration de paramètre. Moins de bugs.
* **Robuste** : obtenez un code prêt pour la production. Avec une documentation interactive automatique.
@ -368,7 +368,7 @@ item: Item
* La validation des données :
* des erreurs automatiques et claires lorsque les données ne sont pas valides.
* une validation même pour les objets JSON profondément imbriqués.
* <abbr title="aussi connu sous le nom de : serialization, parsing, marshalling">Conversion</abbr> des données d'entrée : venant du réseau vers les données et types Python. Lecture depuis :
* <dfn title="également connu sous le nom de : sérialisation, parsing, marshalling">Conversion</dfn> des données d'entrée : venant du réseau vers les données et types Python. Lecture depuis :
* JSON.
* Paramètres de chemin.
* Paramètres de requête.
@ -376,7 +376,7 @@ item: Item
* En-têtes.
* Formulaires.
* Fichiers.
* <abbr title="aussi connu sous le nom de : serialization, parsing, marshalling">Conversion</abbr> des données de sortie : conversion des données et types Python en données réseau (au format JSON) :
* <dfn title="également connu sous le nom de : sérialisation, parsing, marshalling">Conversion</dfn> des données de sortie : conversion des données et types Python en données réseau (au format JSON) :
* Conversion des types Python (`str`, `int`, `float`, `bool`, `list`, etc).
* Objets `datetime`.
* Objets `UUID`.
@ -439,7 +439,7 @@ Pour un exemple plus complet comprenant plus de fonctionnalités, voir le <a hre
* Déclaration de **paramètres** provenant d'autres emplacements comme : **en-têtes**, **cookies**, **champs de formulaire** et **fichiers**.
* Comment définir des **contraintes de validation** comme `maximum_length` ou `regex`.
* Un système **<abbr title="aussi connu sous le nom de composants, ressources, fournisseurs, services, injectables">d'injection de dépendances</abbr>** très puissant et facile à utiliser.
* Un système **<dfn title="également connu sous le nom de : composants, ressources, fournisseurs, services, injectables">d'injection de dépendances</dfn>** très puissant et facile à utiliser.
* Sécurité et authentification, y compris la prise en charge de **OAuth2** avec des **JWT tokens** et l'authentification **HTTP Basic**.
* Des techniques plus avancées (mais tout aussi faciles) pour déclarer des **modèles JSON profondément imbriqués** (grâce à Pydantic).
* Intégration **GraphQL** avec <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> et d'autres bibliothèques.
@ -524,7 +524,7 @@ Utilisées par Starlette :
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Obligatoire si vous souhaitez utiliser le `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Obligatoire si vous souhaitez utiliser la configuration de template par défaut.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Obligatoire si vous souhaitez prendre en charge l<abbr title="convertir la chaîne issue d'une requête HTTP en données Python">« parsing »</abbr> de formulaires avec `request.form()`.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Obligatoire si vous souhaitez prendre en charge l<dfn title="convertir la chaîne issue d'une requête HTTP en données Python">« parsing »</dfn> de formulaires avec `request.form()`.
Utilisées par FastAPI :

View File

@ -2,4 +2,4 @@
Voici les sections introductives et les tutoriels pour apprendre **FastAPI**.
Vous pouvez considérer ceci comme un **livre**, un **cours**, la **méthode officielle** et recommandée pour apprendre FastAPI. 😎
Vous pouvez considérer ceci comme un **livre**, un **cours**, la méthode **officielle** et recommandée pour apprendre FastAPI. 😎

View File

@ -1,8 +1,8 @@
# Introduction aux types Python { #python-types-intro }
Python prend en charge des « type hints » (aussi appelées « annotations de type ») facultatives.
Python prend en charge des « annotations de type » (aussi appelées « type hints ») facultatives.
Ces « type hints » ou annotations sont une syntaxe spéciale qui permet de déclarer le <abbr title="par exemple : str, int, float, bool">type</abbr> d'une variable.
Ces **« annotations de type »** sont une syntaxe spéciale qui permet de déclarer le <dfn title="par exemple : str, int, float, bool">type</dfn> d'une variable.
En déclarant les types de vos variables, les éditeurs et outils peuvent vous offrir un meilleur support.
@ -22,7 +22,7 @@ Si vous êtes un expert Python, et que vous savez déjà tout sur les annotation
Commençons par un exemple simple :
{* ../../docs_src/python_types/tutorial001_py39.py *}
{* ../../docs_src/python_types/tutorial001_py310.py *}
Exécuter ce programme affiche :
@ -34,9 +34,9 @@ La fonction fait ce qui suit :
* Prend un `first_name` et un `last_name`.
* Convertit la première lettre de chacun en majuscule avec `title()`.
* <abbr title="Les met ensemble, en un seul. Avec le contenu de l'un après l'autre.">Concatène</abbr> ces deux valeurs avec un espace au milieu.
* <dfn title="Les met ensemble, en un seul. Avec le contenu de l'un après l'autre.">Concatène</dfn> ces deux valeurs avec un espace au milieu.
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Modifier le code { #edit-it }
@ -78,7 +78,7 @@ C'est tout.
Ce sont les « annotations de type » :
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Ce n'est pas la même chose que de déclarer des valeurs par défaut, ce qui serait :
@ -106,7 +106,7 @@ Avec cela, vous pouvez faire défiler en voyant les options, jusqu'à trouver ce
Regardez cette fonction, elle a déjà des annotations de type :
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Comme l'éditeur connaît les types des variables, vous n'obtenez pas seulement l'autocomplétion, vous obtenez aussi des vérifications d'erreurs :
@ -114,7 +114,7 @@ Comme l'éditeur connaît les types des variables, vous n'obtenez pas seulement
Vous savez maintenant qu'il faut corriger, convertir `age` en chaîne avec `str(age)` :
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Déclarer des types { #declaring-types }
@ -133,29 +133,32 @@ Vous pouvez utiliser, par exemple :
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### Types génériques avec paramètres de type { #generic-types-with-type-parameters }
### Module `typing` { #typing-module }
Il existe certaines structures de données qui peuvent contenir d'autres valeurs, comme `dict`, `list`, `set` et `tuple`. Et les valeurs internes peuvent aussi avoir leur propre type.
Pour certains cas d'utilisation supplémentaires, vous pourriez avoir besoin d'importer certains éléments depuis le module standard `typing`, par exemple lorsque vous voulez déclarer que quelque chose a « n'importe quel type », vous pouvez utiliser `Any` depuis `typing` :
Ces types qui ont des types internes sont appelés types « génériques ». Et il est possible de les déclarer, même avec leurs types internes.
```python
from typing import Any
Pour déclarer ces types et les types internes, vous pouvez utiliser le module standard Python `typing`. Il existe spécifiquement pour prendre en charge ces annotations de type.
#### Versions plus récentes de Python { #newer-versions-of-python }
def some_function(data: Any):
print(data)
```
La syntaxe utilisant `typing` est compatible avec toutes les versions, de Python 3.6 aux plus récentes, y compris Python 3.9, Python 3.10, etc.
### Types génériques { #generic-types }
Au fur et à mesure que Python évolue, les versions plus récentes apportent un meilleur support pour ces annotations de type et dans de nombreux cas vous n'aurez même pas besoin d'importer et d'utiliser le module `typing` pour les déclarer.
Certains types peuvent prendre des « paramètres de type » entre crochets, pour définir leurs types internes, par exemple une « liste de chaînes » se déclarerait `list[str]`.
Si vous pouvez choisir une version plus récente de Python pour votre projet, vous pourrez profiter de cette simplicité supplémentaire.
Ces types qui peuvent prendre des paramètres de type sont appelés des **types génériques** ou **Generics**.
Dans toute la documentation, il y a des exemples compatibles avec chaque version de Python (lorsqu'il y a une différence).
Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :
Par exemple « Python 3.6+ » signifie que c'est compatible avec Python 3.6 ou supérieur (y compris 3.7, 3.8, 3.9, 3.10, etc.). Et « Python 3.9+ » signifie que c'est compatible avec Python 3.9 ou supérieur (y compris 3.10, etc).
Si vous pouvez utiliser les dernières versions de Python, utilisez les exemples pour la dernière version, ils auront la meilleure et la plus simple syntaxe, par exemple, « Python 3.10+ ».
* `list`
* `tuple`
* `set`
* `dict`
#### Liste { #list }
@ -167,9 +170,9 @@ Comme type, mettez `list`.
Comme la liste est un type qui contient des types internes, mettez-les entre crochets :
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info
/// info | Info
Ces types internes entre crochets sont appelés « paramètres de type ».
@ -193,7 +196,7 @@ Et pourtant, l'éditeur sait que c'est un `str` et fournit le support approprié
Vous feriez la même chose pour déclarer des `tuple` et des `set` :
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Cela signifie :
@ -208,7 +211,7 @@ Le premier paramètre de type est pour les clés du `dict`.
Le second paramètre de type est pour les valeurs du `dict` :
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Cela signifie :
@ -218,46 +221,22 @@ Cela signifie :
#### Union { #union }
Vous pouvez déclarer qu'une variable peut être de plusieurs types, par exemple, un `int` ou un `str`.
Vous pouvez déclarer qu'une variable peut être **plusieurs types**, par exemple, un `int` ou un `str`.
Dans Python 3.6 et supérieur (y compris Python 3.10), vous pouvez utiliser le type `Union` de `typing` et mettre entre crochets les types possibles à accepter.
Pour le définir, vous utilisez la <dfn title='aussi appelé « opérateur OU bit à bit », mais ce sens nest pas pertinent ici'>barre verticale (`|`)</dfn> pour séparer les deux types.
Dans Python 3.10, il existe aussi une nouvelle syntaxe où vous pouvez mettre les types possibles séparés par une <abbr title='aussi appelé « opérateur OU bit à bit », mais ce sens nest pas pertinent ici'>barre verticale (`|`)</abbr>.
//// tab | Python 3.10+
C'est ce qu'on appelle une « union », car la variable peut être n'importe quoi dans l'union de ces deux ensembles de types.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
Dans les deux cas, cela signifie que `item` peut être un `int` ou un `str`.
Cela signifie que `item` peut être un `int` ou un `str`.
#### Possiblement `None` { #possibly-none }
Vous pouvez déclarer qu'une valeur peut avoir un type, comme `str`, mais qu'elle peut aussi être `None`.
Dans Python 3.6 et supérieur (y compris Python 3.10), vous pouvez le déclarer en important et en utilisant `Optional` depuis le module `typing`.
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009_py39.py!}
```
Utiliser `Optional[str]` au lieu de simplement `str` permettra à l'éditeur de vous aider à détecter des erreurs où vous supposeriez qu'une valeur est toujours un `str`, alors qu'elle pourrait en fait aussi être `None`.
`Optional[Something]` est en réalité un raccourci pour `Union[Something, None]`, ils sont équivalents.
Cela signifie aussi que dans Python 3.10, vous pouvez utiliser `Something | None` :
//// tab | Python 3.10+
```Python hl_lines="1"
@ -266,96 +245,7 @@ Cela signifie aussi que dans Python 3.10, vous pouvez utiliser `Something | None
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
//// tab | Python 3.9+ alternative
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
#### Utiliser `Union` ou `Optional` { #using-union-or-optional }
Si vous utilisez une version de Python inférieure à 3.10, voici un conseil de mon point de vue très **subjectif** :
* 🚨 Évitez d'utiliser `Optional[SomeType]`
* À la place ✨ **utilisez `Union[SomeType, None]`** ✨.
Les deux sont équivalents et sous le capot ce sont les mêmes, mais je recommanderais `Union` plutôt que `Optional` parce que le mot « facultatif » semble impliquer que la valeur est optionnelle, alors que cela signifie en fait « elle peut être `None` », même si elle n'est pas facultative et est toujours requise.
Je pense que `Union[SomeType, None]` est plus explicite sur ce que cela signifie.
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.
Par exemple, prenons cette fonction :
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
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 « optionnel ») parce qu'il n'a pas de valeur par défaut. Néanmoins, `name` accepte `None` comme valeur :
```Python
say_hi(name=None) # Cela fonctionne, None est valide 🎉
```
La bonne nouvelle est que, dès que vous êtes sur Python 3.10, vous n'avez plus à vous en soucier, car vous pourrez simplement utiliser `|` pour définir des unions de types :
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
Et alors vous n'aurez plus à vous soucier de noms comme `Optional` et `Union`. 😎
#### Types génériques { #generic-types }
Ces types qui prennent des paramètres de type entre crochets sont appelés des **types génériques** ou **Generics**, par exemple :
//// tab | Python 3.10+
Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :
* `list`
* `tuple`
* `set`
* `dict`
Et, comme avec les versions précédentes de Python, depuis le module `typing` :
* `Union`
* `Optional`
* ... et d'autres.
Dans Python 3.10, comme alternative à l'utilisation des génériques `Union` et `Optional`, vous pouvez utiliser la <abbr title='aussi appelé « opérateur OU bit à bit », mais ce sens nest pas pertinent ici'>barre verticale (`|`)</abbr> pour déclarer des unions de types, c'est bien mieux et plus simple.
////
//// tab | Python 3.9+
Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :
* `list`
* `tuple`
* `set`
* `dict`
Et des génériques depuis le module `typing` :
* `Union`
* `Optional`
* ... et d'autres.
////
Utiliser `str | None` au lieu de simplement `str` permettra à l'éditeur de vous aider à détecter des erreurs où vous supposeriez qu'une valeur est toujours un `str`, alors qu'elle pourrait en fait aussi être `None`.
### Classes en tant que types { #classes-as-types }
@ -363,19 +253,19 @@ Vous pouvez aussi déclarer une classe comme type d'une variable.
Disons que vous avez une classe `Person`, avec un nom :
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Vous pouvez ensuite déclarer une variable de type `Person` :
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
Et là encore, vous obtenez tout le support de l'éditeur :
<img src="/img/python-types/image06.png">
Remarquez que cela signifie « `one_person` est une instance de la classe `Person` ».
Remarquez que cela signifie « `one_person` est une **instance** de la classe `Person` ».
Cela ne signifie pas « `one_person` est la classe appelée `Person` ».
Cela ne signifie pas « `one_person` est la **classe** appelée `Person` ».
## Modèles Pydantic { #pydantic-models }
@ -393,7 +283,7 @@ Un exemple tiré de la documentation officielle de Pydantic :
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info
/// info | Info
Pour en savoir plus à propos de <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, consultez sa documentation</a>.
@ -403,33 +293,27 @@ Pour en savoir plus à propos de <a href="https://docs.pydantic.dev/" class="ext
Vous verrez beaucoup plus de tout cela en pratique dans le [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
/// tip | Astuce
Pydantic a un comportement spécial lorsque vous utilisez `Optional` ou `Union[Something, None]` sans valeur par défaut, vous pouvez en lire davantage dans la documentation de Pydantic à propos des <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">champs Optionals requis</a>.
///
## Annotations de type avec métadonnées { #type-hints-with-metadata-annotations }
Python dispose également d'une fonctionnalité qui permet de mettre des **<abbr title="Données sur les données, dans ce cas, des informations sur le type, p. ex. une description.">métadonnées</abbr> supplémentaires** dans ces annotations de type en utilisant `Annotated`.
Python dispose également d'une fonctionnalité qui permet de mettre des **<dfn title="Données sur les données, dans ce cas, des informations sur le type, p. ex. une description.">métadonnées</dfn> supplémentaires** dans ces annotations de type en utilisant `Annotated`.
Depuis Python 3.9, `Annotated` fait partie de la bibliothèque standard, vous pouvez donc l'importer depuis `typing`.
Vous pouvez importer `Annotated` depuis `typing`.
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python lui-même ne fait rien avec ce `Annotated`. Et pour les éditeurs et autres outils, le type est toujours `str`.
Mais vous pouvez utiliser cet espace dans `Annotated` pour fournir à **FastAPI** des métadonnées supplémentaires sur la façon dont vous voulez que votre application se comporte.
L'important à retenir est que le premier paramètre de type que vous passez à `Annotated` est le type réel. Le reste n'est que des métadonnées pour d'autres outils.
L'important à retenir est que **le premier « paramètre de type »** que vous passez à `Annotated` est le **type réel**. Le reste n'est que des métadonnées pour d'autres outils.
Pour l'instant, vous avez juste besoin de savoir que `Annotated` existe, et que c'est du Python standard. 😎
Plus tard, vous verrez à quel point cela peut être puissant.
Plus tard, vous verrez à quel point cela peut être **puissant**.
/// tip | Astuce
Le fait que ce soit du Python standard signifie que vous bénéficierez toujours de la meilleure expérience développeur possible dans votre éditeur, avec les outils que vous utilisez pour analyser et refactoriser votre code, etc. ✨
Le fait que ce soit du **Python standard** signifie que vous bénéficierez toujours de la **meilleure expérience développeur possible** dans votre éditeur, avec les outils que vous utilisez pour analyser et refactoriser votre code, etc. ✨
Et aussi que votre code sera très compatible avec de nombreux autres outils et bibliothèques Python. 🚀
@ -457,7 +341,7 @@ Tout cela peut sembler abstrait. Ne vous inquiétez pas. Vous verrez tout cela e
L'important est qu'en utilisant les types standards de Python, en un seul endroit (au lieu d'ajouter plus de classes, de décorateurs, etc.), **FastAPI** fera une grande partie du travail pour vous.
/// info
/// info | Info
Si vous avez déjà parcouru tout le tutoriel et êtes revenu pour en voir plus sur les types, une bonne ressource est <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">l'« aide-mémoire » de `mypy`</a>.

View File

@ -0,0 +1,3 @@
# Ressources { #resources }
Ressources supplémentaires, liens externes et plus encore. ✈️

View File

@ -0,0 +1,11 @@
/// details | 🌐 Traduction par IA et humains
Cette traduction a été réalisée par une IA guidée par des humains. 🤝
Elle peut contenir des erreurs d'interprétation du sens original, ou paraître peu naturelle, etc. 🤖
Vous pouvez améliorer cette traduction en [nous aidant à mieux guider le LLM d'IA](https://fastapi.tiangolo.com/fr/contributing/#translations).
[Version anglaise](ENGLISH_VERSION_URL)
///

View File

@ -15,12 +15,14 @@ Cela comprend, par exemple :
Pour commencer, importez `BackgroundTasks` et définissez un paramètre dans votre *fonction de chemin d'accès* avec `BackgroundTasks` comme type déclaré.
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** créera l'objet de type `BackgroundTasks` pour vous et le passera comme paramètre.
## Créer une fonction de tâche { #create-a-task-function }
Créez une fonction à exécuter comme tâche d'arrière-plan.
Une fonction à exécuter comme tâche d'arrière-plan est juste une fonction standard qui peut recevoir des paramètres.
Elle peut être une fonction asynchrone (`async def`) ou une fonction normale (`def`), **FastAPI** saura la gérer correctement.
@ -29,13 +31,13 @@ Dans cet exemple, la fonction de tâche écrira dans un fichier (afin de simuler
L'opération d'écriture n'utilisant ni `async` ni `await`, on définit la fonction avec un `def` normal.
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Ajouter une tâche d'arrière-plan { #add-the-background-task }
Dans votre *fonction de chemin d'accès*, passez votre fonction de tâche à l'objet de type `BackgroundTasks` (`background_tasks` ici) grâce à la méthode `.add_task()` :
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` reçoit comme arguments :

View File

@ -0,0 +1,504 @@
# Créer des applications plus grandes - Plusieurs fichiers { #bigger-applications-multiple-files }
Si vous créez une application ou une API web, il est rare que vous puissiez tout mettre dans un seul fichier.
**FastAPI** fournit un outil pratique pour structurer votre application tout en conservant toute la flexibilité.
/// info
Si vous venez de Flask, cela équivaut aux Blueprints de Flask.
///
## Exemple de structure de fichiers { #an-example-file-structure }
Supposons que vous ayez une structure de fichiers comme ceci :
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── dependencies.py
│   └── routers
│   │ ├── __init__.py
│   │ ├── items.py
│   │ └── users.py
│   └── internal
│   ├── __init__.py
│   └── admin.py
```
/// tip | Astuce
Il y a plusieurs fichiers `__init__.py` : un dans chaque répertoire ou sous-répertoire.
C'est cela qui permet d'importer du code d'un fichier dans un autre.
Par exemple, dans `app/main.py` vous pourriez avoir une ligne comme :
```
from app.routers import items
```
///
* Le répertoire `app` contient tout. Et il a un fichier vide `app/__init__.py`, c'est donc un « package Python » (une collection de « modules Python ») : `app`.
* Il contient un fichier `app/main.py`. Comme il se trouve dans un package Python (un répertoire avec un fichier `__init__.py`), c'est un « module » de ce package : `app.main`.
* Il y a aussi un fichier `app/dependencies.py`, tout comme `app/main.py`, c'est un « module » : `app.dependencies`.
* Il y a un sous-répertoire `app/routers/` avec un autre fichier `__init__.py`, c'est donc un « sous-package Python » : `app.routers`.
* Le fichier `app/routers/items.py` est dans un package, `app/routers/`, c'est donc un sous-module : `app.routers.items`.
* De même pour `app/routers/users.py`, c'est un autre sous-module : `app.routers.users`.
* Il y a aussi un sous-répertoire `app/internal/` avec un autre fichier `__init__.py`, c'est donc un autre « sous-package Python » : `app.internal`.
* Et le fichier `app/internal/admin.py` est un autre sous-module : `app.internal.admin`.
<img src="/img/tutorial/bigger-applications/package.drawio.svg">
La même structure de fichiers avec des commentaires :
```bash
.
├── app # "app" est un package Python
│   ├── __init__.py # ce fichier fait de "app" un "package Python"
│   ├── main.py # module "main", ex. import app.main
│   ├── dependencies.py # module "dependencies", ex. import app.dependencies
│   └── routers # "routers" est un "sous-package Python"
│   │ ├── __init__.py # fait de "routers" un "sous-package Python"
│   │ ├── items.py # sous-module "items", ex. import app.routers.items
│   │ └── users.py # sous-module "users", ex. import app.routers.users
│   └── internal # "internal" est un "sous-package Python"
│   ├── __init__.py # fait de "internal" un "sous-package Python"
│   └── admin.py # sous-module "admin", ex. import app.internal.admin
```
## `APIRouter` { #apirouter }
Supposons que le fichier dédié à la gestion des utilisateurs soit le sous-module `/app/routers/users.py`.
Vous voulez séparer les *chemins d'accès* liés à vos utilisateurs du reste du code pour le garder organisé.
Mais cela fait toujours partie de la même application/API web **FastAPI** (cela fait partie du même « package Python »).
Vous pouvez créer les *chemins d'accès* pour ce module à l'aide de `APIRouter`.
### Importer `APIRouter` { #import-apirouter }
Vous l'importez et créez une « instance » de la même manière que vous le feriez avec la classe `FastAPI` :
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### Déclarer des *chemins d'accès* avec `APIRouter` { #path-operations-with-apirouter }
Puis vous l'utilisez pour déclarer vos *chemins d'accès*.
Utilisez-le de la même manière que vous utiliseriez la classe `FastAPI` :
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Vous pouvez considérer `APIRouter` comme une « mini `FastAPI` ».
Toutes les mêmes options sont prises en charge.
Tous les mêmes `parameters`, `responses`, `dependencies`, `tags`, etc.
/// tip | Astuce
Dans cet exemple, la variable s'appelle `router`, mais vous pouvez la nommer comme vous le souhaitez.
///
Nous allons inclure ce `APIRouter` dans l'application principale `FastAPI`, mais d'abord, examinons les dépendances et un autre `APIRouter`.
## Gérer les dépendances { #dependencies }
Nous voyons que nous allons avoir besoin de certaines dépendances utilisées à plusieurs endroits de l'application.
Nous les mettons donc dans leur propre module `dependencies` (`app/dependencies.py`).
Nous allons maintenant utiliser une dépendance simple pour lire un en-tête personnalisé `X-Token` :
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Astuce
Nous utilisons un en-tête inventé pour simplifier cet exemple.
Mais dans les cas réels, vous obtiendrez de meilleurs résultats en utilisant les [utilitaires de sécurité](security/index.md){.internal-link target=_blank} intégrés.
///
## Créer un autre module avec `APIRouter` { #another-module-with-apirouter }
Supposons que vous ayez également les endpoints dédiés à la gestion des « items » de votre application dans le module `app/routers/items.py`.
Vous avez des *chemins d'accès* pour :
* `/items/`
* `/items/{item_id}`
C'est exactement la même structure que pour `app/routers/users.py`.
Mais nous voulons être plus malins et simplifier un peu le code.
Nous savons que tous les *chemins d'accès* de ce module ont les mêmes éléments :
* Préfixe de chemin `prefix` : `/items`.
* `tags` : (un seul tag : `items`).
* `responses` supplémentaires.
* `dependencies` : ils ont tous besoin de la dépendance `X-Token` que nous avons créée.
Donc, au lieu d'ajouter tout cela à chaque *chemin d'accès*, nous pouvons l'ajouter au `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Comme le chemin de chaque *chemin d'accès* doit commencer par `/`, comme dans :
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
... le préfixe ne doit pas inclure un `/` final.
Ainsi, le préfixe dans ce cas est `/items`.
Nous pouvons également ajouter une liste de `tags` et des `responses` supplémentaires qui seront appliqués à tous les *chemins d'accès* inclus dans ce routeur.
Et nous pouvons ajouter une liste de `dependencies` qui seront ajoutées à tous les *chemins d'accès* du routeur et seront exécutées/résolues pour chaque requête qui leur est faite.
/// tip | Astuce
Notez que, tout comme pour les [dépendances dans les décorateurs de *chemin d'accès*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, aucune valeur ne sera transmise à votre *fonction de chemin d'accès*.
///
Le résultat final est que les chemins d'item sont désormais :
* `/items/`
* `/items/{item_id}`
... comme prévu.
* Ils seront marqués avec une liste de tags qui contient une seule chaîne « items ».
* Ces « tags » sont particulièrement utiles pour les systèmes de documentation interactive automatique (utilisant OpenAPI).
* Ils incluront tous les `responses` prédéfinies.
* Tous ces *chemins d'accès* auront la liste des `dependencies` évaluées/exécutées avant eux.
* Si vous déclarez également des dépendances dans un *chemin d'accès* spécifique, **elles seront aussi exécutées**.
* Les dépendances du routeur sont exécutées en premier, puis les [`dependencies` dans le décorateur](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, puis les dépendances des paramètres normaux.
* Vous pouvez également ajouter des [`Security` dependencies avec des `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
/// tip | Astuce
Avoir des `dependencies` dans le `APIRouter` peut servir, par exemple, à exiger une authentification pour tout un groupe de *chemins d'accès*. Même si les dépendances ne sont pas ajoutées individuellement à chacun d'eux.
///
/// check | Vérifications
Les paramètres `prefix`, `tags`, `responses` et `dependencies` sont (comme dans de nombreux autres cas) simplement une fonctionnalité de **FastAPI** pour vous aider à éviter la duplication de code.
///
### Importer les dépendances { #import-the-dependencies }
Ce code se trouve dans le module `app.routers.items`, le fichier `app/routers/items.py`.
Et nous devons récupérer la fonction de dépendance depuis le module `app.dependencies`, le fichier `app/dependencies.py`.
Nous utilisons donc un import relatif avec `..` pour les dépendances :
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Comprendre le fonctionnement des imports relatifs { #how-relative-imports-work }
/// tip | Astuce
Si vous savez parfaitement comment fonctionnent les imports, passez à la section suivante ci-dessous.
///
Un seul point `.`, comme dans :
```Python
from .dependencies import get_token_header
```
signifierait :
* En partant du même package dans lequel vit ce module (le fichier `app/routers/items.py`) (le répertoire `app/routers/`)...
* trouver le module `dependencies` (un fichier imaginaire `app/routers/dependencies.py`)...
* et en importer la fonction `get_token_header`.
Mais ce fichier n'existe pas, nos dépendances sont dans un fichier `app/dependencies.py`.
Rappelez-vous à quoi ressemble la structure de notre app/fichiers :
<img src="/img/tutorial/bigger-applications/package.drawio.svg">
---
Les deux points `..`, comme dans :
```Python
from ..dependencies import get_token_header
```
veulent dire :
* En partant du même package dans lequel vit ce module (le fichier `app/routers/items.py`) (le répertoire `app/routers/`)...
* aller au package parent (le répertoire `app/`)...
* et là, trouver le module `dependencies` (le fichier `app/dependencies.py`)...
* et en importer la fonction `get_token_header`.
Cela fonctionne correctement ! 🎉
---
De la même manière, si nous avions utilisé trois points `...`, comme dans :
```Python
from ...dependencies import get_token_header
```
cela voudrait dire :
* En partant du même package dans lequel vit ce module (le fichier `app/routers/items.py`) (le répertoire `app/routers/`)...
* aller au package parent (le répertoire `app/`)...
* puis aller au parent de ce package (il n'y a pas de package parent, `app` est le niveau supérieur 😱)...
* et là, trouver le module `dependencies` (le fichier `app/dependencies.py`)...
* et en importer la fonction `get_token_header`.
Cela ferait référence à un package au-dessus de `app/`, avec son propre fichier `__init__.py`, etc. Mais nous n'avons pas cela. Donc, cela lèverait une erreur dans notre exemple. 🚨
Mais maintenant vous savez comment cela fonctionne, vous pouvez donc utiliser des imports relatifs dans vos propres applications, aussi complexes soient-elles. 🤓
### Ajouter des `tags`, `responses` et `dependencies` personnalisés { #add-some-custom-tags-responses-and-dependencies }
Nous n'ajoutons pas le préfixe `/items` ni `tags=["items"]` à chaque *chemin d'accès* parce que nous les avons ajoutés au `APIRouter`.
Mais nous pouvons toujours ajouter _davantage_ de `tags` qui seront appliqués à un *chemin d'accès* spécifique, ainsi que des `responses` supplémentaires propres à ce *chemin d'accès* :
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Astuce
Ce dernier *chemin d'accès* aura la combinaison de tags : `["items", "custom"]`.
Et il aura également les deux réponses dans la documentation, une pour `404` et une pour `403`.
///
## Créer l'application `FastAPI` principale { #the-main-fastapi }
Voyons maintenant le module `app/main.py`.
C'est ici que vous importez et utilisez la classe `FastAPI`.
Ce sera le fichier principal de votre application qui reliera tout ensemble.
Et comme la plupart de votre logique vivra désormais dans son propre module, le fichier principal sera assez simple.
### Importer `FastAPI` { #import-fastapi }
Vous importez et créez une classe `FastAPI` comme d'habitude.
Et nous pouvons même déclarer des [dépendances globales](dependencies/global-dependencies.md){.internal-link target=_blank} qui seront combinées avec les dépendances de chaque `APIRouter` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Importer les `APIRouter` { #import-the-apirouter }
Nous importons maintenant les autres sous-modules qui ont des `APIRouter` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Comme les fichiers `app/routers/users.py` et `app/routers/items.py` sont des sous-modules qui font partie du même package Python `app`, nous pouvons utiliser un seul point `.` pour les importer en utilisant des « imports relatifs ».
### Comprendre le fonctionnement de l'import { #how-the-importing-works }
La section :
```Python
from .routers import items, users
```
signifie :
* En partant du même package dans lequel vit ce module (le fichier `app/main.py`) (le répertoire `app/`)...
* chercher le sous-package `routers` (le répertoire `app/routers/`)...
* et en importer le sous-module `items` (le fichier `app/routers/items.py`) et `users` (le fichier `app/routers/users.py`)...
Le module `items` aura une variable `router` (`items.router`). C'est celle que nous avons créée dans le fichier `app/routers/items.py`, c'est un objet `APIRouter`.
Nous faisons ensuite la même chose pour le module `users`.
Nous pourrions aussi les importer ainsi :
```Python
from app.routers import items, users
```
/// info
La première version est un « import relatif » :
```Python
from .routers import items, users
```
La deuxième version est un « import absolu » :
```Python
from app.routers import items, users
```
Pour en savoir plus sur les Packages et Modules Python, lisez <a href="https://docs.python.org/3/tutorial/modules.html" class="external-link" target="_blank">la documentation officielle de Python sur les modules</a>.
///
### Éviter les collisions de noms { #avoid-name-collisions }
Nous importons le sous-module `items` directement, au lieu d'importer uniquement sa variable `router`.
C'est parce que nous avons également une autre variable nommée `router` dans le sous-module `users`.
Si nous les avions importées l'une après l'autre, comme :
```Python
from .routers.items import router
from .routers.users import router
```
le `router` de `users` écraserait celui de `items` et nous ne pourrions pas les utiliser en même temps.
Donc, pour pouvoir utiliser les deux dans le même fichier, nous importons directement les sous-modules :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Inclure les `APIRouter` pour `users` et `items` { #include-the-apirouters-for-users-and-items }
Incluons maintenant les `router` des sous-modules `users` et `items` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info
`users.router` contient le `APIRouter` à l'intérieur du fichier `app/routers/users.py`.
Et `items.router` contient le `APIRouter` à l'intérieur du fichier `app/routers/items.py`.
///
Avec `app.include_router()`, nous pouvons ajouter chaque `APIRouter` à l'application principale `FastAPI`.
Cela inclura toutes les routes de ce routeur comme faisant partie de l'application.
/// note | Détails techniques
En interne, cela créera en fait un *chemin d'accès* pour chaque *chemin d'accès* qui a été déclaré dans le `APIRouter`.
Donc, en coulisses, cela fonctionnera comme si tout faisait partie d'une seule et même application.
///
/// check | Vérifications
Vous n'avez pas à vous soucier de la performance lors de l'inclusion de routeurs.
Cela prendra des microsecondes et ne se produira qu'au démarrage.
Donc cela n'affectera pas la performance. ⚡
///
### Inclure un `APIRouter` avec un `prefix`, des `tags`, des `responses` et des `dependencies` personnalisés { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Imaginons maintenant que votre organisation vous ait fourni le fichier `app/internal/admin.py`.
Il contient un `APIRouter` avec quelques *chemins d'accès* d'administration que votre organisation partage entre plusieurs projets.
Pour cet exemple, il sera très simple. Mais supposons que, parce qu'il est partagé avec d'autres projets de l'organisation, nous ne puissions pas le modifier et ajouter un `prefix`, des `dependencies`, des `tags`, etc. directement au `APIRouter` :
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Mais nous voulons quand même définir un `prefix` personnalisé lors de l'inclusion du `APIRouter` afin que tous ses *chemins d'accès* commencent par `/admin`, nous voulons le sécuriser avec les `dependencies` que nous avons déjà pour ce projet, et nous voulons inclure des `tags` et des `responses`.
Nous pouvons déclarer tout cela sans avoir à modifier le `APIRouter` d'origine en passant ces paramètres à `app.include_router()` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
De cette façon, le `APIRouter` original restera inchangé, afin que nous puissions toujours partager ce même fichier `app/internal/admin.py` avec d'autres projets de l'organisation.
Le résultat est que, dans notre application, chacun des *chemins d'accès* du module `admin` aura :
* Le préfixe `/admin`.
* Le tag `admin`.
* La dépendance `get_token_header`.
* La réponse `418`. 🍵
Mais cela n'affectera que ce `APIRouter` dans notre application, pas dans tout autre code qui l'utilise.
Ainsi, par exemple, d'autres projets pourraient utiliser le même `APIRouter` avec une méthode d'authentification différente.
### Inclure un *chemin d'accès* { #include-a-path-operation }
Nous pouvons également ajouter des *chemins d'accès* directement à l'application `FastAPI`.
Ici, nous le faisons... juste pour montrer que nous le pouvons 🤷 :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
et cela fonctionnera correctement, avec tous les autres *chemins d'accès* ajoutés avec `app.include_router()`.
/// info | Détails très techniques
Note : c'est un détail très technique que vous pouvez probablement **simplement ignorer**.
---
Les `APIRouter` ne sont pas « montés », ils ne sont pas isolés du reste de l'application.
C'est parce que nous voulons inclure leurs *chemins d'accès* dans le schéma OpenAPI et les interfaces utilisateur.
Comme nous ne pouvons pas simplement les isoler et les « monter » indépendamment du reste, les *chemins d'accès* sont « clonés » (recréés), pas inclus directement.
///
## Consulter la documentation API automatique { #check-the-automatic-api-docs }
Maintenant, exécutez votre application :
<div class="termy">
```console
$ fastapi dev app/main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Et ouvrez les documents à <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Vous verrez la documentation API automatique, incluant les chemins de tous les sous-modules, utilisant les bons chemins (et préfixes) et les bons tags :
<img src="/img/tutorial/bigger-applications/image01.png">
## Inclure le même routeur plusieurs fois avec des `prefix` différents { #include-the-same-router-multiple-times-with-different-prefix }
Vous pouvez aussi utiliser `.include_router()` plusieurs fois avec le même routeur en utilisant des préfixes différents.
Cela peut être utile, par exemple, pour exposer la même API sous des préfixes différents, p. ex. `/api/v1` et `/api/latest`.
C'est un usage avancé dont vous n'aurez peut-être pas vraiment besoin, mais il est là au cas où.
## Inclure un `APIRouter` dans un autre { #include-an-apirouter-in-another }
De la même manière que vous pouvez inclure un `APIRouter` dans une application `FastAPI`, vous pouvez inclure un `APIRouter` dans un autre `APIRouter` en utilisant :
```Python
router.include_router(other_router)
```
Vous devez vous assurer de le faire avant d'inclure `router` dans l'application `FastAPI`, afin que les *chemins d'accès* de `other_router` soient également inclus.

View File

@ -0,0 +1,61 @@
# Corps - Champs { #body-fields }
De la même manière que vous pouvez déclarer des validations supplémentaires et des métadonnées dans les paramètres d'une fonction de chemin d'accès avec `Query`, `Path` et `Body`, vous pouvez déclarer des validations et des métadonnées à l'intérieur des modèles Pydantic en utilisant `Field` de Pydantic.
## Importer `Field` { #import-field }
D'abord, vous devez l'importer :
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Alertes
Notez que `Field` est importé directement depuis `pydantic`, et non depuis `fastapi` comme le sont les autres (`Query`, `Path`, `Body`, etc.).
///
## Déclarer les attributs du modèle { #declare-model-attributes }
Vous pouvez ensuite utiliser `Field` avec des attributs de modèle :
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` fonctionne de la même manière que `Query`, `Path` et `Body`, il dispose des mêmes paramètres, etc.
/// note | Détails techniques
En réalité, `Query`, `Path` et d'autres que vous verrez ensuite créent des objets de sous-classes d'une classe commune `Param`, qui est elle-même une sous-classe de la classe `FieldInfo` de Pydantic.
Et `Field` de Pydantic renvoie également une instance de `FieldInfo`.
`Body` renvoie aussi directement des objets d'une sous-classe de `FieldInfo`. Et il y en a d'autres que vous verrez plus tard qui sont des sous-classes de la classe `Body`.
Rappelez-vous que lorsque vous importez `Query`, `Path` et d'autres depuis `fastapi`, ce sont en réalité des fonctions qui renvoient des classes spéciales.
///
/// tip | Astuce
Remarquez comment chaque attribut de modèle avec un type, une valeur par défaut et `Field` a la même structure qu'un paramètre de fonction de chemin d'accès, avec `Field` au lieu de `Path`, `Query` et `Body`.
///
## Ajouter des informations supplémentaires { #add-extra-information }
Vous pouvez déclarer des informations supplémentaires dans `Field`, `Query`, `Body`, etc. Elles seront incluses dans le JSON Schema généré.
Vous en apprendrez davantage sur l'ajout d'informations supplémentaires plus loin dans les documents, lorsque vous apprendrez à déclarer des exemples.
/// warning | Alertes
Les clés supplémentaires passées à `Field` seront également présentes dans le schéma OpenAPI résultant pour votre application.
Comme ces clés ne font pas nécessairement partie de la spécification OpenAPI, certains outils OpenAPI, par exemple [le validateur OpenAPI](https://validator.swagger.io/), peuvent ne pas fonctionner avec votre schéma généré.
///
## Récapitulatif { #recap }
Vous pouvez utiliser `Field` de Pydantic pour déclarer des validations supplémentaires et des métadonnées pour les attributs de modèle.
Vous pouvez également utiliser des arguments nommés supplémentaires pour transmettre des métadonnées JSON Schema additionnelles.

View File

@ -104,12 +104,6 @@ Comme, par défaut, les valeurs singulières sont interprétées comme des param
q: str | None = None
```
Ou en Python 3.9 :
```Python
q: Union[str, None] = None
```
Par exemple :
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}

View File

@ -0,0 +1,220 @@
# Corps - Modèles imbriqués { #body-nested-models }
Avec FastAPI, vous pouvez définir, valider, documenter et utiliser des modèles imbriqués à n'importe quelle profondeur (grâce à Pydantic).
## Déclarer des champs de liste { #list-fields }
Vous pouvez définir un attribut comme étant un sous-type. Par exemple, une `list` Python :
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Cela fera de `tags` une liste, bien que le type des éléments de la liste ne soit pas déclaré.
## Champs de liste avec paramètre de type { #list-fields-with-type-parameter }
Mais Python a une manière spécifique de déclarer des listes avec des types internes, ou « paramètres de type » :
### Déclarer une `list` avec un paramètre de type { #declare-a-list-with-a-type-parameter }
Pour déclarer des types qui ont des paramètres de type (types internes), comme `list`, `dict`, `tuple`,
passez le(s) type(s) interne(s) comme « paramètres de type » à l'aide de crochets : `[` et `]`
```Python
my_list: list[str]
```
C'est simplement la syntaxe Python standard pour les déclarations de type.
Utilisez cette même syntaxe standard pour les attributs de modèles avec des types internes.
Ainsi, dans notre exemple, nous pouvons faire de `tags` spécifiquement une « liste de chaînes » :
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Types set { #set-types }
Mais en y réfléchissant, nous réalisons que les tags ne devraient pas se répéter, ce seraient probablement des chaînes uniques.
Et Python dispose d'un type de données spécial pour les ensembles d'éléments uniques, le `set`.
Nous pouvons alors déclarer `tags` comme un set de chaînes :
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Avec cela, même si vous recevez une requête contenant des doublons, elle sera convertie en un set d'éléments uniques.
Et chaque fois que vous renverrez ces données, même si la source contenait des doublons, elles seront renvoyées sous la forme d'un set d'éléments uniques.
Elles seront également annotées / documentées en conséquence.
## Modèles imbriqués { #nested-models }
Chaque attribut d'un modèle Pydantic a un type.
Mais ce type peut lui-même être un autre modèle Pydantic.
Ainsi, vous pouvez déclarer des « objets » JSON profondément imbriqués avec des noms d'attributs, des types et des validations spécifiques.
Tout cela, de manière arbitrairement imbriquée.
### Définir un sous-modèle { #define-a-submodel }
Par exemple, nous pouvons définir un modèle `Image` :
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Utiliser le sous-modèle comme type { #use-the-submodel-as-a-type }
Nous pouvons ensuite l'utiliser comme type d'un attribut :
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Cela signifie que FastAPI attendrait un corps similaire à :
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Là encore, avec cette simple déclaration, avec FastAPI vous obtenez :
- Prise en charge par l'éditeur (autocomplétion, etc.), même pour les modèles imbriqués
- Conversion des données
- Validation des données
- Documentation automatique
## Types spéciaux et validation { #special-types-and-validation }
Outre les types singuliers normaux comme `str`, `int`, `float`, etc. vous pouvez utiliser des types singuliers plus complexes qui héritent de `str`.
Pour voir toutes les options dont vous disposez, consultez <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">laperçu des types de Pydantic</a>. Vous verrez quelques exemples au chapitre suivant.
Par exemple, comme dans le modèle `Image` nous avons un champ `url`, nous pouvons le déclarer comme instance de `HttpUrl` de Pydantic au lieu de `str` :
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
La chaîne sera vérifiée comme URL valide et documentée comme telle dans JSON Schema / OpenAPI.
## Attributs avec des listes de sous-modèles { #attributes-with-lists-of-submodels }
Vous pouvez également utiliser des modèles Pydantic comme sous-types de `list`, `set`, etc. :
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Cela attendra (convertira, validera, documentera, etc.) un corps JSON comme :
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info
Remarquez que la clé `images` contient maintenant une liste d'objets image.
///
## Modèles profondément imbriqués { #deeply-nested-models }
Vous pouvez définir des modèles imbriqués à une profondeur arbitraire :
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info
Remarquez que `Offer` a une liste d`Item`, qui à leur tour ont une liste optionnelle d`Image`.
///
## Corps de listes pures { #bodies-of-pure-lists }
Si la valeur de premier niveau du corps JSON attendu est un `array` JSON (une `list` Python), vous pouvez déclarer le type dans le paramètre de la fonction, de la même manière que dans les modèles Pydantic :
```Python
images: list[Image]
```
comme :
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Bénéficier de la prise en charge de l'éditeur partout { #editor-support-everywhere }
Et vous bénéficiez de la prise en charge de l'éditeur partout.
Même pour les éléments à l'intérieur des listes :
<img src="/img/tutorial/body-nested-models/image01.png">
Vous ne pourriez pas obtenir ce type de prise en charge de l'éditeur si vous travailliez directement avec des `dict` au lieu de modèles Pydantic.
Mais vous n'avez pas à vous en soucier non plus, les `dict` entrants sont convertis automatiquement et votre sortie est également convertie automatiquement en JSON.
## Corps de `dict` arbitraires { #bodies-of-arbitrary-dicts }
Vous pouvez également déclarer un corps comme un `dict` avec des clés dun certain type et des valeurs dun autre type.
De cette façon, vous n'avez pas besoin de savoir à l'avance quels sont les noms de champs/attributs valides (comme ce serait le cas avec des modèles Pydantic).
Cela serait utile si vous voulez recevoir des clés que vous ne connaissez pas à l'avance.
---
Un autre cas utile est lorsque vous souhaitez avoir des clés d'un autre type (par exemple `int`).
C'est ce que nous allons voir ici.
Dans ce cas, vous accepteriez n'importe quel `dict` tant qu'il a des clés `int` avec des valeurs `float` :
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Astuce
Gardez à l'esprit que JSON ne prend en charge que des `str` comme clés.
Mais Pydantic dispose d'une conversion automatique des données.
Cela signifie que, même si vos clients d'API ne peuvent envoyer que des chaînes comme clés, tant que ces chaînes contiennent des entiers purs, Pydantic les convertira et les validera.
Et le `dict` que vous recevez dans `weights` aura en réalité des clés `int` et des valeurs `float`.
///
## Récapitulatif { #recap }
Avec FastAPI, vous bénéficiez de la flexibilité maximale fournie par les modèles Pydantic, tout en gardant votre code simple, concis et élégant.
Mais avec tous les avantages :
- Prise en charge par l'éditeur (autocomplétion partout !)
- Conversion des données (a.k.a. parsing / sérialisation)
- Validation des données
- Documentation des schémas
- Documentation automatique

View File

@ -0,0 +1,100 @@
# Corps - Mises à jour { #body-updates }
## Mettre à jour en remplaçant avec `PUT` { #update-replacing-with-put }
Pour mettre à jour un élément, vous pouvez utiliser lopération <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a>.
Vous pouvez utiliser le `jsonable_encoder` pour convertir les données dentrée en données pouvant être stockées au format JSON (par exemple, avec une base de données NoSQL). Par exemple, convertir `datetime` en `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
On utilise `PUT` pour recevoir des données qui doivent remplacer les données existantes.
### Avertissement concernant le remplacement { #warning-about-replacing }
Cela signifie que si vous souhaitez mettre à jour lélément `bar` avec `PUT` et un corps contenant :
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
comme il ninclut pas lattribut déjà enregistré « tax »: 20.2, le modèle dentrée prendrait la valeur par défaut « tax »: 10.5.
Et les données seraient enregistrées avec cette « nouvelle » `tax` de `10.5`.
## Effectuer des mises à jour partielles avec `PATCH` { #partial-updates-with-patch }
Vous pouvez également utiliser lopération <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> pour mettre à jour des données de manière partielle.
Cela signifie que vous pouvez nenvoyer que les données que vous souhaitez mettre à jour, en laissant le reste intact.
/// note | Remarque
`PATCH` est moins utilisé et moins connu que `PUT`.
Et de nombreuses équipes nutilisent que `PUT`, même pour les mises à jour partielles.
Vous êtes libre de les utiliser comme vous le souhaitez, **FastAPI** nimpose aucune restriction.
Mais ce guide vous montre, plus ou moins, la façon dont ils sont censés être utilisés.
///
### Utiliser le paramètre `exclude_unset` de Pydantic { #using-pydantics-exclude-unset-parameter }
Si vous souhaitez recevoir des mises à jour partielles, il est très utile dutiliser le paramètre `exclude_unset` dans la méthode `.model_dump()` du modèle Pydantic.
Comme `item.model_dump(exclude_unset=True)`.
Cela génère un `dict` ne contenant que les données définies lors de la création du modèle `item`, en excluant les valeurs par défaut.
Vous pouvez ensuite lutiliser pour produire un `dict` avec uniquement les données définies (envoyées dans la requête), en omettant les valeurs par défaut :
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Utiliser le paramètre `update` de Pydantic { #using-pydantics-update-parameter }
Vous pouvez maintenant créer une copie du modèle existant avec `.model_copy()`, et passer le paramètre `update` avec un `dict` contenant les données à mettre à jour.
Comme `stored_item_model.model_copy(update=update_data)` :
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Récapitulatif des mises à jour partielles { #partial-updates-recap }
En résumé, pour appliquer des mises à jour partielles, vous procédez ainsi :
* (Optionnel) utilisez `PATCH` au lieu de `PUT`.
* Récupérez les données stockées.
* Placez ces données dans un modèle Pydantic.
* Générez un `dict` sans valeurs par défaut à partir du modèle dentrée (en utilisant `exclude_unset`).
* De cette façon, vous mettez à jour uniquement les valeurs effectivement définies par lutilisateur, au lieu décraser des valeurs déjà stockées par des valeurs par défaut de votre modèle.
* Créez une copie du modèle stocké, en mettant à jour ses attributs avec les mises à jour partielles reçues (en utilisant le paramètre `update`).
* Convertissez le modèle copié en quelque chose qui peut être stocké dans votre base de données (par exemple en utilisant le `jsonable_encoder`).
* Cela est comparable à lutilisation à nouveau de la méthode `.model_dump()` du modèle, mais cela vérifie (et convertit) les valeurs vers des types pouvant être convertis en JSON, par exemple `datetime` en `str`.
* Enregistrez les données dans votre base de données.
* Retournez le modèle mis à jour.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Astuce
Vous pouvez en réalité utiliser cette même technique avec une opération HTTP `PUT`.
Mais lexemple ici utilise `PATCH` car il a été créé pour ces cas dusage.
///
/// note | Remarque
Remarquez que le modèle dentrée est toujours validé.
Ainsi, si vous souhaitez recevoir des mises à jour partielles pouvant omettre tous les attributs, vous devez disposer dun modèle avec tous les attributs marqués comme optionnels (avec des valeurs par défaut ou `None`).
Pour distinguer les modèles avec toutes les valeurs optionnelles pour les mises à jour et les modèles avec des valeurs requises pour la création, vous pouvez utiliser les idées décrites dans [Modèles supplémentaires](extra-models.md){.internal-link target=_blank}.
///

View File

@ -10,7 +10,7 @@ Pour déclarer un corps de **requête**, on utilise les modèles de <a href="htt
/// info
Pour envoyer de la donnée, vous devriez utiliser : `POST` (le plus populaire), `PUT`, `DELETE` ou `PATCH`.
Pour envoyer de la donnée, vous devez utiliser : `POST` (le plus populaire), `PUT`, `DELETE` ou `PATCH`.
Envoyer un corps dans une requête `GET` a un comportement non défini dans les spécifications, cela est néanmoins supporté par **FastAPI**, seulement pour des cas d'utilisation très complexes/extrêmes.
@ -56,7 +56,7 @@ Par exemple, le modèle ci-dessus déclare un JSON « `object` » (ou `dict` P
## Le déclarer comme paramètre { #declare-it-as-a-parameter }
Pour l'ajouter à votre *opération de chemin*, déclarez-le comme vous déclareriez des paramètres de chemin ou de requête :
Pour l'ajouter à votre *chemin d'accès*, déclarez-le comme vous déclareriez des paramètres de chemin ou de requête :
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
@ -81,7 +81,7 @@ Les schémas JSON de vos modèles seront intégrés au schéma OpenAPI global de
<img src="/img/tutorial/body/image01.png">
Et seront aussi utilisés dans chaque *opération de chemin* de la documentation utilisant ces modèles :
Et seront aussi utilisés dans chaque *chemin d'accès* de la documentation utilisant ces modèles :
<img src="/img/tutorial/body/image02.png">
@ -115,7 +115,7 @@ Ce qui améliore le support pour les modèles Pydantic avec :
* de l'autocomplétion
* des vérifications de type
* du « refactoring » (ou remaniement de code)
* du « refactoring »
* de la recherche
* des inspections
@ -129,7 +129,7 @@ Dans la fonction, vous pouvez accéder à tous les attributs de l'objet du modè
## Corps de la requête + paramètres de chemin { #request-body-path-parameters }
Vous pouvez déclarer des paramètres de chemin et un corps de requête pour la même *opération de chemin*.
Vous pouvez déclarer des paramètres de chemin et un corps de requête pour la même *chemin d'accès*.
**FastAPI** est capable de reconnaître que les paramètres de la fonction qui correspondent aux paramètres de chemin doivent être **récupérés depuis le chemin**, et que les paramètres de fonctions déclarés comme modèles Pydantic devraient être **récupérés depuis le corps de la requête**.
@ -137,7 +137,7 @@ Vous pouvez déclarer des paramètres de chemin et un corps de requête pour la
## Corps de la requête + paramètres de chemin et de requête { #request-body-path-query-parameters }
Vous pouvez aussi déclarer un **corps**, et des paramètres de **chemin** et de **requête** dans la même *opération de chemin*.
Vous pouvez aussi déclarer un **corps**, et des paramètres de **chemin** et de **requête** dans la même *chemin d'accès*.
**FastAPI** saura reconnaître chacun d'entre eux et récupérer la bonne donnée au bon endroit.
@ -153,7 +153,7 @@ Les paramètres de la fonction seront reconnus comme tel :
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`.
L'annotation de type `str | None` (Python 3.10+) ou `Union` dans `Union[str, None]` (Python 3.9+) n'est pas utilisée par **FastAPI** pour déterminer que la valeur n'est pas requise, il le saura parce qu'elle a une valeur par défaut `= None`.
L'annotation de type `str | None` n'est pas utilisée par **FastAPI** pour déterminer que la valeur n'est pas requise, il le saura parce qu'elle a une valeur par défaut `= None`.
Mais ajouter ces annotations de type permettra à votre éditeur de vous offrir un meilleur support et de détecter des erreurs.

View File

@ -0,0 +1,76 @@
# Modèles de paramètres de cookies { #cookie-parameter-models }
Si vous avez un groupe de **cookies** liés, vous pouvez créer un **modèle Pydantic** pour les déclarer. 🍪
Cela vous permet de **réutiliser le modèle** à **plusieurs endroits** et aussi de déclarer des validations et des métadonnées pour tous les paramètres en une seule fois. 😎
/// note | Remarque
Ceci est pris en charge depuis la version `0.115.0` de FastAPI. 🤓
///
/// tip | Astuce
Cette même technique s'applique à `Query`, `Cookie` et `Header`. 😎
///
## Déclarer des cookies avec un modèle Pydantic { #cookies-with-a-pydantic-model }
Déclarez les paramètres de **cookie** dont vous avez besoin dans un **modèle Pydantic**, puis déclarez le paramètre comme `Cookie` :
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** va **extraire** les données pour **chaque champ** à partir des **cookies** reçus dans la requête et vous fournir le modèle Pydantic que vous avez défini.
## Consulter la documentation { #check-the-docs }
Vous pouvez voir les cookies définis dans l'interface de la documentation à `/docs` :
<div class="screenshot">
<img src="/img/tutorial/cookie-param-models/image01.png">
</div>
/// info
Gardez à l'esprit que, comme les **navigateurs gèrent les cookies** de manière particulière et en arrière-plan, ils **n'autorisent pas** facilement **JavaScript** à y accéder.
Si vous allez dans **l'interface de la documentation de l'API** à `/docs`, vous pourrez voir la **documentation** des cookies pour vos *chemins d'accès*.
Mais même si vous **remplissez les données** et cliquez sur « Execute », comme l'interface de la documentation fonctionne avec **JavaScript**, les cookies ne seront pas envoyés et vous verrez un **message d'erreur** comme si vous n'aviez saisi aucune valeur.
///
## Interdire les cookies supplémentaires { #forbid-extra-cookies }
Dans certains cas d'utilisation particuliers (probablement peu courants), vous pourriez vouloir **restreindre** les cookies que vous souhaitez recevoir.
Votre API a désormais le pouvoir de contrôler son propre <dfn title="C'est une blague, au cas où. Cela n'a rien à voir avec les consentements aux cookies, mais c'est amusant que même l'API puisse maintenant rejeter les pauvres cookies. Prenez un cookie. 🍪">consentement aux cookies</dfn>. 🤪🍪
Vous pouvez utiliser la configuration du modèle de Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Si un client tente d'envoyer des **cookies supplémentaires**, il recevra une **réponse d'erreur**.
Pauvres bannières de cookies, avec tous leurs efforts pour obtenir votre consentement pour que l'<dfn title="C'est encore une blague. Ne faites pas attention à moi. Prenez un café avec votre cookie. ☕">API pour le rejeter</dfn>. 🍪
Par exemple, si le client tente d'envoyer un cookie `santa_tracker` avec la valeur `good-list-please`, il recevra une **réponse d'erreur** lui indiquant que le `santa_tracker` <dfn title="Le Père Noël désapprouve le manque de cookies. 🎅 D'accord, plus de blagues de cookies.">le cookie n'est pas autorisé</dfn> :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Récapitulatif { #summary }
Vous pouvez utiliser des **modèles Pydantic** pour déclarer des <dfn title="Prenez un dernier cookie avant de partir. 🍪">**cookies**</dfn> dans **FastAPI**. 😎

View File

@ -0,0 +1,45 @@
# Paramètres de cookie { #cookie-parameters }
Vous pouvez définir des paramètres de cookie de la même manière que vous définissez les paramètres `Query` et `Path`.
## Importer `Cookie` { #import-cookie }
Commencez par importer `Cookie` :
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Déclarer des paramètres `Cookie` { #declare-cookie-parameters }
Déclarez ensuite les paramètres de cookie en utilisant la même structure qu'avec `Path` et `Query`.
Vous pouvez définir la valeur par défaut ainsi que tous les paramètres supplémentaires de validation ou d'annotation :
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Détails techniques
`Cookie` est une classe « sœur » de `Path` et `Query`. Elle hérite également de la même classe commune `Param`.
Mais rappelez-vous que lorsque vous importez `Query`, `Path`, `Cookie` et d'autres depuis `fastapi`, il s'agit en réalité de fonctions qui renvoient des classes spéciales.
///
/// info
Pour déclarer des cookies, vous devez utiliser `Cookie`, sinon les paramètres seraient interprétés comme des paramètres de requête.
///
/// info
Gardez à l'esprit que, comme **les navigateurs gèrent les cookies** de manière particulière et en coulisses, ils **n'autorisent pas** facilement **JavaScript** à y accéder.
Si vous allez dans l'**interface de la documentation de l'API** à `/docs`, vous pourrez voir la **documentation** des cookies pour vos *chemins d'accès*.
Mais même si vous **renseignez les données** et cliquez sur « Execute », comme l'interface de documentation fonctionne avec **JavaScript**, les cookies ne seront pas envoyés et vous verrez un message **d'erreur** comme si vous n'aviez saisi aucune valeur.
///
## Récapitulatif { #recap }
Déclarez les cookies avec `Cookie`, en utilisant le même schéma commun que `Query` et `Path`.

View File

@ -0,0 +1,88 @@
# CORS (Partage des ressources entre origines) { #cors-cross-origin-resource-sharing }
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">CORS ou « Cross-Origin Resource Sharing »</a> fait référence aux situations où un frontend exécuté dans un navigateur contient du code JavaScript qui communique avec un backend, et où le backend se trouve dans une « origine » différente de celle du frontend.
## Origine { #origin }
Une origine est la combinaison du protocole (`http`, `https`), du domaine (`myapp.com`, `localhost`, `localhost.tiangolo.com`) et du port (`80`, `443`, `8080`).
Ainsi, toutes celles-ci sont des origines différentes :
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Même si elles sont toutes sur `localhost`, elles utilisent des protocoles ou des ports différents, ce sont donc des « origines » différentes.
## Étapes { #steps }
Disons donc que vous avez un frontend exécuté dans votre navigateur à `http://localhost:8080`, et que son JavaScript essaie de communiquer avec un backend exécuté à `http://localhost` (comme nous ne spécifions pas de port, le navigateur supposera le port par défaut `80`).
Le navigateur enverra alors une requête HTTP `OPTIONS` au backend `:80`, et si le backend envoie les en-têtes appropriés autorisant la communication depuis cette origine différente (`http://localhost:8080`), alors le navigateur `:8080` permettra au JavaScript du frontend denvoyer sa requête au backend `:80`.
Pour y parvenir, le backend `:80` doit disposer dune liste « dorigines autorisées ».
Dans ce cas, la liste devrait inclure `http://localhost:8080` pour que le frontend `:8080` fonctionne correctement.
## Caractères génériques { #wildcards }
Il est également possible de déclarer la liste comme « * » (un « wildcard ») pour indiquer que toutes sont autorisées.
Mais cela nautorisera que certains types de communication, en excluant tout ce qui implique des informations didentification : cookies, en-têtes Authorization comme ceux utilisés avec les Bearer Tokens, etc.
Ainsi, pour que tout fonctionne correctement, il est préférable dindiquer explicitement les origines autorisées.
## Utiliser `CORSMiddleware` { #use-corsmiddleware }
Vous pouvez le configurer dans votre application **FastAPI** à laide de `CORSMiddleware`.
* Importer `CORSMiddleware`.
* Créer une liste dorigines autorisées (sous forme de chaînes).
* Lajouter comme « middleware » à votre application **FastAPI**.
Vous pouvez également spécifier si votre backend autorise :
* Les informations didentification (en-têtes Authorization, cookies, etc.).
* Des méthodes HTTP spécifiques (`POST`, `PUT`) ou toutes avec le caractère générique « * ».
* Des en-têtes HTTP spécifiques ou tous avec le caractère générique « * ».
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
Les paramètres utilisés par défaut par limplémentation de `CORSMiddleware` sont restrictifs, vous devez donc activer explicitement des origines, méthodes ou en-têtes particuliers afin que les navigateurs soient autorisés à les utiliser dans un contexte interdomaine.
Les arguments suivants sont pris en charge :
* `allow_origins` - Une liste dorigines autorisées à effectuer des requêtes cross-origin. Par ex. `['https://example.org', 'https://www.example.org']`. Vous pouvez utiliser `['*']` pour autoriser nimporte quelle origine.
* `allow_origin_regex` - Une chaîne regex pour faire correspondre les origines autorisées à effectuer des requêtes cross-origin. Par ex. `'https://.*\.example\.org'`.
* `allow_methods` - Une liste de méthodes HTTP qui doivent être autorisées pour les requêtes cross-origin. Par défaut `['GET']`. Vous pouvez utiliser `['*']` pour autoriser toutes les méthodes standard.
* `allow_headers` - Une liste den-têtes HTTP de requête qui doivent être pris en charge pour les requêtes cross-origin. Par défaut `[]`. Vous pouvez utiliser `['*']` pour autoriser tous les en-têtes. Les en-têtes `Accept`, `Accept-Language`, `Content-Language` et `Content-Type` sont toujours autorisés pour les <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">requêtes CORS simples</a>.
* `allow_credentials` - Indique que les cookies doivent être pris en charge pour les requêtes cross-origin. Par défaut `False`.
Aucun de `allow_origins`, `allow_methods` et `allow_headers` ne peut être défini à `['*']` si `allow_credentials` est défini à `True`. Ils doivent tous être <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards" class="external-link" rel="noopener" target="_blank">spécifiés explicitement</a>.
* `expose_headers` - Indique les en-têtes de réponse qui doivent être accessibles au navigateur. Par défaut `[]`.
* `max_age` - Définit un temps maximum (en secondes) pendant lequel les navigateurs peuvent mettre en cache les réponses CORS. Par défaut `600`.
Le middleware répond à deux types particuliers de requêtes HTTP ...
### Requêtes CORS de prévérification { #cors-preflight-requests }
Il sagit de toute requête `OPTIONS` avec les en-têtes `Origin` et `Access-Control-Request-Method`.
Dans ce cas, le middleware interceptera la requête entrante et répondra avec les en-têtes CORS appropriés, et soit une réponse `200`, soit `400` à titre informatif.
### Requêtes simples { #simple-requests }
Toute requête avec un en-tête `Origin`. Dans ce cas, le middleware laissera passer la requête normalement, mais inclura les en-têtes CORS appropriés dans la réponse.
## En savoir plus { #more-info }
Pour plus dinformations sur <abbr title="Cross-Origin Resource Sharing - Partage des ressources entre origines">CORS</abbr>, consultez la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">documentation CORS de Mozilla</a>.
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** fournit plusieurs middlewares dans `fastapi.middleware` uniquement pour votre confort, en tant que développeur. Mais la plupart des middlewares disponibles proviennent directement de Starlette.
///

View File

@ -6,7 +6,7 @@ Vous pouvez connecter le <abbr title="En anglais: debugger">débogueur</abbr> da
Dans votre application FastAPI, importez et exécutez directement `uvicorn` :
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### À propos de `__name__ == "__main__"` { #about-name-main }
@ -87,7 +87,7 @@ Parce que vous exécutez le serveur Uvicorn directement depuis votre code, vous
Par exemple, dans Visual Studio Code, vous pouvez :
- Allez dans le panneau « Debug ».
- « Add configuration... ».
- « Add configuration ... ».
- Sélectionnez « Python ».
- Lancez le <abbr title="En anglais: debugger">débogueur</abbr> avec l'option « Python: Current File (Integrated Terminal) ».
@ -102,7 +102,7 @@ Voici à quoi cela pourrait ressembler :
Si vous utilisez Pycharm, vous pouvez :
- Ouvrez le menu « Run ».
- Sélectionnez l'option « Debug... ».
- Sélectionnez l'option « Debug ... ».
- Un menu contextuel s'affiche alors.
- Sélectionnez le fichier à déboguer (dans ce cas, `main.py`).

View File

@ -0,0 +1,288 @@
# Utiliser des classes comme dépendances { #classes-as-dependencies }
Avant d'aller plus loin dans le système d'**Injection de dépendances**, mettons à niveau l'exemple précédent.
## Un `dict` de l'exemple précédent { #a-dict-from-the-previous-example }
Dans l'exemple précédent, nous renvoyions un `dict` depuis notre dépendance (« dependable ») :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Mais nous recevons alors un `dict` dans le paramètre `commons` de la fonction de chemin d'accès.
Et les éditeurs ne peuvent pas apporter beaucoup d'assistance (comme l'autocomplétion) pour les `dict`, car ils ne peuvent pas connaître leurs clés ni les types de valeurs.
Nous pouvons faire mieux ...
## Ce qui fait d'un objet une dépendance { #what-makes-a-dependency }
Jusqu'à présent, vous avez vu des dépendances déclarées sous forme de fonctions.
Mais ce n'est pas la seule manière de déclarer des dépendances (même si c'est probablement la plus courante).
L'élément clé est qu'une dépendance doit être un « callable ».
Un « callable » en Python est tout ce que Python peut « appeler » comme une fonction.
Ainsi, si vous avez un objet `something` (qui n'est peutêtre pas une fonction) et que vous pouvez « l'appeler » (l'exécuter) comme :
```Python
something()
```
ou
```Python
something(some_argument, some_keyword_argument="foo")
```
alors c'est un « callable ».
## Utiliser des classes comme dépendances { #classes-as-dependencies_1 }
Vous remarquerez que pour créer une instance d'une classe Python, vous utilisez la même syntaxe.
Par exemple :
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
Dans ce cas, `fluffy` est une instance de la classe `Cat`.
Et pour créer `fluffy`, vous « appelez » `Cat`.
Donc, une classe Python est aussi un « callable ».
Ainsi, avec **FastAPI**, vous pouvez utiliser une classe Python comme dépendance.
Ce que **FastAPI** vérifie réellement, c'est qu'il s'agit d'un « callable » (fonction, classe ou autre) et des paramètres qui y sont définis.
Si vous passez un « callable » comme dépendance dans **FastAPI**, il en analysera les paramètres et les traitera de la même manière que les paramètres d'une fonction de chemin d'accès. Y compris les sousdépendances.
Cela s'applique également aux callables sans aucun paramètre. Comme ce serait le cas pour des fonctions de chemin d'accès sans paramètres.
Nous pouvons alors remplacer la dépendance « dependable » `common_parameters` cidessus par la classe `CommonQueryParams` :
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Faites attention à la méthode `__init__` utilisée pour créer l'instance de la classe :
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
... il a les mêmes paramètres que notre précédent `common_parameters` :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Ce sont ces paramètres que **FastAPI** utilisera pour « résoudre » la dépendance.
Dans les deux cas, il y aura :
- Un paramètre de requête optionnel `q` qui est un `str`.
- Un paramètre de requête `skip` qui est un `int`, avec une valeur par défaut de `0`.
- Un paramètre de requête `limit` qui est un `int`, avec une valeur par défaut de `100`.
Dans les deux cas, les données seront converties, validées, documentées dans le schéma OpenAPI, etc.
## Utiliser { #use-it }
Vous pouvez maintenant déclarer votre dépendance en utilisant cette classe.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** appelle la classe `CommonQueryParams`. Cela crée une « instance » de cette classe et l'instance sera passée comme paramètre `commons` à votre fonction.
## Annotation de type vs `Depends` { #type-annotation-vs-depends }
Remarquez que nous écrivons `CommonQueryParams` deux fois dans le code cidessus :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Le dernier `CommonQueryParams`, dans :
```Python
... Depends(CommonQueryParams)
```
... est ce que **FastAPI** utilisera réellement pour savoir quelle est la dépendance.
C'est à partir de celuici que FastAPI extraira les paramètres déclarés et c'est ce que FastAPI appellera effectivement.
---
Dans ce cas, le premier `CommonQueryParams`, dans :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams ...
```
////
... n'a aucune signification particulière pour **FastAPI**. FastAPI ne l'utilisera pas pour la conversion des données, la validation, etc. (car il utilise `Depends(CommonQueryParams)` pour cela).
Vous pourriez en fait écrire simplement :
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons = Depends(CommonQueryParams)
```
////
... comme dans :
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Mais il est recommandé de déclarer le type ; ainsi, votre éditeur saura ce qui sera passé comme paramètre `commons`, et pourra vous aider avec l'autocomplétion, les vérifications de type, etc. :
<img src="/img/tutorial/dependencies/image02.png">
## Raccourci { #shortcut }
Mais vous voyez qu'il y a ici de la duplication de code : nous écrivons `CommonQueryParams` deux fois :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI** fournit un raccourci pour ces cas, lorsque la dépendance est spécifiquement une classe que **FastAPI** va « appeler » pour créer une instance de la classe ellemême.
Pour ces cas précis, vous pouvez faire ce qui suit :
Au lieu d'écrire :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
... vous écrivez :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Vous déclarez la dépendance comme type du paramètre et vous utilisez `Depends()` sans aucun paramètre, au lieu d'avoir à réécrire la classe entière à l'intérieur de `Depends(CommonQueryParams)`.
Le même exemple ressemblerait alors à ceci :
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
... et **FastAPI** saura quoi faire.
/// tip | Astuce
Si cela vous semble plus déroutant qu'utile, ignorezle, vous n'en avez pas besoin.
Ce n'est qu'un raccourci. Parce que **FastAPI** tient à vous aider à minimiser la duplication de code.
///

View File

@ -0,0 +1,69 @@
# Gérer les dépendances dans les décorateurs de chemins d'accès { #dependencies-in-path-operation-decorators }
Dans certains cas, vous n'avez pas vraiment besoin de la valeur de retour d'une dépendance dans votre *fonction de chemin d'accès*.
Ou la dépendance ne retourne aucune valeur.
Mais vous avez quand même besoin qu'elle soit exécutée/résolue.
Dans ces cas, au lieu de déclarer un paramètre de *fonction de chemin d'accès* avec `Depends`, vous pouvez ajouter une `list` de `dependencies` au *décorateur de chemin d'accès*.
## Ajouter `dependencies` au *décorateur de chemin d'accès* { #add-dependencies-to-the-path-operation-decorator }
Le *décorateur de chemin d'accès* accepte un argument optionnel `dependencies`.
Il doit s'agir d'une `list` de `Depends()` :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Ces dépendances seront exécutées/résolues de la même manière que des dépendances normales. Mais leur valeur (si elles en retournent une) ne sera pas transmise à votre *fonction de chemin d'accès*.
/// tip | Astuce
Certains éditeurs vérifient les paramètres de fonction non utilisés et les signalent comme des erreurs.
En utilisant ces `dependencies` dans le *décorateur de chemin d'accès*, vous pouvez vous assurer qu'elles sont exécutées tout en évitant des erreurs de l'éditeur/des outils.
Cela peut également éviter toute confusion pour les nouveaux développeurs qui voient un paramètre inutilisé dans votre code et pourraient penser qu'il est superflu.
///
/// info | Info
Dans cet exemple, nous utilisons des en-têtes personnalisés fictifs `X-Key` et `X-Token`.
Mais dans des cas réels, lors de l'implémentation de la sécurité, vous tirerez davantage d'avantages en utilisant les [utilitaires de sécurité (chapitre suivant)](../security/index.md){.internal-link target=_blank} intégrés.
///
## Gérer les erreurs et les valeurs de retour des dépendances { #dependencies-errors-and-return-values }
Vous pouvez utiliser les mêmes *fonctions* de dépendance que d'habitude.
### Définir les exigences des dépendances { #dependency-requirements }
Elles peuvent déclarer des exigences pour la requête (comme des en-têtes) ou d'autres sous-dépendances :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Lever des exceptions { #raise-exceptions }
Ces dépendances peuvent `raise` des exceptions, comme des dépendances normales :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Gérer les valeurs de retour { #return-values }
Elles peuvent retourner des valeurs ou non, ces valeurs ne seront pas utilisées.
Vous pouvez donc réutiliser une dépendance normale (qui retourne une valeur) que vous utilisez déjà ailleurs ; même si la valeur n'est pas utilisée, la dépendance sera exécutée :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Définir des dépendances pour un groupe de chemins d'accès { #dependencies-for-a-group-of-path-operations }
Plus tard, en lisant comment structurer des applications plus grandes ([Applications plus grandes - Plusieurs fichiers](../../tutorial/bigger-applications.md){.internal-link target=_blank}), éventuellement avec plusieurs fichiers, vous apprendrez à déclarer un unique paramètre `dependencies` pour un groupe de *chemins d'accès*.
## Définir des dépendances globales { #global-dependencies }
Ensuite, nous verrons comment ajouter des dépendances à l'application `FastAPI` entière, afin qu'elles s'appliquent à chaque *chemin d'accès*.

View File

@ -0,0 +1,289 @@
# Utiliser des dépendances avec `yield` { #dependencies-with-yield }
FastAPI prend en charge des dépendances qui effectuent des <dfn title='parfois aussi appelées « exit code », « cleanup code », « teardown code », « closing code », « context manager exit code », etc.'>étapes supplémentaires après l'exécution</dfn>.
Pour cela, utilisez `yield` au lieu de `return`, et écrivez les étapes supplémentaires (code) après.
/// tip | Astuce
Vous devez vous assurer d'utiliser `yield` une seule fois par dépendance.
///
/// note | Détails techniques
Toute fonction valide à utiliser avec :
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> ou
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
sera valide comme dépendance **FastAPI**.
En fait, FastAPI utilise ces deux décorateurs en interne.
///
## Créer une dépendance de base de données avec `yield` { #a-database-dependency-with-yield }
Par exemple, vous pouvez l'utiliser pour créer une session de base de données et la fermer après la fin.
Seul le code précédant et incluant l'instruction `yield` est exécuté avant la création de la réponse :
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
La valeur transmise par `yield` est celle qui est injectée dans les *chemins d'accès* et autres dépendances :
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
Le code suivant l'instruction `yield` est exécuté après la réponse :
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Astuce
Vous pouvez utiliser des fonctions `async` ou des fonctions classiques.
**FastAPI** fera ce qu'il faut dans chaque cas, comme avec des dépendances normales.
///
## Créer une dépendance avec `yield` et `try` { #a-dependency-with-yield-and-try }
Si vous utilisez un bloc `try` dans une dépendance avec `yield`, vous recevrez toute exception qui a été levée lors de l'utilisation de la dépendance.
Par exemple, si à un moment donné, dans une autre dépendance ou dans un *chemin d'accès*, un code a effectué un « rollback » de transaction de base de données ou a créé une autre exception, vous recevrez l'exception dans votre dépendance.
Vous pouvez donc rechercher cette exception spécifique dans la dépendance avec `except SomeException`.
De la même manière, vous pouvez utiliser `finally` pour vous assurer que les étapes de sortie sont exécutées, qu'il y ait eu une exception ou non.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Utiliser des sous-dépendances avec `yield` { #sub-dependencies-with-yield }
Vous pouvez avoir des sous-dépendances et des « arbres » de sous-dépendances de toute taille et forme, et certaines ou toutes peuvent utiliser `yield`.
**FastAPI** s'assurera que le « code de sortie » dans chaque dépendance avec `yield` est exécuté dans le bon ordre.
Par exemple, `dependency_c` peut dépendre de `dependency_b`, et `dependency_b` de `dependency_a` :
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
Et elles peuvent toutes utiliser `yield`.
Dans ce cas, `dependency_c`, pour exécuter son code de sortie, a besoin que la valeur de `dependency_b` (appelée ici `dep_b`) soit toujours disponible.
Et, à son tour, `dependency_b` a besoin que la valeur de `dependency_a` (appelée ici `dep_a`) soit disponible pour son code de sortie.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
De la même manière, vous pouvez avoir certaines dépendances avec `yield` et d'autres avec `return`, et faire en sorte que certaines dépendent des autres.
Et vous pouvez avoir une seule dépendance qui exige plusieurs autres dépendances avec `yield`, etc.
Vous pouvez combiner les dépendances comme vous le souhaitez.
**FastAPI** s'assurera que tout est exécuté dans le bon ordre.
/// note | Détails techniques
Cela fonctionne grâce aux <a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">gestionnaires de contexte</a> de Python.
**FastAPI** les utilise en interne pour y parvenir.
///
## Utiliser des dépendances avec `yield` et `HTTPException` { #dependencies-with-yield-and-httpexception }
Vous avez vu que vous pouvez utiliser des dépendances avec `yield` et avoir des blocs `try` qui tentent d'exécuter du code puis exécutent du code de sortie après `finally`.
Vous pouvez également utiliser `except` pour intercepter l'exception qui a été levée et faire quelque chose avec.
Par exemple, vous pouvez lever une autre exception, comme `HTTPException`.
/// tip | Astuce
C'est une technique plutôt avancée, et dans la plupart des cas vous n'en aurez pas vraiment besoin, car vous pouvez lever des exceptions (y compris `HTTPException`) depuis le reste de votre code applicatif, par exemple, dans la *fonction de chemin d'accès*.
Mais elle est à votre disposition si vous en avez besoin. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Si vous souhaitez intercepter des exceptions et créer une réponse personnalisée en fonction de cela, créez un [Gestionnaire d'exceptions personnalisé](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
## Utiliser des dépendances avec `yield` et `except` { #dependencies-with-yield-and-except }
Si vous interceptez une exception avec `except` dans une dépendance avec `yield` et que vous ne la relancez pas (ou que vous ne levez pas une nouvelle exception), FastAPI ne pourra pas remarquer qu'il y a eu une exception, de la même manière que cela se produirait avec Python classique :
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
Dans ce cas, le client verra une réponse *HTTP 500 Internal Server Error* comme il se doit, étant donné que nous ne levons pas de `HTTPException` ou similaire, mais le serveur **n'aura aucun logs** ni aucune autre indication de l'erreur. 😱
### Toujours `raise` dans les dépendances avec `yield` et `except` { #always-raise-in-dependencies-with-yield-and-except }
Si vous interceptez une exception dans une dépendance avec `yield`, à moins de lever une autre `HTTPException` ou similaire, **vous devez relancer l'exception d'origine**.
Vous pouvez relancer la même exception avec `raise` :
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
À présent, le client recevra la même réponse *HTTP 500 Internal Server Error*, mais le serveur aura notre `InternalError` personnalisé dans les logs. 😎
## Comprendre l'exécution des dépendances avec `yield` { #execution-of-dependencies-with-yield }
La séquence d'exécution ressemble plus ou moins à ce diagramme. Le temps s'écoule de haut en bas. Et chaque colonne représente une des parties qui interagit ou exécute du code.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info
Une **seule réponse** sera envoyée au client. Il peut s'agir d'une des réponses d'erreur ou de la réponse provenant du *chemin d'accès*.
Après l'envoi de l'une de ces réponses, aucune autre réponse ne peut être envoyée.
///
/// tip | Astuce
Si vous levez une exception dans le code de la *fonction de chemin d'accès*, elle sera transmise aux dépendances avec `yield`, y compris `HTTPException`. Dans la plupart des cas, vous voudrez relancer cette même exception ou en lever une nouvelle depuis la dépendance avec `yield` pour vous assurer qu'elle est correctement gérée.
///
## Utiliser la sortie anticipée et `scope` { #early-exit-and-scope }
Normalement, le code de sortie des dépendances avec `yield` est exécuté **après la réponse** envoyée au client.
Mais si vous savez que vous n'aurez pas besoin d'utiliser la dépendance après être revenu de la *fonction de chemin d'accès*, vous pouvez utiliser `Depends(scope="function")` pour indiquer à FastAPI qu'il doit fermer la dépendance après le retour de la *fonction de chemin d'accès*, mais **avant** que la **réponse ne soit envoyée**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` reçoit un paramètre `scope` qui peut être :
* « function » : démarrer la dépendance avant la *fonction de chemin d'accès* qui gère la requête, terminer la dépendance après la fin de la *fonction de chemin d'accès*, mais **avant** que la réponse ne soit renvoyée au client. Ainsi, la fonction de dépendance sera exécutée **autour** de la *fonction de chemin d'accès*.
* « request » : démarrer la dépendance avant la *fonction de chemin d'accès* qui gère la requête (similaire à l'utilisation de « function »), mais terminer **après** que la réponse a été renvoyée au client. Ainsi, la fonction de dépendance sera exécutée **autour** du cycle **requête** et réponse.
S'il n'est pas spécifié et que la dépendance utilise `yield`, le `scope` sera par défaut « request ».
### Définir `scope` pour les sous-dépendances { #scope-for-sub-dependencies }
Lorsque vous déclarez une dépendance avec un `scope="request"` (par défaut), toute sous-dépendance doit également avoir un `scope` de « request ».
Mais une dépendance avec un `scope` de « function » peut avoir des dépendances avec un `scope` de « function » et un `scope` de « request ».
Cela vient du fait que toute dépendance doit pouvoir exécuter son code de sortie avant ses sous-dépendances, car elle pourrait encore avoir besoin de les utiliser pendant son code de sortie.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
## Utiliser des dépendances avec `yield`, `HTTPException`, `except` et Background Tasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
Les dépendances avec `yield` ont évolué au fil du temps pour couvrir différents cas d'utilisation et corriger certains problèmes.
Si vous souhaitez voir ce qui a changé dans différentes versions de FastAPI, vous pouvez en savoir plus dans le guide avancé, dans [Dépendances avancées - Dépendances avec `yield`, `HTTPException`, `except` et Background Tasks](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks){.internal-link target=_blank}.
## Gestionnaires de contexte { #context-managers }
### Que sont les « Context Managers » { #what-are-context-managers }
Les « Context Managers » sont des objets Python que vous pouvez utiliser dans une instruction `with`.
Par exemple, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">vous pouvez utiliser `with` pour lire un fichier</a> :
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
En coulisse, `open("./somefile.txt")` crée un objet appelé « Context Manager ».
Lorsque le bloc `with` se termine, il s'assure de fermer le fichier, même s'il y a eu des exceptions.
Lorsque vous créez une dépendance avec `yield`, **FastAPI** créera en interne un gestionnaire de contexte pour celle-ci et le combinera avec d'autres outils associés.
### Utiliser des gestionnaires de contexte dans des dépendances avec `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Alertes
C'est, plus ou moins, une idée « avancée ».
Si vous débutez avec **FastAPI**, vous voudrez peut-être l'ignorer pour le moment.
///
En Python, vous pouvez créer des gestionnaires de contexte en <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">créant une classe avec deux méthodes : `__enter__()` et `__exit__()`</a>.
Vous pouvez également les utiliser dans des dépendances **FastAPI** avec `yield` en utilisant
des instructions `with` ou `async with` à l'intérieur de la fonction de dépendance :
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Astuce
Une autre façon de créer un gestionnaire de contexte consiste à utiliser :
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> ou
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
pour décorer une fonction avec un unique `yield`.
C'est ce que **FastAPI** utilise en interne pour les dépendances avec `yield`.
Mais vous n'avez pas à utiliser ces décorateurs pour les dépendances FastAPI (et vous ne devriez pas).
FastAPI le fera pour vous en interne.
///

View File

@ -0,0 +1,15 @@
# Dépendances globales { #global-dependencies }
Pour certains types d'applications, vous pourriez vouloir ajouter des dépendances à l'application entière.
Comme vous pouvez [ajouter des `dependencies` aux *décorateurs de chemin d'accès*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, vous pouvez les ajouter à l'application `FastAPI`.
Dans ce cas, elles seront appliquées à tous les *chemins d'accès* de l'application :
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Et toutes les idées de la section sur [l'ajout de `dependencies` aux *décorateurs de chemin d'accès*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} s'appliquent toujours, mais dans ce cas à tous les *chemins d'accès* de l'application.
## Dépendances pour des groupes de *chemins d'accès* { #dependencies-for-groups-of-path-operations }
Plus tard, en lisant comment structurer des applications plus grandes ([Applications plus grandes - Plusieurs fichiers](../../tutorial/bigger-applications.md){.internal-link target=_blank}), éventuellement avec plusieurs fichiers, vous apprendrez comment déclarer un unique paramètre `dependencies` pour un groupe de *chemins d'accès*.

View File

@ -0,0 +1,250 @@
# Dépendances { #dependencies }
**FastAPI** dispose dun système d**<dfn title="aussi connu sous le nom de : composants, ressources, fournisseurs, services, injectables">Injection de dépendances</dfn>** très puissant mais intuitif.
Il est conçu pour être très simple à utiliser, et pour faciliter lintégration dautres composants à **FastAPI** pour nimporte quel développeur.
## Quest-ce que « linjection de dépendances » { #what-is-dependency-injection }
L**« injection de dépendances »** signifie, en programmation, quil existe un moyen pour votre code (dans ce cas, vos fonctions de chemins daccès) de déclarer ce dont il a besoin pour fonctionner et utiliser : « dépendances ».
Ensuite, ce système (dans ce cas **FastAPI**) se charge de faire tout le nécessaire pour fournir à votre code ces dépendances requises (« injecter » les dépendances).
Cest très utile lorsque vous avez besoin de :
* Avoir de la logique partagée (la même logique de code encore et encore).
* Partager des connexions à la base de données.
* Imposer la sécurité, lauthentification, des exigences de rôles, etc.
* Et bien dautres choses ...
Tout cela, en minimisant la répétition de code.
## Premiers pas { #first-steps }
Voyons un exemple très simple. Il sera tellement simple quil nest pas très utile, pour linstant.
Mais de cette façon nous pouvons nous concentrer sur le fonctionnement du système d**injection de dépendances**.
### Créer une dépendance, ou « dependable » { #create-a-dependency-or-dependable }
Concentrons-nous dabord sur la dépendance.
Cest simplement une fonction qui peut prendre tous les mêmes paramètres quune fonction de chemin daccès peut prendre :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
Cest tout.
**2 lignes**.
Et elle a la même forme et structure que toutes vos fonctions de chemins daccès.
Vous pouvez la considérer comme une fonction de chemin daccès sans le « décorateur » (sans le `@app.get("/some-path")`).
Et elle peut retourner tout ce que vous voulez.
Dans ce cas, cette dépendance attend :
* Un paramètre de requête optionnel `q` qui est une `str`.
* Un paramètre de requête optionnel `skip` qui est un `int`, et vaut `0` par défaut.
* Un paramètre de requête optionnel `limit` qui est un `int`, et vaut `100` par défaut.
Puis elle retourne simplement un `dict` contenant ces valeurs.
/// info | Info
FastAPI a ajouté la prise en charge de `Annotated` (et a commencé à le recommander) dans la version 0.95.0.
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant dutiliser `Annotated`.
Vous devez vous assurer de [mettre à niveau la version de FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} vers au moins la 0.95.1 avant dutiliser `Annotated`.
///
### Importer `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Déclarer la dépendance, dans le « dependant » { #declare-the-dependency-in-the-dependant }
De la même manière que vous utilisez `Body`, `Query`, etc. avec les paramètres de votre fonction de chemin daccès, utilisez `Depends` avec un nouveau paramètre :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Même si vous utilisez `Depends` dans les paramètres de votre fonction de la même façon que `Body`, `Query`, etc., `Depends` fonctionne un peu différemment.
Vous ne donnez à `Depends` quun seul paramètre.
Ce paramètre doit être quelque chose comme une fonction.
Vous ne lappelez pas directement (najoutez pas de parenthèses à la fin), vous le passez simplement en paramètre à `Depends()`.
Et cette fonction prend des paramètres de la même manière que les fonctions de chemins daccès.
/// tip | Astuce
Vous verrez quelles autres « choses », en plus des fonctions, peuvent être utilisées comme dépendances dans le prochain chapitre.
///
Chaque fois quune nouvelle requête arrive, **FastAPI** se charge de :
* Appeler votre fonction de dépendance (« dependable ») avec les bons paramètres.
* Récupérer le résultat de votre fonction.
* Affecter ce résultat au paramètre dans votre fonction de chemin daccès.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
De cette façon vous écrivez le code partagé une seule fois et **FastAPI** se charge de lappeler pour vos chemins daccès.
/// check | Vérifications
Notez que vous navez pas à créer une classe spéciale et à la passer quelque part à **FastAPI** pour l« enregistrer » ou quoi que ce soit de similaire.
Vous la passez simplement à `Depends` et **FastAPI** sait faire le reste.
///
## Partager des dépendances `Annotated` { #share-annotated-dependencies }
Dans les exemples ci-dessus, vous voyez quil y a un tout petit peu de **duplication de code**.
Lorsque vous devez utiliser la dépendance `common_parameters()`, vous devez écrire tout le paramètre avec lannotation de type et `Depends()` :
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Mais comme nous utilisons `Annotated`, nous pouvons stocker cette valeur `Annotated` dans une variable et lutiliser à plusieurs endroits :
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Astuce
Cest simplement du Python standard, cela sappelle un « alias de type », ce nest en fait pas spécifique à **FastAPI**.
Mais comme **FastAPI** est basé sur les standards Python, y compris `Annotated`, vous pouvez utiliser cette astuce dans votre code. 😎
///
Les dépendances continueront de fonctionner comme prévu, et la **meilleure partie** est que **linformation de type sera conservée**, ce qui signifie que votre éditeur pourra continuer à vous fournir **lautocomplétion**, **des erreurs en ligne**, etc. Idem pour dautres outils comme `mypy`.
Cela sera particulièrement utile lorsque vous lutiliserez dans une **grande base de code** où vous utilisez **les mêmes dépendances** encore et encore dans **de nombreux chemins daccès**.
## Utiliser `async` ou non { #to-async-or-not-to-async }
Comme les dépendances seront aussi appelées par **FastAPI** (tout comme vos fonctions de chemins daccès), les mêmes règles sappliquent lors de la définition de vos fonctions.
Vous pouvez utiliser `async def` ou un `def` normal.
Et vous pouvez déclarer des dépendances avec `async def` à lintérieur de fonctions de chemins daccès `def` normales, ou des dépendances `def` à lintérieur de fonctions de chemins daccès `async def`, etc.
Peu importe. **FastAPI** saura quoi faire.
/// note | Remarque
Si vous ne savez pas, consultez la section [Async : *« Pressé ? »*](../../async.md#in-a-hurry){.internal-link target=_blank} à propos de `async` et `await` dans la documentation.
///
## Intégrer à OpenAPI { #integrated-with-openapi }
Toutes les déclarations de requête, validations et exigences de vos dépendances (et sous-dépendances) seront intégrées dans le même schéma OpenAPI.
Ainsi, la documentation interactive contiendra aussi toutes les informations issues de ces dépendances :
<img src="/img/tutorial/dependencies/image01.png">
## Utilisation simple { #simple-usage }
Si vous y regardez de près, les fonctions de chemins daccès sont déclarées pour être utilisées chaque fois quun « chemin » et une « opération » correspondent, puis **FastAPI** se charge dappeler la fonction avec les bons paramètres, en extrayant les données de la requête.
En réalité, tous (ou la plupart) des frameworks web fonctionnent de cette manière.
Vous nappelez jamais ces fonctions directement. Elles sont appelées par votre framework (dans ce cas, **FastAPI**).
Avec le système dinjection de dépendances, vous pouvez aussi indiquer à **FastAPI** que votre fonction de chemin daccès « dépend » également dautre chose qui doit être exécuté avant votre fonction de chemin daccès, et **FastAPI** se chargera de lexécuter et d« injecter » les résultats.
Dautres termes courants pour cette même idée « dinjection de dépendances » sont :
* ressources
* fournisseurs
* services
* injectables
* composants
## Plug-ins **FastAPI** { #fastapi-plug-ins }
Les intégrations et « plug-ins » peuvent être construits en utilisant le système d**injection de dépendances**. Mais en réalité, il ny a **pas besoin de créer des « plug-ins »**, car en utilisant des dépendances il est possible de déclarer un nombre infini dintégrations et dinteractions qui deviennent disponibles pour vos fonctions de chemins daccès.
Et les dépendances peuvent être créées de manière très simple et intuitive, ce qui vous permet dimporter juste les packages Python dont vous avez besoin, et de les intégrer à vos fonctions dAPI en quelques lignes de code, *littéralement*.
Vous verrez des exemples de cela dans les prochains chapitres, à propos des bases de données relationnelles et NoSQL, de la sécurité, etc.
## Compatibilité **FastAPI** { #fastapi-compatibility }
La simplicité du système dinjection de dépendances rend **FastAPI** compatible avec :
* toutes les bases de données relationnelles
* les bases de données NoSQL
* les packages externes
* les API externes
* les systèmes dauthentification et dautorisation
* les systèmes de supervision dusage dAPI
* les systèmes dinjection de données de réponse
* etc.
## Simple et puissant { #simple-and-powerful }
Bien que le système hiérarchique dinjection de dépendances soit très simple à définir et à utiliser, il reste très puissant.
Vous pouvez définir des dépendances qui, à leur tour, peuvent définir leurs propres dépendances.
Au final, un arbre hiérarchique de dépendances est construit, et le système d**injection de dépendances** se charge de résoudre toutes ces dépendances pour vous (et leurs sous-dépendances) et de fournir (injecter) les résultats à chaque étape.
Par exemple, supposons que vous ayez 4 endpoints dAPI (chemins daccès) :
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
alors vous pourriez ajouter différentes exigences dautorisations pour chacun deux uniquement avec des dépendances et des sous-dépendances :
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Intégrer à **OpenAPI** { #integrated-with-openapi_1 }
Toutes ces dépendances, tout en déclarant leurs exigences, ajoutent également des paramètres, des validations, etc. à vos chemins daccès.
**FastAPI** se chargera dajouter le tout au schéma OpenAPI, afin que cela apparaisse dans les systèmes de documentation interactive.

View File

@ -0,0 +1,105 @@
# Sous-dépendances { #sub-dependencies }
Vous pouvez créer des dépendances qui ont des sous-dépendances.
Elles peuvent être aussi profondes que nécessaire.
**FastAPI** se chargera de les résoudre.
## Créer une première dépendance « dependable » { #first-dependency-dependable }
Vous pouvez créer une première dépendance (« dependable ») comme :
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Elle déclare un paramètre de requête optionnel `q` de type `str`, puis le retourne simplement.
C'est assez simple (pas très utile), mais cela nous aidera à nous concentrer sur le fonctionnement des sous-dépendances.
## Créer une seconde dépendance, « dependable » et « dependant » { #second-dependency-dependable-and-dependant }
Vous pouvez ensuite créer une autre fonction de dépendance (un « dependable ») qui, en même temps, déclare sa propre dépendance (elle est donc aussi un « dependant ») :
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Concentrons-nous sur les paramètres déclarés :
- Même si cette fonction est ellemême une dépendance (« dependable »), elle déclare aussi une autre dépendance (elle « dépend » d'autre chose).
- Elle dépend de `query_extractor` et affecte la valeur renvoyée au paramètre `q`.
- Elle déclare également un cookie `last_query` optionnel, de type `str`.
- Si l'utilisateur n'a fourni aucune requête `q`, nous utilisons la dernière requête utilisée, que nous avons enregistrée auparavant dans un cookie.
## Utiliser la dépendance { #use-the-dependency }
Nous pouvons ensuite utiliser la dépendance avec :
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info
Notez que nous ne déclarons qu'une seule dépendance dans la *fonction de chemin d'accès*, `query_or_cookie_extractor`.
Mais **FastAPI** saura qu'il doit d'abord résoudre `query_extractor`, pour passer ses résultats à `query_or_cookie_extractor` lors de son appel.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Utiliser la même dépendance plusieurs fois { #using-the-same-dependency-multiple-times }
Si l'une de vos dépendances est déclarée plusieurs fois pour le même *chemin d'accès*, par exemple si plusieurs dépendances ont une sous-dépendance commune, **FastAPI** saura n'appeler cette sous-dépendance qu'une seule fois par requête.
Et il enregistrera la valeur renvoyée dans un <dfn title="Un utilitaire/système pour stocker des valeurs calculées/générées, afin de les réutiliser au lieu de les recalculer.">« cache »</dfn> et la transmettra à tous les « dependants » qui en ont besoin dans cette requête spécifique, au lieu d'appeler la dépendance plusieurs fois pour la même requête.
Dans un scénario avancé où vous savez que vous avez besoin que la dépendance soit appelée à chaque étape (éventuellement plusieurs fois) dans la même requête au lieu d'utiliser la valeur « mise en cache », vous pouvez définir le paramètre `use_cache=False` lors de l'utilisation de `Depends` :
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ non annoté
/// tip | Astuce
Privilégiez la version `Annotated` si possible.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Récapituler { #recap }
En dehors de tout le jargon utilisé ici, le système d'**injection de dépendances** est assez simple.
Ce ne sont que des fonctions qui ressemblent aux *fonctions de chemin d'accès*.
Mais il est très puissant et vous permet de déclarer des « graphes » (arbres) de dépendances imbriquées aussi profondément que vous le souhaitez.
/// tip | Astuce
Tout cela peut ne pas sembler très utile avec ces exemples simples.
Mais vous verrez à quel point c'est utile dans les chapitres sur la **sécurité**.
Et vous verrez aussi la quantité de code que cela vous fera économiser.
///

View File

@ -0,0 +1,35 @@
# Encodeur compatible JSON { #json-compatible-encoder }
Il existe des cas où vous pourriez avoir besoin de convertir un type de données (comme un modèle Pydantic) en quelque chose de compatible avec JSON (comme un `dict`, `list`, etc.).
Par exemple, si vous devez le stocker dans une base de données.
Pour cela, **FastAPI** fournit une fonction `jsonable_encoder()`.
## Utiliser `jsonable_encoder` { #using-the-jsonable-encoder }
Imaginons que vous ayez une base de données `fake_db` qui ne reçoit que des données compatibles JSON.
Par exemple, elle ne reçoit pas d'objets `datetime`, car ceux-ci ne sont pas compatibles avec JSON.
Ainsi, un objet `datetime` doit être converti en une `str` contenant les données au <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">format ISO</a>.
De la même manière, cette base de données n'accepterait pas un modèle Pydantic (un objet avec des attributs), seulement un `dict`.
Vous pouvez utiliser `jsonable_encoder` pour cela.
Elle reçoit un objet, comme un modèle Pydantic, et renvoie une version compatible JSON :
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
Dans cet exemple, elle convertirait le modèle Pydantic en `dict`, et le `datetime` en `str`.
Le résultat de son appel est quelque chose qui peut être encodé avec la fonction standard de Python <a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>.
Elle ne renvoie pas une grande `str` contenant les données au format JSON (sous forme de chaîne). Elle renvoie une structure de données standard de Python (par ex. un `dict`) avec des valeurs et sous-valeurs toutes compatibles avec JSON.
/// note | Remarque
`jsonable_encoder` est en fait utilisée par **FastAPI** en interne pour convertir des données. Mais elle est utile dans de nombreux autres scénarios.
///

View File

@ -0,0 +1,62 @@
# Types de données supplémentaires { #extra-data-types }
Jusqu'à présent, vous avez utilisé des types de données courants, comme :
* `int`
* `float`
* `str`
* `bool`
Mais vous pouvez aussi utiliser des types de données plus complexes.
Et vous bénéficierez toujours des mêmes fonctionnalités que jusqu'à présent :
* Excellente prise en charge dans l'éditeur.
* Conversion des données à partir des requêtes entrantes.
* Conversion des données pour les données de réponse.
* Validation des données.
* Annotations et documentation automatiques.
## Autres types de données { #other-data-types }
Voici quelques types de données supplémentaires que vous pouvez utiliser :
* `UUID` :
* Un « identifiant universel unique » standard, couramment utilisé comme ID dans de nombreuses bases de données et systèmes.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str`.
* `datetime.datetime` :
* Un `datetime.datetime` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str` au format ISO 8601, par exemple : `2008-09-15T15:53:00+05:00`.
* `datetime.date` :
* `datetime.date` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str` au format ISO 8601, par exemple : `2008-09-15`.
* `datetime.time` :
* Un `datetime.time` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str` au format ISO 8601, par exemple : `14:23:55.003`.
* `datetime.timedelta` :
* Un `datetime.timedelta` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `float` de secondes totales.
* Pydantic permet aussi de le représenter sous la forme d'un « encodage de différence de temps ISO 8601 », <a href="https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers" class="external-link" target="_blank">voir la documentation pour plus d'informations</a>.
* `frozenset` :
* Dans les requêtes et les réponses, traité de la même manière qu'un `set` :
* Dans les requêtes, une liste sera lue, les doublons éliminés, puis convertie en `set`.
* Dans les réponses, le `set` sera converti en `list`.
* Le schéma généré indiquera que les valeurs du `set` sont uniques (en utilisant `uniqueItems` de JSON Schema).
* `bytes` :
* `bytes` Python standard.
* Dans les requêtes et les réponses, traité comme une `str`.
* Le schéma généré indiquera qu'il s'agit d'une `str` avec le « format » `binary`.
* `Decimal` :
* `Decimal` Python standard.
* Dans les requêtes et les réponses, géré de la même manière qu'un `float`.
* Vous pouvez consulter tous les types de données Pydantic valides ici : <a href="https://docs.pydantic.dev/latest/usage/types/types/" class="external-link" target="_blank">Types de données Pydantic</a>.
## Exemple { #example }
Voici un exemple de *chemin d'accès* avec des paramètres utilisant certains des types ci-dessus.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Notez que les paramètres à l'intérieur de la fonction ont leur type de données naturel et que vous pouvez, par exemple, effectuer des manipulations de dates normales, comme :
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}

View File

@ -0,0 +1,211 @@
# Modèles supplémentaires { #extra-models }
En poursuivant l'exemple précédent, il est courant d'avoir plusieurs modèles liés.
C'est particulièrement vrai pour les modèles d'utilisateur, car :
* Le modèle d'entrée doit pouvoir contenir un mot de passe.
* Le modèle de sortie ne doit pas avoir de mot de passe.
* Le modèle de base de données devra probablement avoir un mot de passe haché.
/// danger | Danger
Ne stockez jamais les mots de passe des utilisateurs en clair. Stockez toujours un « hachage sécurisé » que vous pourrez ensuite vérifier.
Si vous ne savez pas ce que c'est, vous apprendrez ce qu'est un « hachage de mot de passe » dans les [chapitres sur la sécurité](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}.
///
## Utiliser plusieurs modèles { #multiple-models }
Voici une idée générale de l'apparence des modèles avec leurs champs de mot de passe et les endroits où ils sont utilisés :
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### À propos de `**user_in.model_dump()` { #about-user-in-model-dump }
#### La méthode `.model_dump()` de Pydantic { #pydantics-model-dump }
`user_in` est un modèle Pydantic de classe `UserIn`.
Les modèles Pydantic ont une méthode `.model_dump()` qui renvoie un `dict` avec les données du modèle.
Ainsi, si nous créons un objet Pydantic `user_in` comme :
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
et que nous appelons ensuite :
```Python
user_dict = user_in.model_dump()
```
nous avons maintenant un `dict` avec les données dans la variable `user_dict` (c'est un `dict` au lieu d'un objet modèle Pydantic).
Et si nous appelons :
```Python
print(user_dict)
```
nous obtiendrions un `dict` Python contenant :
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Déballer un `dict` { #unpacking-a-dict }
Si nous prenons un `dict` comme `user_dict` et que nous le passons à une fonction (ou une classe) avec `**user_dict`, Python va « déballer » ce `dict`. Il passera les clés et valeurs de `user_dict` directement comme arguments nommés.
Ainsi, en reprenant `user_dict` ci-dessus, écrire :
```Python
UserInDB(**user_dict)
```
aurait pour résultat quelque chose d'équivalent à :
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Ou plus exactement, en utilisant `user_dict` directement, quels que soient ses contenus futurs :
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Créer un modèle Pydantic à partir du contenu d'un autre { #a-pydantic-model-from-the-contents-of-another }
Comme dans l'exemple ci-dessus nous avons obtenu `user_dict` depuis `user_in.model_dump()`, ce code :
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
serait équivalent à :
```Python
UserInDB(**user_in.model_dump())
```
... parce que `user_in.model_dump()` est un `dict`, et nous demandons ensuite à Python de « déballer » ce `dict` en le passant à `UserInDB` précédé de `**`.
Ainsi, nous obtenons un modèle Pydantic à partir des données d'un autre modèle Pydantic.
#### Déballer un `dict` et ajouter des mots-clés supplémentaires { #unpacking-a-dict-and-extra-keywords }
Et en ajoutant ensuite l'argument nommé supplémentaire `hashed_password=hashed_password`, comme ici :
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
... revient à :
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Alertes
Les fonctions auxiliaires `fake_password_hasher` et `fake_save_user` ne servent qu'à démontrer un flux de données possible, mais elles n'offrent évidemment aucune sécurité réelle.
///
## Réduire la duplication { #reduce-duplication }
Réduire la duplication de code est l'une des idées centrales de **FastAPI**.
La duplication de code augmente les risques de bogues, de problèmes de sécurité, de désynchronisation du code (lorsque vous mettez à jour un endroit mais pas les autres), etc.
Et ces modèles partagent beaucoup de données et dupliquent des noms et types d'attributs.
Nous pouvons faire mieux.
Nous pouvons déclarer un modèle `UserBase` qui sert de base à nos autres modèles. Ensuite, nous pouvons créer des sous-classes de ce modèle qui héritent de ses attributs (déclarations de type, validation, etc.).
Toutes les conversions de données, validations, documentation, etc., fonctionneront comme d'habitude.
De cette façon, nous pouvons ne déclarer que les différences entre les modèles (avec `password` en clair, avec `hashed_password` et sans mot de passe) :
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` ou `anyOf` { #union-or-anyof }
Vous pouvez déclarer qu'une réponse est l'`Union` de deux types ou plus, ce qui signifie que la réponse peut être n'importe lequel d'entre eux.
Cela sera défini dans OpenAPI avec `anyOf`.
Pour ce faire, utilisez l'annotation de type Python standard <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a> :
/// note | Remarque
Lors de la définition d'une <a href="https://docs.pydantic.dev/latest/concepts/types/#unions" class="external-link" target="_blank">`Union`</a>, incluez d'abord le type le plus spécifique, suivi du type le moins spécifique. Dans l'exemple ci-dessous, le type le plus spécifique `PlaneItem` précède `CarItem` dans `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` en Python 3.10 { #union-in-python-3-10 }
Dans cet exemple, nous passons `Union[PlaneItem, CarItem]` comme valeur de l'argument `response_model`.
Comme nous le passons comme valeur d'un argument au lieu de l'utiliser dans une annotation de type, nous devons utiliser `Union` même en Python 3.10.
S'il s'agissait d'une annotation de type, nous pourrions utiliser la barre verticale, comme :
```Python
some_variable: PlaneItem | CarItem
```
Mais si nous écrivons cela dans l'affectation `response_model=PlaneItem | CarItem`, nous obtiendrons une erreur, car Python essaierait d'effectuer une « opération invalide » entre `PlaneItem` et `CarItem` au lieu de l'interpréter comme une annotation de type.
## Liste de modèles { #list-of-models }
De la même manière, vous pouvez déclarer des réponses contenant des listes d'objets.
Pour cela, utilisez le `list` Python standard :
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Réponse avec un `dict` arbitraire { #response-with-arbitrary-dict }
Vous pouvez également déclarer une réponse en utilisant un simple `dict` arbitraire, en déclarant uniquement le type des clés et des valeurs, sans utiliser de modèle Pydantic.
C'est utile si vous ne connaissez pas à l'avance les noms de champs/attributs valides (qui seraient nécessaires pour un modèle Pydantic).
Dans ce cas, vous pouvez utiliser `dict` :
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Récapitulatif { #recap }
Utilisez plusieurs modèles Pydantic et héritez librement selon chaque cas.
Vous n'avez pas besoin d'avoir un seul modèle de données par entité si cette entité doit pouvoir avoir différents « états ». Comme pour l'« entité » utilisateur, avec un état incluant `password`, `password_hash` et sans mot de passe.

View File

@ -1,8 +1,8 @@
# Démarrage { #first-steps }
# Démarrer { #first-steps }
Le fichier **FastAPI** le plus simple possible pourrait ressembler à ceci :
{* ../../docs_src/first_steps/tutorial001_py39.py *}
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Copiez cela dans un fichier `main.py`.
@ -56,7 +56,7 @@ INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Cette ligne montre lURL où votre application est servie, sur votre machine locale.
### Vérifiez { #check-it }
### Vérifier { #check-it }
Ouvrez votre navigateur à ladresse <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
@ -183,7 +183,7 @@ Cest tout ! Vous pouvez maintenant accéder à votre application à cette URL
### Étape 1 : importer `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` est une classe Python qui fournit toutes les fonctionnalités nécessaires à votre API.
@ -197,7 +197,7 @@ Vous pouvez donc aussi utiliser toutes les fonctionnalités de <a href="https://
### Étape 2 : créer une « instance » `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Ici, la variable `app` sera une « instance » de la classe `FastAPI`.
@ -266,14 +266,14 @@ Nous allons donc aussi les appeler « opérations ».
#### Définir un « décorateur de chemin daccès » { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
Le `@app.get("/")` indique à **FastAPI** que la fonction juste en dessous est chargée de gérer les requêtes qui vont vers:
* le chemin `/`
* en utilisant une <abbr title="une méthode HTTP GET"><code>get</code> opération</abbr>
* en utilisant une <dfn title="une méthode HTTP GET"><code>get</code> opération</dfn>
/// info | `@décorateur` Info
/// info | `@decorator` Info
Cette syntaxe `@something` en Python est appelée un « décorateur ».
@ -320,7 +320,7 @@ Voici notre « fonction de chemin daccès » :
* **opération** : `get`.
* **fonction** : la fonction sous le « décorateur » (sous `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Cest une fonction Python.
@ -332,9 +332,9 @@ Dans ce cas, cest une fonction `async`.
Vous pouvez aussi la définir comme une fonction normale au lieu de `async def` :
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note
/// note | Remarque
Si vous ne connaissez pas la différence, consultez [Asynchrone : « Pressé ? »](../async.md#in-a-hurry){.internal-link target=_blank}.
@ -342,7 +342,7 @@ Si vous ne connaissez pas la différence, consultez [Asynchrone : « Pressé ?
### Étape 5 : retourner le contenu { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Vous pouvez retourner un `dict`, une `list`, des valeurs uniques comme `str`, `int`, etc.

View File

@ -0,0 +1,244 @@
# Gérer les erreurs { #handling-errors }
Il existe de nombreuses situations où vous devez signaler une erreur à un client qui utilise votre API.
Ce client peut être un navigateur avec un frontend, un code d'un tiers, un appareil IoT, etc.
Vous pourriez avoir besoin d'indiquer au client que :
* Le client n'a pas les privilèges suffisants pour cette opération.
* Le client n'a pas accès à cette ressource.
* L'élément auquel le client tentait d'accéder n'existe pas.
* etc.
Dans ces cas, vous retournez normalement un **code d'état HTTP** dans la plage de **400** (de 400 à 499).
C'est similaire aux codes d'état HTTP 200 (de 200 à 299). Ces codes « 200 » signifient que, d'une certaine manière, la requête a été un « succès ».
Les codes d'état dans la plage des 400 signifient qu'il y a eu une erreur côté client.
Vous souvenez-vous de toutes ces erreurs **« 404 Not Found »** (et des blagues) ?
## Utiliser `HTTPException` { #use-httpexception }
Pour renvoyer au client des réponses HTTP avec des erreurs, vous utilisez `HTTPException`.
### Importer `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Lever une `HTTPException` dans votre code { #raise-an-httpexception-in-your-code }
`HTTPException` est une exception Python normale avec des données supplémentaires pertinentes pour les API.
Comme il s'agit d'une exception Python, vous ne la `return` pas, vous la `raise`.
Cela signifie aussi que si vous êtes dans une fonction utilitaire appelée depuis votre fonction de chemin d'accès, et que vous levez la `HTTPException` à l'intérieur de cette fonction utilitaire, le reste du code de la fonction de chemin d'accès ne s'exécutera pas : la requête sera immédiatement interrompue et l'erreur HTTP issue de la `HTTPException` sera envoyée au client.
L'avantage de lever une exception plutôt que de retourner une valeur apparaîtra plus clairement dans la section sur les Dépendances et la Sécurité.
Dans cet exemple, lorsque le client demande un élément par un ID qui n'existe pas, levez une exception avec un code d'état `404` :
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### Réponse résultante { #the-resulting-response }
Si le client demande `http://example.com/items/foo` (un `item_id` « foo »), il recevra un code d'état HTTP 200 et une réponse JSON :
```JSON
{
"item": "The Foo Wrestlers"
}
```
Mais si le client demande `http://example.com/items/bar` (un `item_id` inexistant « bar »), il recevra un code d'état HTTP 404 (l'erreur « not found ») et une réponse JSON :
```JSON
{
"detail": "Item not found"
}
```
/// tip | Astuce
Lorsque vous levez une `HTTPException`, vous pouvez passer n'importe quelle valeur convertible en JSON comme paramètre `detail`, pas uniquement un `str`.
Vous pouvez passer un `dict`, une `list`, etc.
Elles sont gérées automatiquement par **FastAPI** et converties en JSON.
///
## Ajouter des en-têtes personnalisés { #add-custom-headers }
Dans certaines situations, il est utile de pouvoir ajouter des en-têtes personnalisés à l'erreur HTTP. Par exemple, pour certains types de sécurité.
Vous n'aurez probablement pas besoin de l'utiliser directement dans votre code.
Mais si vous en aviez besoin pour un scénario avancé, vous pouvez ajouter des en-têtes personnalisés :
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Installer des gestionnaires d'exception personnalisés { #install-custom-exception-handlers }
Vous pouvez ajouter des gestionnaires d'exception personnalisés avec <a href="https://www.starlette.dev/exceptions/" class="external-link" target="_blank">les mêmes utilitaires d'exception de Starlette</a>.
Supposons que vous ayez une exception personnalisée `UnicornException` que vous (ou une bibliothèque que vous utilisez) pourriez `raise`.
Et vous souhaitez gérer cette exception globalement avec FastAPI.
Vous pouvez ajouter un gestionnaire d'exception personnalisé avec `@app.exception_handler()` :
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Ici, si vous appelez `/unicorns/yolo`, le chemin d'accès va `raise` une `UnicornException`.
Mais elle sera gérée par `unicorn_exception_handler`.
Ainsi, vous recevrez une erreur propre, avec un code d'état HTTP `418` et un contenu JSON :
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.requests import Request` et `from starlette.responses import JSONResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` par simple commodité pour vous, développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en va de même pour `Request`.
///
## Remplacer les gestionnaires d'exception par défaut { #override-the-default-exception-handlers }
**FastAPI** fournit des gestionnaires d'exception par défaut.
Ces gestionnaires se chargent de renvoyer les réponses JSON par défaut lorsque vous `raise` une `HTTPException` et lorsque la requête contient des données invalides.
Vous pouvez remplacer ces gestionnaires d'exception par les vôtres.
### Remplacer les exceptions de validation de la requête { #override-request-validation-exceptions }
Lorsqu'une requête contient des données invalides, **FastAPI** lève en interne une `RequestValidationError`.
Et il inclut également un gestionnaire d'exception par défaut pour cela.
Pour la remplacer, importez `RequestValidationError` et utilisez-la avec `@app.exception_handler(RequestValidationError)` pour décorer le gestionnaire d'exception.
Le gestionnaire d'exception recevra une `Request` et l'exception.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
À présent, si vous allez sur `/items/foo`, au lieu d'obtenir l'erreur JSON par défaut suivante :
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
vous obtiendrez une version texte, avec :
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Remplacer le gestionnaire d'erreurs `HTTPException` { #override-the-httpexception-error-handler }
De la même manière, vous pouvez remplacer le gestionnaire de `HTTPException`.
Par exemple, vous pourriez vouloir renvoyer une réponse en texte brut au lieu de JSON pour ces erreurs :
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.responses import PlainTextResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` par simple commodité pour vous, le développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
///
/// warning | Alertes
Gardez à l'esprit que la `RequestValidationError` contient l'information du nom de fichier et de la ligne où l'erreur de validation se produit, afin que vous puissiez l'afficher dans vos journaux avec les informations pertinentes si vous le souhaitez.
Mais cela signifie que si vous vous contentez de la convertir en chaîne et de renvoyer cette information directement, vous pourriez divulguer un peu d'information sur votre système. C'est pourquoi, ici, le code extrait et affiche chaque erreur indépendamment.
///
### Utiliser le corps de `RequestValidationError` { #use-the-requestvalidationerror-body }
La `RequestValidationError` contient le `body` qu'elle a reçu avec des données invalides.
Vous pouvez l'utiliser pendant le développement de votre application pour journaliser le corps et le déboguer, le renvoyer à l'utilisateur, etc.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Essayez maintenant d'envoyer un élément invalide comme :
```JSON
{
"title": "towel",
"size": "XL"
}
```
Vous recevrez une réponse vous indiquant que les données sont invalides et contenant le corps reçu :
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### `HTTPException` de FastAPI vs `HTTPException` de Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** a sa propre `HTTPException`.
Et la classe d'erreur `HTTPException` de **FastAPI** hérite de la classe d'erreur `HTTPException` de Starlette.
La seule différence est que la `HTTPException` de **FastAPI** accepte toute donnée sérialisable en JSON pour le champ `detail`, tandis que la `HTTPException` de Starlette n'accepte que des chaînes.
Ainsi, vous pouvez continuer à lever la `HTTPException` de **FastAPI** normalement dans votre code.
Mais lorsque vous enregistrez un gestionnaire d'exception, vous devez l'enregistrer pour la `HTTPException` de Starlette.
De cette façon, si une partie du code interne de Starlette, ou une extension ou un plug-in Starlette, lève une `HTTPException` de Starlette, votre gestionnaire pourra l'intercepter et la traiter.
Dans cet exemple, afin de pouvoir avoir les deux `HTTPException` dans le même code, les exceptions de Starlette sont renommées en `StarletteHTTPException` :
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Réutiliser les gestionnaires d'exception de **FastAPI** { #reuse-fastapis-exception-handlers }
Si vous souhaitez utiliser l'exception avec les mêmes gestionnaires d'exception par défaut de **FastAPI**, vous pouvez importer et réutiliser les gestionnaires d'exception par défaut depuis `fastapi.exception_handlers` :
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
Dans cet exemple, vous vous contentez d'afficher l'erreur avec un message très expressif, mais vous voyez l'idée. Vous pouvez utiliser l'exception puis simplement réutiliser les gestionnaires d'exception par défaut.

View File

@ -0,0 +1,72 @@
# Modèles de paramètres d'en-tête { #header-parameter-models }
Si vous avez un groupe de **paramètres d'en-tête** liés, vous pouvez créer un **modèle Pydantic** pour les déclarer.
Cela vous permet de **réutiliser le modèle** à **plusieurs endroits** et aussi de déclarer des validations et des métadonnées pour tous les paramètres en une seule fois. 😎
/// note | Remarque
Cela est pris en charge depuis la version `0.115.0` de FastAPI. 🤓
///
## Paramètres d'en-tête avec un modèle Pydantic { #header-parameters-with-a-pydantic-model }
Déclarez les **paramètres d'en-tête** dont vous avez besoin dans un **modèle Pydantic**, puis déclarez le paramètre comme `Header` :
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** extrait les données de **chaque champ** depuis les **en-têtes** de la requête et vous fournit le modèle Pydantic que vous avez défini.
## Consulter la documentation { #check-the-docs }
Vous pouvez voir les en-têtes requis dans l'interface de la documentation à `/docs` :
<div class="screenshot">
<img src="/img/tutorial/header-param-models/image01.png">
</div>
## Interdire les en-têtes supplémentaires { #forbid-extra-headers }
Dans certains cas d'utilisation particuliers (probablement pas très courants), vous pourriez vouloir **restreindre** les en-têtes que vous souhaitez recevoir.
Vous pouvez utiliser la configuration du modèle de Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Si un client essaie d'envoyer des **en-têtes supplémentaires**, il recevra une **réponse d'erreur**.
Par exemple, si le client essaie d'envoyer un en-tête `tool` avec la valeur `plumbus`, il recevra une **réponse d'erreur** lui indiquant que le paramètre d'en-tête `tool` n'est pas autorisé :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Désactiver convert_underscores { #disable-convert-underscores }
Comme pour les paramètres d'en-tête classiques, lorsque vous avez des caractères de soulignement dans les noms de paramètres, ils sont **automatiquement convertis en tirets**.
Par exemple, si vous avez un paramètre d'en-tête `save_data` dans le code, l'en-tête HTTP attendu sera `save-data`, et il apparaîtra ainsi dans la documentation.
Si, pour une raison quelconque, vous devez désactiver cette conversion automatique, vous pouvez aussi le faire pour les modèles Pydantic de paramètres d'en-tête.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Alertes
Avant de définir `convert_underscores` à `False`, gardez à l'esprit que certains proxys et serveurs HTTP interdisent l'utilisation d'en-têtes contenant des underscores.
///
## Résumé { #summary }
Vous pouvez utiliser des **modèles Pydantic** pour déclarer des **en-têtes** dans **FastAPI**. 😎

View File

@ -0,0 +1,91 @@
# Paramètres d'en-tête { #header-parameters }
Vous pouvez définir des paramètres `Header` de la même manière que vous définissez des paramètres `Query`, `Path` et `Cookie`.
## Importer `Header` { #import-header }
Commencez par importer `Header` :
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Déclarer des paramètres `Header` { #declare-header-parameters }
Déclarez ensuite les paramètres d'en-tête en utilisant la même structure qu'avec `Path`, `Query` et `Cookie`.
Vous pouvez définir la valeur par défaut ainsi que tous les paramètres supplémentaires de validation ou d'annotation :
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Détails techniques
`Header` est une classe « sœur » de `Path`, `Query` et `Cookie`. Elle hérite également de la même classe commune `Param`.
Mais rappelez-vous que lorsque vous importez `Query`, `Path`, `Header` et d'autres depuis `fastapi`, ce sont en réalité des fonctions qui renvoient des classes spéciales.
///
/// info
Pour déclarer des en-têtes, vous devez utiliser `Header`, sinon les paramètres seraient interprétés comme des paramètres de requête.
///
## Conversion automatique { #automatic-conversion }
`Header` offre un peu de fonctionnalité supplémentaire par rapport à `Path`, `Query` et `Cookie`.
La plupart des en-têtes standards sont séparés par un caractère « trait d'union », également appelé « signe moins » (`-`).
Mais une variable comme `user-agent` est invalide en Python.
Ainsi, par défaut, `Header` convertit les caractères des noms de paramètres du tiret bas (`_`) en trait d'union (`-`) pour extraire et documenter les en-têtes.
De plus, les en-têtes HTTP ne sont pas sensibles à la casse, vous pouvez donc les déclarer avec le style Python standard (aussi appelé « snake_case »).
Vous pouvez donc utiliser `user_agent` comme vous le feriez normalement dans du code Python, au lieu d'avoir à mettre des majuscules aux premières lettres comme `User_Agent` ou quelque chose de similaire.
Si, pour une raison quelconque, vous devez désactiver la conversion automatique des traits bas en traits d'union, définissez le paramètre `convert_underscores` de `Header` sur `False` :
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Alertes
Avant de définir `convert_underscores` sur `False`, gardez à l'esprit que certains proxies et serveurs HTTP interdisent l'utilisation d'en-têtes contenant des traits bas.
///
## Gérer les en-têtes dupliqués { #duplicate-headers }
Il est possible de recevoir des en-têtes en double. Autrement dit, le même en-tête avec plusieurs valeurs.
Vous pouvez définir ces cas à l'aide d'une liste dans la déclaration de type.
Vous recevrez toutes les valeurs de l'en-tête dupliqué sous forme de `list` Python.
Par exemple, pour déclarer un en-tête `X-Token` qui peut apparaître plusieurs fois, vous pouvez écrire :
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Si vous communiquez avec ce *chemin d'accès* en envoyant deux en-têtes HTTP comme :
```
X-Token: foo
X-Token: bar
```
La réponse ressemblerait à ceci :
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Récapitulatif { #recap }
Déclarez les en-têtes avec `Header`, en suivant le même modèle que pour `Query`, `Path` et `Cookie`.
Et ne vous souciez pas des traits bas dans vos variables, **FastAPI** s'occupe de les convertir.

View File

@ -0,0 +1,120 @@
# Métadonnées et URL des documents { #metadata-and-docs-urls }
Vous pouvez personnaliser plusieurs configurations de métadonnées dans votre application **FastAPI**.
## Métadonnées pour l'API { #metadata-for-api }
Vous pouvez définir les champs suivants qui sont utilisés dans la spécification OpenAPI et les interfaces utilisateur de documentation automatique de lAPI :
| Paramètre | Type | Description |
|------------|------|-------------|
| `title` | `str` | Le titre de lAPI. |
| `summary` | `str` | Un court résumé de lAPI. <small>Disponible depuis OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
| `description` | `str` | Une brève description de lAPI. Elle peut utiliser Markdown. |
| `version` | `string` | La version de lAPI. Cest la version de votre propre application, pas dOpenAPI. Par exemple `2.5.0`. |
| `terms_of_service` | `str` | Une URL vers les Conditions dutilisation de lAPI. Le cas échéant, il doit sagir dune URL. |
| `contact` | `dict` | Les informations de contact pour lAPI exposée. Cela peut contenir plusieurs champs. <details><summary>champs de <code>contact</code></summary><table><thead><tr><th>Paramètre</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>Le nom identifiant de la personne/organisation de contact.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>LURL pointant vers les informations de contact. DOIT être au format dune URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>Ladresse e-mail de la personne/organisation de contact. DOIT être au format dune adresse e-mail.</td></tr></tbody></table></details> |
| `license_info` | `dict` | Les informations de licence pour lAPI exposée. Cela peut contenir plusieurs champs. <details><summary>champs de <code>license_info</code></summary><table><thead><tr><th>Paramètre</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>OBLIGATOIRE</strong> (si un <code>license_info</code> est défini). Le nom de la licence utilisée pour lAPI.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Une expression de licence <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> pour lAPI. Le champ <code>identifier</code> est mutuellement exclusif du champ <code>url</code>. <small>Disponible depuis OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>Une URL vers la licence utilisée pour lAPI. DOIT être au format dune URL.</td></tr></tbody></table></details> |
Vous pouvez les définir comme suit :
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Astuce
Vous pouvez écrire du Markdown dans le champ `description` et il sera rendu dans la sortie.
///
Avec cette configuration, les documents API automatiques ressembleraient à :
<img src="/img/tutorial/metadata/image01.png">
## Identifiant de licence { #license-identifier }
Depuis OpenAPI 3.1.0 et FastAPI 0.99.0, vous pouvez également définir `license_info` avec un `identifier` au lieu dune `url`.
Par exemple :
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Métadonnées pour les tags { #metadata-for-tags }
Vous pouvez également ajouter des métadonnées supplémentaires pour les différents tags utilisés pour regrouper vos chemins d'accès avec le paramètre `openapi_tags`.
Il prend une liste contenant un dictionnaire pour chaque tag.
Chaque dictionnaire peut contenir :
* `name` (**requis**) : un `str` avec le même nom de tag que vous utilisez dans le paramètre `tags` de vos *chemins d'accès* et `APIRouter`s.
* `description` : un `str` avec une courte description pour le tag. Il peut contenir du Markdown et sera affiché dans linterface utilisateur de la documentation.
* `externalDocs` : un `dict` décrivant une documentation externe avec :
* `description` : un `str` avec une courte description pour la documentation externe.
* `url` (**requis**) : un `str` avec lURL de la documentation externe.
### Créer des métadonnées pour les tags { #create-metadata-for-tags }
Essayons cela avec un exemple de tags pour `users` et `items`.
Créez des métadonnées pour vos tags et transmettez-les au paramètre `openapi_tags` :
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Notez que vous pouvez utiliser Markdown à lintérieur des descriptions, par exemple « login » sera affiché en gras (**login**) et « fancy » sera affiché en italique (_fancy_).
/// tip | Astuce
Vous navez pas à ajouter des métadonnées pour tous les tags que vous utilisez.
///
### Utiliser vos tags { #use-your-tags }
Utilisez le paramètre `tags` avec vos *chemins d'accès* (et `APIRouter`s) pour les affecter à différents tags :
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info
En savoir plus sur les tags dans [Configuration de chemins d'accès](path-operation-configuration.md#tags){.internal-link target=_blank}.
///
### Consultez les documents { #check-the-docs }
Désormais, si vous consultez les documents, ils afficheront toutes les métadonnées supplémentaires :
<img src="/img/tutorial/metadata/image02.png">
### Définir lordre des tags { #order-of-tags }
Lordre de chaque dictionnaire de métadonnées de tag définit également lordre affiché dans linterface utilisateur de la documentation.
Par exemple, même si `users` viendrait après `items` par ordre alphabétique, il est affiché avant, car nous avons ajouté ses métadonnées comme premier dictionnaire de la liste.
## URL OpenAPI { #openapi-url }
Par défaut, le schéma OpenAPI est servi à `/openapi.json`.
Mais vous pouvez le configurer avec le paramètre `openapi_url`.
Par exemple, pour quil soit servi à `/api/v1/openapi.json` :
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Si vous souhaitez désactiver complètement le schéma OpenAPI, vous pouvez définir `openapi_url=None`, cela désactivera également les interfaces utilisateur de la documentation qui lutilisent.
## URL des documents { #docs-urls }
Vous pouvez configurer les deux interfaces utilisateur de documentation incluses :
* **Swagger UI** : servie à `/docs`.
* Vous pouvez définir son URL avec le paramètre `docs_url`.
* Vous pouvez la désactiver en définissant `docs_url=None`.
* **ReDoc** : servie à `/redoc`.
* Vous pouvez définir son URL avec le paramètre `redoc_url`.
* Vous pouvez la désactiver en définissant `redoc_url=None`.
Par exemple, pour que Swagger UI soit servi à `/documentation` et désactiver ReDoc :
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}

View File

@ -0,0 +1,95 @@
# Middleware { #middleware }
Vous pouvez ajouter des middlewares aux applications **FastAPI**.
Un « middleware » est une fonction qui agit sur chaque **requête** avant quelle ne soit traitée par un *chemin d'accès* spécifique. Et aussi sur chaque **réponse** avant son renvoi.
* Il intercepte chaque **requête** qui parvient à votre application.
* Il peut alors faire quelque chose avec cette **requête** ou exécuter tout code nécessaire.
* Ensuite, il transmet la **requête** pour quelle soit traitée par le reste de lapplication (par un *chemin d'accès*).
* Puis il récupère la **réponse** générée par lapplication (par un *chemin d'accès*).
* Il peut faire quelque chose avec cette **réponse** ou exécuter tout code nécessaire.
* Enfin, il renvoie la **réponse**.
/// note | Détails techniques
Si vous avez des dépendances avec `yield`, le code de sortie sexécutera après le middleware.
Sil y avait des tâches darrière-plan (présentées dans la section [Tâches darrière-plan](background-tasks.md){.internal-link target=_blank}, que vous verrez plus tard), elles sexécuteront après tous les middlewares.
///
## Créer un middleware { #create-a-middleware }
Pour créer un middleware, utilisez le décorateur `@app.middleware("http")` au-dessus dune fonction.
La fonction de middleware reçoit:
* La `request`.
* Une fonction `call_next` qui recevra la `request` en paramètre.
* Cette fonction transmettra la `request` au *chemin d'accès* correspondant.
* Puis elle renverra la `response` générée par le *chemin d'accès* correspondant.
* Vous pouvez ensuite modifier la `response` avant de la renvoyer.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | Astuce
Gardez à lesprit que des en-têtes propriétaires personnalisés peuvent être ajoutés <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">en utilisant le préfixe `X-`</a>.
Mais si vous avez des en-têtes personnalisés que vous voulez rendre visibles pour un client dans un navigateur, vous devez les ajouter à votre configuration CORS ([CORS (Partage des ressources entre origines)](cors.md){.internal-link target=_blank}) en utilisant le paramètre `expose_headers` documenté dans <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">la documentation CORS de Starlette</a>.
///
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.requests import Request`.
**FastAPI** le fournit pour votre confort de développeur. Mais cela provient directement de Starlette.
///
### Avant et après la `response` { #before-and-after-the-response }
Vous pouvez ajouter du code à exécuter avec la `request`, avant que tout *chemin d'accès* ne la reçoive.
Et aussi après que la `response` a été générée, avant de la renvoyer.
Par exemple, vous pourriez ajouter un en-tête personnalisé `X-Process-Time` contenant le temps en secondes nécessaire pour traiter la requête et générer une réponse:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | Astuce
Ici, nous utilisons <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> au lieu de `time.time()` car cela peut être plus précis pour ces cas dusage. 🤓
///
## Ordre dexécution de plusieurs middlewares { #multiple-middleware-execution-order }
Quand vous ajoutez plusieurs middlewares en utilisant soit le décorateur `@app.middleware()`, soit la méthode `app.add_middleware()`, chaque nouveau middleware enveloppe lapplication, formant une pile. Le dernier middleware ajouté est le plus externe, et le premier est le plus interne.
Sur le chemin de la requête, le plus externe sexécute en premier.
Sur le chemin de la réponse, il sexécute en dernier.
Par exemple:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Cela aboutit à lordre dexécution suivant:
* **Requête** : MiddlewareB → MiddlewareA → route
* **Réponse** : route → MiddlewareA → MiddlewareB
Ce comportement dempilement garantit que les middlewares sexécutent dans un ordre prévisible et contrôlable.
## Autres middlewares { #other-middlewares }
Vous pouvez en lire davantage sur dautres middlewares dans le [Guide de lutilisateur avancé : Middleware avancé](../advanced/middleware.md){.internal-link target=_blank}.
Vous verrez comment gérer <abbr title="Cross-Origin Resource Sharing - Partage des ressources entre origines">CORS</abbr> avec un middleware dans la section suivante.

View File

@ -0,0 +1,107 @@
# Configurer les chemins d'accès { #path-operation-configuration }
Vous pouvez passer plusieurs paramètres à votre *décorateur de chemin d'accès* pour le configurer.
/// warning | Alertes
Notez que ces paramètres sont passés directement au *décorateur de chemin d'accès*, et non à votre *fonction de chemin d'accès*.
///
## Définir le code d'état de la réponse { #response-status-code }
Vous pouvez définir le `status_code` (HTTP) à utiliser dans la réponse de votre *chemin d'accès*.
Vous pouvez passer directement le code `int`, comme `404`.
Mais si vous ne vous souvenez pas à quoi correspond chaque code numérique, vous pouvez utiliser les constantes abrégées dans `status` :
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Ce code d'état sera utilisé dans la réponse et ajouté au schéma OpenAPI.
/// note | Détails techniques
Vous pouvez également utiliser `from starlette import status`.
**FastAPI** fournit le même `starlette.status` sous le nom `fastapi.status` pour votre commodité, en tant que développeur. Mais cela provient directement de Starlette.
///
## Ajouter des tags { #tags }
Vous pouvez ajouter des tags à votre *chemin d'accès*, en passant le paramètre `tags` avec une `list` de `str` (généralement un seul `str`) :
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Ils seront ajoutés au schéma OpenAPI et utilisés par les interfaces de documentation automatiques :
<img src="/img/tutorial/path-operation-configuration/image01.png">
### Utiliser des tags avec Enum { #tags-with-enums }
Si vous avez une grande application, vous pourriez finir par accumuler **plusieurs tags**, et vous voudrez vous assurer d'utiliser toujours le **même tag** pour les *chemins d'accès* associés.
Dans ces cas, il peut être judicieux de stocker les tags dans un `Enum`.
**FastAPI** le prend en charge de la même manière qu'avec des chaînes simples :
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Ajouter un résumé et une description { #summary-and-description }
Vous pouvez ajouter un `summary` et une `description` :
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Utiliser la description depuis la docstring { #description-from-docstring }
Comme les descriptions ont tendance à être longues et à couvrir plusieurs lignes, vous pouvez déclarer la description du *chemin d'accès* dans la <dfn title="une chaîne multilignes comme première expression à l'intérieur d'une fonction (non assignée à une variable) utilisée pour la documentation">docstring</dfn> de la fonction et **FastAPI** la lira à partir de là.
Vous pouvez écrire <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> dans la docstring, il sera interprété et affiché correctement (en tenant compte de l'indentation de la docstring).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Elle sera utilisée dans les documents interactifs :
<img src="/img/tutorial/path-operation-configuration/image02.png">
## Définir la description de la réponse { #response-description }
Vous pouvez spécifier la description de la réponse avec le paramètre `response_description` :
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info
Notez que `response_description` se réfère spécifiquement à la réponse, tandis que `description` se réfère au *chemin d'accès* en général.
///
/// check | Vérifications
OpenAPI spécifie que chaque *chemin d'accès* requiert une description de réponse.
Donc, si vous n'en fournissez pas, **FastAPI** en générera automatiquement une « Réponse réussie ».
///
<img src="/img/tutorial/path-operation-configuration/image03.png">
## Déprécier un *chemin d'accès* { #deprecate-a-path-operation }
Si vous devez marquer un *chemin d'accès* comme <dfn title="obsolète, il est recommandé de ne pas l'utiliser">déprécié</dfn>, sans pour autant le supprimer, passez le paramètre `deprecated` :
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Il sera clairement marqué comme déprécié dans les documents interactifs :
<img src="/img/tutorial/path-operation-configuration/image04.png">
Voyez à quoi ressemblent les *chemins d'accès* dépréciés et non dépréciés :
<img src="/img/tutorial/path-operation-configuration/image05.png">
## Récapitulatif { #recap }
Vous pouvez facilement configurer et ajouter des métadonnées à vos *chemins d'accès* en passant des paramètres aux *décorateurs de chemin d'accès*.

View File

@ -54,11 +54,11 @@ Cela n'a pas d'importance pour **FastAPI**. Il détectera les paramètres par le
Ainsi, vous pouvez déclarer votre fonction comme suit :
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Mais gardez à l'esprit que si vous utilisez `Annotated`, vous n'aurez pas ce problème, cela n'aura pas d'importance car vous n'utilisez pas les valeurs par défaut des paramètres de fonction pour `Query()` ou `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Ordonner les paramètres comme vous le souhaitez, astuces { #order-the-parameters-as-you-need-tricks }
@ -81,15 +81,15 @@ Si vous voulez :
Passez `*`, comme premier paramètre de la fonction.
Python ne fera rien avec ce `*`, mais il saura que tous les paramètres suivants doivent être appelés comme arguments "mots-clés" (paires clé-valeur), également connus sous le nom de <abbr title="De : K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Même s'ils n'ont pas de valeur par défaut.
Python ne fera rien avec ce `*`, mais il saura que tous les paramètres suivants doivent être appelés comme arguments « mots-clés » (paires clé-valeur), également connus sous le nom de <abbr title="De : K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Même s'ils n'ont pas de valeur par défaut.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Mieux avec `Annotated` { #better-with-annotated }
Gardez à l'esprit que si vous utilisez `Annotated`, comme vous n'utilisez pas les valeurs par défaut des paramètres de fonction, vous n'aurez pas ce problème, et vous n'aurez probablement pas besoin d'utiliser `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Validations numériques : supérieur ou égal { #number-validations-greater-than-or-equal }
@ -97,7 +97,7 @@ Avec `Query` et `Path` (et d'autres que vous verrez plus tard) vous pouvez décl
Ici, avec `ge=1`, `item_id` devra être un nombre entier « `g`reater than or `e`qual » à `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Validations numériques : supérieur et inférieur ou égal { #number-validations-greater-than-and-less-than-or-equal }
@ -106,7 +106,7 @@ La même chose s'applique pour :
* `gt` : `g`reater `t`han
* `le` : `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Validations numériques : flottants, supérieur et inférieur { #number-validations-floats-greater-than-and-less-than }
@ -118,7 +118,7 @@ Ainsi, `0.5` serait une valeur valide. Mais `0.0` ou `0` ne le serait pas.
Et la même chose pour <abbr title="less than"><code>lt</code></abbr>.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Pour résumer { #recap }

View File

@ -2,7 +2,7 @@
Vous pouvez déclarer des « paramètres » ou « variables » de chemin avec la même syntaxe utilisée par les chaînes de format Python :
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
La valeur du paramètre de chemin `item_id` sera transmise à votre fonction dans l'argument `item_id`.
@ -16,7 +16,7 @@ Donc, si vous exécutez cet exemple et allez sur <a href="http://127.0.0.1:8000/
Vous pouvez déclarer le type d'un paramètre de chemin dans la fonction, en utilisant les annotations de type Python standard :
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
Ici, `item_id` est déclaré comme `int`.
@ -26,7 +26,7 @@ Cela vous apporte la prise en charge par l'éditeur dans votre fonction, avec v
///
## <abbr title="également appelé : sérialisation, parsing, marshalling">Conversion</abbr> de données { #data-conversion }
## <dfn title="également appelé : sérialisation, parsing, marshalling">Conversion</dfn> de données { #data-conversion }
Si vous exécutez cet exemple et ouvrez votre navigateur sur <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, vous verrez comme réponse :
@ -38,7 +38,7 @@ Si vous exécutez cet exemple et ouvrez votre navigateur sur <a href="http://127
Remarquez que la valeur reçue par votre fonction (et renvoyée) est `3`, en tant qu'entier (`int`) Python, pas la chaîne de caractères « 3 ».
Ainsi, avec cette déclaration de type, **FastAPI** vous fournit automatiquement le <abbr title="conversion de la chaîne de caractères provenant d'une requête HTTP en données Python">« parsing »</abbr> de la requête.
Ainsi, avec cette déclaration de type, **FastAPI** vous fournit automatiquement le <dfn title="conversion de la chaîne de caractères provenant d'une requête HTTP en données Python">« parsing »</dfn> de la requête.
///
@ -118,19 +118,19 @@ Et vous pouvez aussi avoir un chemin `/users/{user_id}` pour récupérer des don
Comme les *chemins d'accès* sont évalués dans l'ordre, vous devez vous assurer que le chemin `/users/me` est déclaré avant celui de `/users/{user_id}` :
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Sinon, le chemin `/users/{user_id}` correspondrait aussi à `/users/me`, « pensant » qu'il reçoit un paramètre `user_id` avec la valeur « me ».
De même, vous ne pouvez pas redéfinir un chemin d'accès :
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
Le premier sera toujours utilisé puisque le chemin correspond en premier.
## Valeurs prédéfinies { #predefined-values }
Si vous avez un *chemin d'accès* qui reçoit un *paramètre de chemin*, mais que vous voulez que les valeurs possibles de ce *paramètre de chemin* soient prédéfinies, vous pouvez utiliser une <abbr title="Enumeration">`Enum`</abbr> Python standard.
Si vous avez un *chemin d'accès* qui reçoit un *paramètre de chemin*, mais que vous voulez que les valeurs possibles de ce *paramètre de chemin* soient prédéfinies, vous pouvez utiliser une <abbr title="Enumeration - Énumération">`Enum`</abbr> Python standard.
### Créer une classe `Enum` { #create-an-enum-class }
@ -140,11 +140,11 @@ En héritant de `str`, la documentation de l'API saura que les valeurs doivent
Créez ensuite des attributs de classe avec des valeurs fixes, qui seront les valeurs valides disponibles :
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Astuce
Si vous vous demandez, « AlexNet », « ResNet » et « LeNet » sont juste des noms de <abbr title="Techniquement, architectures de modèles de Deep Learning">modèles</abbr> de Machine Learning.
Si vous vous demandez, « AlexNet », « ResNet » et « LeNet » sont juste des noms de <dfn title="Techniquement, architectures de modèles de Deep Learning">modèles</dfn> de Machine Learning.
///
@ -152,7 +152,7 @@ Si vous vous demandez, « AlexNet », « ResNet » et « LeNet » sont juste des
Créez ensuite un *paramètre de chemin* avec une annotation de type utilisant la classe d'énumération que vous avez créée (`ModelName`) :
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Consulter la documentation { #check-the-docs }
@ -168,13 +168,13 @@ La valeur du *paramètre de chemin* sera un *membre d'énumération*.
Vous pouvez le comparer avec le *membre d'énumération* dans votre enum `ModelName` :
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Obtenir la *valeur de l'énumération* { #get-the-enumeration-value }
Vous pouvez obtenir la valeur réelle (une `str` dans ce cas) avec `model_name.value`, ou en général, `votre_membre_d_enum.value` :
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Astuce
@ -188,7 +188,7 @@ Vous pouvez retourner des *membres d'énumération* depuis votre *chemin d'accè
Ils seront convertis vers leurs valeurs correspondantes (des chaînes de caractères ici) avant d'être renvoyés au client :
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
Dans votre client, vous recevrez une réponse JSON comme :
@ -227,7 +227,7 @@ Dans ce cas, le nom du paramètre est `file_path`, et la dernière partie, `:pat
Vous pouvez donc l'utiliser ainsi :
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Astuce
@ -242,7 +242,7 @@ Dans ce cas, l'URL serait : `/files//home/johndoe/myfile.txt`, avec un double sl
Avec **FastAPI**, en utilisant des déclarations de type Python courtes, intuitives et standard, vous obtenez :
* Support de l'éditeur : vérifications d'erreurs, autocomplétion, etc.
* Données « <abbr title="conversion de la chaîne de caractères provenant d'une requête HTTP en données Python">parsing</abbr> »
* Données « <dfn title="conversion de la chaîne de caractères provenant d'une requête HTTP en données Python">parsing</dfn> »
* Validation de données
* Annotations d'API et documentation automatique

View File

@ -0,0 +1,68 @@
# Modèles de paramètres de requête { #query-parameter-models }
Si vous avez un groupe de paramètres de requête liés, vous pouvez créer un modèle Pydantic pour les déclarer.
Cela vous permet de réutiliser le modèle à plusieurs endroits et aussi de déclarer des validations et des métadonnées pour tous les paramètres en une seule fois. 😎
/// note | Remarque
Pris en charge depuis FastAPI version `0.115.0`. 🤓
///
## Déclarer des paramètres de requête avec un modèle Pydantic { #query-parameters-with-a-pydantic-model }
Déclarez les paramètres de requête dont vous avez besoin dans un modèle Pydantic, puis déclarez le paramètre en tant que `Query` :
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
FastAPI extrait les données pour chaque champ à partir des paramètres de requête de la requête et vous fournit le modèle Pydantic que vous avez défini.
## Consulter les documents { #check-the-docs }
Vous pouvez voir les paramètres de requête dans l'interface des documents à `/docs` :
<div class="screenshot">
<img src="/img/tutorial/query-param-models/image01.png">
</div>
## Interdire des paramètres de requête supplémentaires { #forbid-extra-query-parameters }
Dans certains cas d'utilisation particuliers (probablement peu courants), vous pouvez vouloir restreindre les paramètres de requête que vous souhaitez recevoir.
Vous pouvez utiliser la configuration du modèle Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Si un client tente d'envoyer des données supplémentaires dans les paramètres de requête, il recevra une réponse d'erreur.
Par exemple, si le client tente d'envoyer un paramètre de requête `tool` avec la valeur `plumbus`, comme :
```http
https://example.com/items/?limit=10&tool=plumbus
```
Il recevra une réponse d'erreur lui indiquant que le paramètre de requête `tool` n'est pas autorisé :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Résumé { #summary }
Vous pouvez utiliser des modèles Pydantic pour déclarer des paramètres de requête dans FastAPI. 😎
/// tip | Astuce
Alerte spoiler : vous pouvez aussi utiliser des modèles Pydantic pour déclarer des cookies et des en-têtes, mais vous lirez cela plus tard dans le tutoriel. 🤫
///

View File

@ -47,40 +47,16 @@ Cest le moment de lutiliser avec FastAPI. 🚀
Nous avions cette annotation de type :
//// tab | Python 3.10+
```Python
q: str | None = None
```
////
//// tab | Python 3.9+
```Python
q: Union[str, None] = None
```
////
Ce que nous allons faire, cest lenglober avec `Annotated`, de sorte que cela devienne :
//// tab | Python 3.10+
```Python
q: Annotated[str | None] = None
```
////
//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
```
////
Les deux versions signifient la même chose, `q` est un paramètre qui peut être une `str` ou `None`, et par défaut, cest `None`.
Passons maintenant aux choses amusantes. 🎉
@ -109,7 +85,7 @@ FastAPI va maintenant :
## Alternative (ancienne) : `Query` comme valeur par défaut { #alternative-old-query-as-the-default-value }
Les versions précédentes de FastAPI (avant <abbr title="avant 2023-03">0.95.0</abbr>) exigeaient dutiliser `Query` comme valeur par défaut de votre paramètre, au lieu de le mettre dans `Annotated`. Il y a de fortes chances que vous voyiez du code qui lutilise encore, je vais donc vous lexpliquer.
Les versions précédentes de FastAPI (avant <dfn title="avant 2023-03">0.95.0</dfn>) exigeaient dutiliser `Query` comme valeur par défaut de votre paramètre, au lieu de le mettre dans `Annotated`. Il y a de fortes chances que vous voyiez du code qui lutilise encore, je vais donc vous lexpliquer.
/// tip | Astuce
@ -191,7 +167,7 @@ Vous pouvez également ajouter un paramètre `min_length` :
## Ajouter des expressions régulières { #add-regular-expressions }
Vous pouvez définir un `pattern` d<abbr title="Une expression régulière, regex ou regexp, est une suite de caractères qui définit un motif de recherche pour les chaînes de caractères.">expression régulière</abbr> auquel le paramètre doit correspondre :
Vous pouvez définir un `pattern` d<dfn title="Une expression régulière, regex ou regexp, est une suite de caractères qui définit un motif de recherche pour les chaînes de caractères.">expression régulière</dfn> auquel le paramètre doit correspondre :
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
@ -211,7 +187,7 @@ Vous pouvez, bien sûr, utiliser des valeurs par défaut autres que `None`.
Disons que vous voulez déclarer le paramètre de requête `q` avec un `min_length` de `3`, et avec une valeur par défaut de « fixedquery » :
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Remarque
@ -241,7 +217,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
Donc, lorsque vous avez besoin de déclarer une valeur comme requise tout en utilisant `Query`, vous pouvez simplement ne pas déclarer de valeur par défaut :
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Requis, peut valoir `None` { #required-can-be-none }
@ -292,7 +268,7 @@ Linterface de documentation interactive de lAPI sera mise à jour en cons
Vous pouvez également définir une `list` de valeurs par défaut si aucune nest fournie :
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Si vous allez à :
@ -315,7 +291,7 @@ la valeur par défaut de `q` sera : `["foo", "bar"]` et votre réponse sera :
Vous pouvez aussi utiliser `list` directement au lieu de `list[str]` :
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Remarque
@ -371,7 +347,7 @@ Vous pouvez alors déclarer un `alias`, et cet alias sera utilisé pour trouver
Disons que vous naimez plus ce paramètre.
Vous devez le laisser là quelque temps car des clients lutilisent, mais vous voulez que les documents laffichent clairement comme <abbr title="obsolète, recommandé de ne pas lutiliser">déprécié</abbr>.
Vous devez le laisser là quelque temps car des clients lutilisent, mais vous voulez que les documents laffichent clairement comme <dfn title="obsolète, il est recommandé de ne pas lutiliser">déprécié</dfn>.
Passez alors le paramètre `deprecated=True` à `Query` :
@ -401,7 +377,7 @@ Pydantic a aussi <a href="https://docs.pydantic.dev/latest/concepts/validators/#
///
Par exemple, ce validateur personnalisé vérifie que lID ditem commence par `isbn-` pour un numéro de livre <abbr title="International Standard Book Number - Numéro international normalisé du livre">ISBN</abbr> ou par `imdb-` pour un ID dURL de film <abbr title="IMDB (Internet Movie Database) est un site web contenant des informations sur les films">IMDB</abbr> :
Par exemple, ce validateur personnalisé vérifie que lID ditem commence par `isbn-` pour un numéro de livre <abbr title="International Standard Book Number - Numéro international normalisé du livre">ISBN</abbr> ou par `imdb-` pour un ID dURL de film <abbr title="Internet Movie Database - Base de données de films sur Internet: un site web contenant des informations sur les films">IMDB</abbr> :
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
@ -435,7 +411,7 @@ Avez-vous remarqué ? Une chaîne utilisant `value.startswith()` peut prendre un
#### Un élément aléatoire { #a-random-item }
Avec `data.items()` nous obtenons un <abbr title="Quelque chose que lon peut itérer avec une boucle for, comme une liste, un set, etc.">objet itérable</abbr> avec des tuples contenant la clé et la valeur pour chaque élément du dictionnaire.
Avec `data.items()` nous obtenons un <dfn title="Quelque chose que lon peut itérer avec une boucle for, comme une liste, un set, etc.">objet itérable</dfn> avec des tuples contenant la clé et la valeur pour chaque élément du dictionnaire.
Nous convertissons cet objet itérable en une `list` propre avec `list(data.items())`.

View File

@ -2,7 +2,7 @@
Quand vous déclarez d'autres paramètres de fonction qui ne font pas partie des paramètres de chemin, ils sont automatiquement interprétés comme des paramètres de « query ».
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
La query est l'ensemble des paires clé-valeur placées après le `?` dans une URL, séparées par des caractères `&`.
@ -24,7 +24,7 @@ Mais lorsque vous les déclarez avec des types Python (dans l'exemple ci-dessus,
Tous les mêmes processus qui s'appliquaient aux paramètres de chemin s'appliquent aussi aux paramètres de requête :
* Prise en charge de l'éditeur (évidemment)
* <abbr title="conversion de la chaîne provenant d'une requête HTTP en données Python">« parsing »</abbr> des données
* <dfn title="conversion de la chaîne provenant d'une requête HTTP en données Python">« parsing »</dfn> des données
* Validation des données
* Documentation automatique
@ -67,7 +67,7 @@ Dans ce cas, le paramètre de fonction `q` sera optionnel et vaudra `None` par d
/// check | Vérifications
Notez également que FastAPI est suffisamment intelligent pour remarquer que le paramètre de chemin `item_id` est un paramètre de chemin et que `q` ne l'est pas, c'est donc un paramètre de requête.
Notez également que **FastAPI** est suffisamment intelligent pour remarquer que le paramètre de chemin `item_id` est un paramètre de chemin et que `q` ne l'est pas, c'est donc un paramètre de requête.
///
@ -127,7 +127,7 @@ Si vous ne voulez pas leur donner de valeur spécifique mais simplement les rend
Mais si vous voulez rendre un paramètre de requête obligatoire, vous pouvez simplement ne déclarer aucune valeur par défaut :
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Ici, le paramètre de requête `needy` est un paramètre de requête requis de type `str`.

View File

@ -0,0 +1,176 @@
# Envoyer des fichiers { #request-files }
Vous pouvez définir des fichiers à téléverser par le client en utilisant `File`.
/// info
Pour recevoir des fichiers téléversés, installez d'abord <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, de l'activer, puis d'installer le paquet, par exemple :
```console
$ pip install python-multipart
```
C'est parce que les fichiers téléversés sont envoyés en « données de formulaire ».
///
## Importer `File` { #import-file }
Importez `File` et `UploadFile` depuis `fastapi` :
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Définir des paramètres `File` { #define-file-parameters }
Créez des paramètres de fichier de la même manière que pour `Body` ou `Form` :
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info
`File` est une classe qui hérite directement de `Form`.
Mais souvenez-vous que lorsque vous importez `Query`, `Path`, `File` et d'autres depuis `fastapi`, ce sont en réalité des fonctions qui renvoient des classes spéciales.
///
/// tip | Astuce
Pour déclarer des fichiers dans le corps de la requête, vous devez utiliser `File`, sinon les paramètres seraient interprétés comme des paramètres de requête ou des paramètres de corps (JSON).
///
Les fichiers seront téléversés en « données de formulaire ».
Si vous déclarez le type de votre paramètre de *fonction de chemin d'accès* comme `bytes`, **FastAPI** lira le fichier pour vous et vous recevrez le contenu sous forme de `bytes`.
Gardez à l'esprit que cela signifie que tout le contenu sera stocké en mémoire. Cela fonctionnera bien pour de petits fichiers.
Mais dans plusieurs cas, vous pourriez bénéficier de l'utilisation d'`UploadFile`.
## Paramètres de fichier avec `UploadFile` { #file-parameters-with-uploadfile }
Définissez un paramètre de fichier de type `UploadFile` :
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Utiliser `UploadFile` présente plusieurs avantages par rapport à `bytes` :
- Vous n'avez pas besoin d'utiliser `File()` comme valeur par défaut du paramètre.
- Il utilise un fichier « spooled » :
- Un fichier stocké en mémoire jusqu'à une taille maximale, puis, au-delà de cette limite, stocké sur le disque.
- Cela fonctionne donc bien pour des fichiers volumineux comme des images, des vidéos, de gros binaires, etc., sans consommer toute la mémoire.
- Vous pouvez obtenir des métadonnées à partir du fichier téléversé.
- Il offre une interface `async` de type <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a>.
- Il expose un véritable objet Python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> que vous pouvez passer directement à d'autres bibliothèques qui attendent un objet « file-like ».
### `UploadFile` { #uploadfile }
`UploadFile` a les attributs suivants :
- `filename` : une `str` contenant le nom de fichier original téléversé (par ex. `myimage.jpg`).
- `content_type` : une `str` avec le type de contenu (type MIME / type média) (par ex. `image/jpeg`).
- `file` : un <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> (un objet <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">de type fichier</a>). C'est l'objet fichier Python réel que vous pouvez passer directement à d'autres fonctions ou bibliothèques qui attendent un objet « file-like ».
`UploadFile` a les méthodes `async` suivantes. Elles appellent toutes les méthodes correspondantes du fichier sous-jacent (en utilisant le `SpooledTemporaryFile` interne).
- `write(data)` : écrit `data` (`str` ou `bytes`) dans le fichier.
- `read(size)` : lit `size` (`int`) octets/caractères du fichier.
- `seek(offset)` : se déplace à la position d'octet `offset` (`int`) dans le fichier.
- Par ex., `await myfile.seek(0)` irait au début du fichier.
- C'est particulièrement utile si vous exécutez `await myfile.read()` une fois puis devez relire le contenu.
- `close()` : ferme le fichier.
Comme toutes ces méthodes sont `async`, vous devez les « await ».
Par exemple, à l'intérieur d'une *fonction de chemin d'accès* `async`, vous pouvez obtenir le contenu avec :
```Python
contents = await myfile.read()
```
Si vous êtes dans une *fonction de chemin d'accès* `def` normale, vous pouvez accéder directement à `UploadFile.file`, par exemple :
```Python
contents = myfile.file.read()
```
/// note | Détails techniques `async`
Lorsque vous utilisez les méthodes `async`, **FastAPI** exécute les méthodes de fichier dans un pool de threads et les attend.
///
/// note | Détails techniques Starlette
L'`UploadFile` de **FastAPI** hérite directement de l'`UploadFile` de **Starlette**, mais ajoute certaines parties nécessaires pour le rendre compatible avec **Pydantic** et les autres parties de FastAPI.
///
## Qu'est-ce que les « données de formulaire » { #what-is-form-data }
La façon dont les formulaires HTML (`<form></form>`) envoient les données au serveur utilise normalement un encodage « spécial » pour ces données, différent de JSON.
**FastAPI** s'assure de lire ces données au bon endroit plutôt que depuis JSON.
/// note | Détails techniques
Les données des formulaires sont normalement encodées avec le « type de média » `application/x-www-form-urlencoded` lorsqu'elles n'incluent pas de fichiers.
Mais lorsque le formulaire inclut des fichiers, il est encodé en `multipart/form-data`. Si vous utilisez `File`, **FastAPI** saura qu'il doit récupérer les fichiers depuis la partie appropriée du corps.
Si vous souhaitez en savoir plus sur ces encodages et les champs de formulaire, consultez la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Réseau des développeurs Mozilla">MDN</abbr> Web Docs pour <code>POST</code></a>.
///
/// warning | Alertes
Vous pouvez déclarer plusieurs paramètres `File` et `Form` dans un *chemin d'accès*, mais vous ne pouvez pas également déclarer des champs `Body` que vous vous attendez à recevoir en JSON, car la requête aura le corps encodé en `multipart/form-data` au lieu de `application/json`.
Ce n'est pas une limitation de **FastAPI**, cela fait partie du protocole HTTP.
///
## Téléversement de fichier facultatif { #optional-file-upload }
Vous pouvez rendre un fichier facultatif en utilisant des annotations de type standard et en définissant une valeur par défaut à `None` :
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` avec des métadonnées supplémentaires { #uploadfile-with-additional-metadata }
Vous pouvez aussi utiliser `File()` avec `UploadFile`, par exemple pour définir des métadonnées supplémentaires :
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Téléverser plusieurs fichiers { #multiple-file-uploads }
Il est possible de téléverser plusieurs fichiers en même temps.
Ils seraient associés au même « champ de formulaire » envoyé en « données de formulaire ».
Pour cela, déclarez une `list` de `bytes` ou d'`UploadFile` :
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Vous recevrez, comme déclaré, une `list` de `bytes` ou d'`UploadFile`.
/// 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 convenance en tant que développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
///
### Téléversements multiples avec métadonnées supplémentaires { #multiple-file-uploads-with-additional-metadata }
Et de la même manière que précédemment, vous pouvez utiliser `File()` pour définir des paramètres supplémentaires, même pour `UploadFile` :
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Récapitulatif { #recap }
Utilisez `File`, `bytes` et `UploadFile` pour déclarer des fichiers à téléverser dans la requête, envoyés en « données de formulaire ».

View File

@ -0,0 +1,78 @@
# Modèles de formulaire { #form-models }
Vous pouvez utiliser des **modèles Pydantic** pour déclarer des **champs de formulaire** dans FastAPI.
/// info
Pour utiliser les formulaires, installez d'abord <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, de l'activer, puis d'installer le paquet, par exemple :
```console
$ pip install python-multipart
```
///
/// note | Remarque
Ceci est pris en charge depuis la version `0.113.0` de FastAPI. 🤓
///
## Modèles Pydantic pour les formulaires { #pydantic-models-for-forms }
Vous avez simplement besoin de déclarer un **modèle Pydantic** avec les champs que vous souhaitez recevoir comme **champs de formulaire**, puis de déclarer le paramètre comme `Form` :
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** va **extraire** les données pour **chaque champ** à partir des **données de formulaire** de la requête et vous fournir le modèle Pydantic que vous avez défini.
## Consulter les documents { #check-the-docs }
Vous pouvez le vérifier dans l'interface des documents à `/docs` :
<div class="screenshot">
<img src="/img/tutorial/request-form-models/image01.png">
</div>
## Interdire les champs de formulaire supplémentaires { #forbid-extra-form-fields }
Dans certains cas d'utilisation particuliers (probablement peu courants), vous pourriez vouloir **restreindre** les champs de formulaire à ceux déclarés dans le modèle Pydantic, et **interdire** tout champ **supplémentaire**.
/// note | Remarque
Ceci est pris en charge depuis la version `0.114.0` de FastAPI. 🤓
///
Vous pouvez utiliser la configuration du modèle Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Si un client tente d'envoyer des données supplémentaires, il recevra une **réponse d'erreur**.
Par exemple, si le client essaie d'envoyer les champs de formulaire :
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
Il recevra une réponse d'erreur lui indiquant que le champ `extra` n'est pas autorisé :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Résumer { #summary }
Vous pouvez utiliser des modèles Pydantic pour déclarer des champs de formulaire dans FastAPI. 😎

View File

@ -0,0 +1,41 @@
# Utiliser des formulaires et des fichiers de requête { #request-forms-and-files }
Vous pouvez définir des fichiers et des champs de formulaire en même temps à l'aide de `File` et `Form`.
/// info
Pour recevoir des fichiers téléversés et/ou des données de formulaire, installez d'abord <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Vous devez créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, l'activer, puis installer ce paquet, par exemple :
```console
$ pip install python-multipart
```
///
## Importer `File` et `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Définir des paramètres `File` et `Form` { #define-file-and-form-parameters }
Créez des paramètres de fichier et de formulaire de la même manière que pour `Body` ou `Query` :
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Les fichiers et les champs de formulaire seront téléversés en tant que données de formulaire et vous les recevrez.
Et vous pouvez déclarer certains fichiers comme `bytes` et d'autres comme `UploadFile`.
/// warning | Alertes
Vous pouvez déclarer plusieurs paramètres `File` et `Form` dans un *chemin d'accès*, mais vous ne pouvez pas aussi déclarer des champs `Body` que vous vous attendez à recevoir en JSON, car la requête aura le corps encodé en `multipart/form-data` au lieu de `application/json`.
Ce n'est pas une limitation de **FastAPI**, cela fait partie du protocole HTTP.
///
## Récapitulatif { #recap }
Utilisez `File` et `Form` ensemble lorsque vous devez recevoir des données et des fichiers dans la même requête.

View File

@ -0,0 +1,73 @@
# Données de formulaire { #form-data }
Lorsque vous devez recevoir des champs de formulaire au lieu de JSON, vous pouvez utiliser `Form`.
/// info
Pour utiliser les formulaires, installez d'abord <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, de l'activer, puis d'installer ce paquet, par exemple :
```console
$ pip install python-multipart
```
///
## Importer `Form` { #import-form }
Importez `Form` depuis `fastapi` :
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Définir les paramètres `Form` { #define-form-parameters }
Créez des paramètres de formulaire comme vous le feriez pour `Body` ou `Query` :
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Par exemple, dans l'une des manières dont la spécification OAuth2 peut être utilisée (appelée « password flow »), il est requis d'envoyer un `username` et un `password` comme champs de formulaire.
La <dfn title="spécification">spécification</dfn> exige que les champs soient exactement nommés `username` et `password`, et qu'ils soient envoyés en tant que champs de formulaire, pas en JSON.
Avec `Form`, vous pouvez déclarer les mêmes configurations que pour `Body` (ainsi que `Query`, `Path`, `Cookie`), y compris la validation, des exemples, un alias (p. ex. `user-name` au lieu de `username`), etc.
/// info
`Form` est une classe qui hérite directement de `Body`.
///
/// tip | Astuce
Pour déclarer des corps de formulaire, vous devez utiliser `Form` explicitement, car sinon les paramètres seraient interprétés comme des paramètres de requête ou des paramètres de corps (JSON).
///
## À propos des « champs de formulaire » { #about-form-fields }
La manière dont les formulaires HTML (`<form></form>`) envoient les données au serveur utilise normalement un encodage « spécial » pour ces données, différent de JSON.
**FastAPI** s'assure de lire ces données au bon endroit au lieu de JSON.
/// note | Détails techniques
Les données issues des formulaires sont normalement encodées avec le « type de média » `application/x-www-form-urlencoded`.
Mais lorsque le formulaire inclut des fichiers, il est encodé en `multipart/form-data`. Vous lirez la gestion des fichiers dans le chapitre suivant.
Si vous voulez en savoir plus sur ces encodages et les champs de formulaire, consultez la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Réseau des développeurs Mozilla">MDN</abbr> web docs pour <code>POST</code></a>.
///
/// warning | Alertes
Vous pouvez déclarer plusieurs paramètres `Form` dans un chemin d'accès, mais vous ne pouvez pas aussi déclarer des champs `Body` que vous vous attendez à recevoir en JSON, car la requête aura le corps encodé en `application/x-www-form-urlencoded` au lieu de `application/json`.
Ce n'est pas une limitation de **FastAPI**, cela fait partie du protocole HTTP.
///
## Récapitulatif { #recap }
Utilisez `Form` pour déclarer les paramètres d'entrée des données de formulaire.

View File

@ -0,0 +1,343 @@
# Modèle de réponse - Type de retour { #response-model-return-type }
Vous pouvez déclarer le type utilisé pour la réponse en annotant le **type de retour** de la *fonction de chemin d'accès*.
Vous pouvez utiliser des **annotations de type** de la même manière que pour les données d'entrée dans les **paramètres** de fonction. Vous pouvez utiliser des modèles Pydantic, des listes, des dictionnaires, des valeurs scalaires comme des entiers, des booléens, etc.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI utilisera ce type de retour pour :
* **Valider** les données renvoyées.
* Si les données sont invalides (par exemple, il manque un champ), cela signifie que le code de *votre* application est défectueux, qu'il ne renvoie pas ce qu'il devrait, et un erreur serveur sera renvoyée au lieu de renvoyer des données incorrectes. De cette façon, vous et vos clients pouvez être certains de recevoir les données attendues et avec la structure attendue.
* Ajouter un **JSON Schema** pour la réponse, dans lOpenAPI du *chemin d'accès*.
* Ceci sera utilisé par la **documentation automatique**.
* Ceci sera également utilisé par les outils de génération automatique de code client.
Mais surtout :
* Il **limitera et filtrera** les données de sortie à ce qui est défini dans le type de retour.
* C'est particulièrement important pour la **sécurité**, nous verrons cela plus bas.
## Paramètre `response_model` { #response-model-parameter }
Il existe des cas où vous devez ou souhaitez renvoyer des données qui ne correspondent pas exactement à ce que déclare le type.
Par exemple, vous pourriez vouloir **renvoyer un dictionnaire** ou un objet de base de données, mais **le déclarer comme un modèle Pydantic**. Ainsi, le modèle Pydantic ferait toute la documentation des données, la validation, etc. pour l'objet que vous avez renvoyé (par exemple un dictionnaire ou un objet de base de données).
Si vous ajoutez l'annotation du type de retour, les outils et éditeurs se plaindront avec une (juste) erreur vous indiquant que votre fonction renvoie un type (par exemple un dict) différent de ce que vous avez déclaré (par exemple un modèle Pydantic).
Dans ces cas, vous pouvez utiliser le paramètre `response_model` du *décorateur de chemin d'accès* au lieu du type de retour.
Vous pouvez utiliser le paramètre `response_model` dans n'importe lequel des *chemins d'accès* :
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Remarque
Notez que `response_model` est un paramètre de la méthode « decorator » (`get`, `post`, etc.). Pas de votre *fonction de chemin d'accès*, comme tous les paramètres et le corps.
///
`response_model` reçoit le même type que vous déclareriez pour un champ de modèle Pydantic, il peut donc s'agir d'un modèle Pydantic, mais il peut aussi être, par exemple, une `list` de modèles Pydantic, comme `List[Item]`.
FastAPI utilisera ce `response_model` pour toute la documentation des données, la validation, etc. et aussi pour **convertir et filtrer les données de sortie** selon sa déclaration de type.
/// tip | Astuce
Si vous avez des vérifications de type strictes dans votre éditeur, mypy, etc., vous pouvez déclarer le type de retour de la fonction en `Any`.
Ainsi, vous indiquez à l'éditeur que vous renvoyez intentionnellement n'importe quoi. Mais FastAPI effectuera quand même la documentation, la validation, le filtrage, etc. des données avec `response_model`.
///
### Priorité de `response_model` { #response-model-priority }
Si vous déclarez à la fois un type de retour et un `response_model`, c'est `response_model` qui aura la priorité et sera utilisé par FastAPI.
De cette manière, vous pouvez ajouter des annotations de type correctes à vos fonctions même si vous renvoyez un type différent du modèle de réponse, pour qu'il soit utilisé par l'éditeur et des outils comme mypy. Et vous pouvez toujours laisser FastAPI faire la validation des données, la documentation, etc. avec `response_model`.
Vous pouvez également utiliser `response_model=None` pour désactiver la création dun modèle de réponse pour ce *chemin d'accès* ; vous pourriez en avoir besoin si vous ajoutez des annotations de type pour des choses qui ne sont pas des champs valides Pydantic, vous verrez un exemple de cela dans une des sections ci-dessous.
## Renvoyer les mêmes données d'entrée { #return-the-same-input-data }
Ici, nous déclarons un modèle `UserIn`, il contiendra un mot de passe en clair :
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Info
Pour utiliser `EmailStr`, installez d'abord <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>.
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, de l'activer, puis de l'installer, par exemple :
```console
$ pip install email-validator
```
ou avec :
```console
$ pip install "pydantic[email]"
```
///
Et nous utilisons ce modèle pour déclarer notre entrée et le même modèle pour déclarer notre sortie :
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Désormais, chaque fois qu'un navigateur crée un utilisateur avec un mot de passe, l'API renverra le même mot de passe dans la réponse.
Dans ce cas, cela peut ne pas poser de problème, car c'est le même utilisateur qui envoie le mot de passe.
Mais si nous utilisons le même modèle pour un autre *chemin d'accès*, nous pourrions envoyer les mots de passe de nos utilisateurs à tous les clients.
/// danger | Danger
Ne stockez jamais le mot de passe en clair d'un utilisateur et ne l'envoyez pas dans une réponse de cette manière, à moins de connaître tous les écueils et de savoir exactement ce que vous faites.
///
## Ajouter un modèle de sortie { #add-an-output-model }
Nous pouvons à la place créer un modèle d'entrée avec le mot de passe en clair et un modèle de sortie sans celui-ci :
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Ici, même si notre *fonction de chemin d'accès* renvoie le même utilisateur d'entrée qui contient le mot de passe :
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
... nous avons déclaré `response_model` comme étant notre modèle `UserOut`, qui n'inclut pas le mot de passe :
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Ainsi, **FastAPI** se chargera de filtrer toutes les données qui ne sont pas déclarées dans le modèle de sortie (en utilisant Pydantic).
### `response_model` ou type de retour { #response-model-or-return-type }
Dans ce cas, comme les deux modèles sont différents, si nous annotions le type de retour de la fonction en `UserOut`, léditeur et les outils se plaindraient que nous renvoyons un type invalide, car ce sont des classes différentes.
C'est pourquoi, dans cet exemple, nous devons le déclarer dans le paramètre `response_model`.
... mais continuez à lire ci-dessous pour voir comment contourner cela.
## Type de retour et filtrage des données { #return-type-and-data-filtering }
Continuons l'exemple précédent. Nous voulions **annoter la fonction avec un type**, mais nous voulions pouvoir renvoyer depuis la fonction quelque chose qui inclut **plus de données**.
Nous voulons que FastAPI continue de **filtrer** les données à laide du modèle de réponse. Ainsi, même si la fonction renvoie plus de données, la réponse ninclura que les champs déclarés dans le modèle de réponse.
Dans l'exemple précédent, comme les classes étaient différentes, nous avons dû utiliser le paramètre `response_model`. Mais cela signifie aussi que nous ne bénéficions pas de la prise en charge de l'éditeur et des outils pour la vérification du type de retour de la fonction.
Mais dans la plupart des cas où nous avons besoin de quelque chose comme cela, nous voulons que le modèle **filtre/supprime** simplement une partie des données comme dans cet exemple.
Et dans ces cas, nous pouvons utiliser des classes et l'héritage pour tirer parti des **annotations de type** de fonction afin d'obtenir une meilleure prise en charge dans l'éditeur et les outils, tout en bénéficiant du **filtrage de données** de FastAPI.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Avec cela, nous obtenons la prise en charge des outils, des éditeurs et de mypy car ce code est correct en termes de types, et nous bénéficions également du filtrage des données par FastAPI.
Comment cela fonctionne-t-il ? Voyons cela. 🤓
### Annotations de type et outils { #type-annotations-and-tooling }
Voyons d'abord comment les éditeurs, mypy et autres outils considèreraient cela.
`BaseUser` a les champs de base. Puis `UserIn` hérite de `BaseUser` et ajoute le champ `password`, il inclura donc tous les champs des deux modèles.
Nous annotons le type de retour de la fonction en `BaseUser`, mais nous renvoyons en réalité une instance de `UserIn`.
Léditeur, mypy et d'autres outils ne sen plaindront pas car, en termes de typage, `UserIn` est une sous-classe de `BaseUser`, ce qui signifie que cest un type *valide* lorsque ce qui est attendu est n'importe quoi de type `BaseUser`.
### Filtrage des données par FastAPI { #fastapi-data-filtering }
Maintenant, pour FastAPI, il verra le type de retour et s'assurera que ce que vous renvoyez inclut **uniquement** les champs qui sont déclarés dans le type.
FastAPI fait plusieurs choses en interne avec Pydantic pour s'assurer que ces mêmes règles d'héritage de classes ne sont pas utilisées pour le filtrage des données renvoyées, sinon vous pourriez finir par renvoyer beaucoup plus de données que prévu.
De cette façon, vous obtenez le meilleur des deux mondes : annotations de type avec **prise en charge par les outils** et **filtrage des données**.
## Le voir dans la documentation { #see-it-in-the-docs }
Dans la documentation automatique, vous pouvez vérifier que le modèle d'entrée et le modèle de sortie auront chacun leur propre JSON Schema :
<img src="/img/tutorial/response-model/image01.png">
Et les deux modèles seront utilisés pour la documentation API interactive :
<img src="/img/tutorial/response-model/image02.png">
## Autres annotations de type de retour { #other-return-type-annotations }
Il peut y avoir des cas où vous renvoyez quelque chose qui n'est pas un champ Pydantic valide et vous l'annotez dans la fonction, uniquement pour obtenir la prise en charge fournie par les outils (léditeur, mypy, etc.).
### Renvoyer directement une Response { #return-a-response-directly }
Le cas le plus courant serait [de renvoyer directement une Response comme expliqué plus loin dans la documentation avancée](../advanced/response-directly.md){.internal-link target=_blank}.
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Ce cas simple est géré automatiquement par FastAPI car l'annotation du type de retour est la classe (ou une sous-classe de) `Response`.
Et les outils seront également satisfaits car `RedirectResponse` et `JSONResponse` sont des sous-classes de `Response`, donc l'annotation de type est correcte.
### Annoter une sous-classe de Response { #annotate-a-response-subclass }
Vous pouvez aussi utiliser une sous-classe de `Response` dans l'annotation de type :
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Cela fonctionnera également car `RedirectResponse` est une sous-classe de `Response`, et FastAPI gérera automatiquement ce cas simple.
### Annotations de type de retour invalides { #invalid-return-type-annotations }
Mais lorsque vous renvoyez un autre objet arbitraire qui n'est pas un type Pydantic valide (par exemple un objet de base de données) et que vous l'annotez ainsi dans la fonction, FastAPI essaiera de créer un modèle de réponse Pydantic à partir de cette annotation de type, et échouera.
Il en serait de même si vous aviez quelque chose comme une <dfn title="Une union entre plusieurs types signifie « n'importe lequel de ces types ».">union</dfn> entre différents types dont un ou plusieurs ne sont pas des types Pydantic valides, par exemple ceci échouerait 💥 :
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
... cela échoue parce que l'annotation de type n'est pas un type Pydantic et n'est pas juste une unique classe `Response` ou une sous-classe, c'est une union (l'un des deux) entre une `Response` et un `dict`.
### Désactiver le modèle de réponse { #disable-response-model }
En reprenant l'exemple ci-dessus, vous pourriez ne pas vouloir avoir la validation par défaut des données, la documentation, le filtrage, etc. effectués par FastAPI.
Mais vous pourriez vouloir tout de même conserver lannotation du type de retour dans la fonction pour bénéficier de la prise en charge des outils comme les éditeurs et les vérificateurs de type (par exemple mypy).
Dans ce cas, vous pouvez désactiver la génération du modèle de réponse en définissant `response_model=None` :
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Cela fera en sorte que FastAPI ignore la génération du modèle de réponse et vous permettra ainsi davoir toutes les annotations de type de retour dont vous avez besoin sans que cela naffecte votre application FastAPI. 🤓
## Paramètres d'encodage du modèle de réponse { #response-model-encoding-parameters }
Votre modèle de réponse peut avoir des valeurs par défaut, par exemple :
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (ou `str | None = None` en Python 3.10) a une valeur par défaut `None`.
* `tax: float = 10.5` a une valeur par défaut `10.5`.
* `tags: List[str] = []` a une valeur par défaut de liste vide : `[]`.
mais vous pourriez vouloir les omettre du résultat si elles n'ont pas été réellement stockées.
Par exemple, si vous avez des modèles avec de nombreux attributs optionnels dans une base NoSQL, mais que vous ne voulez pas envoyer de très longues réponses JSON remplies de valeurs par défaut.
### Utiliser le paramètre `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter }
Vous pouvez définir le paramètre du *décorateur de chemin d'accès* `response_model_exclude_unset=True` :
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
et ces valeurs par défaut ne seront pas incluses dans la réponse, uniquement les valeurs effectivement définies.
Ainsi, si vous envoyez une requête à ce *chemin d'accès* pour l'article avec l'ID `foo`, la réponse (sans les valeurs par défaut) sera :
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Info
Vous pouvez également utiliser :
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
comme décrit dans <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">la documentation Pydantic</a> pour `exclude_defaults` et `exclude_none`.
///
#### Données avec des valeurs pour des champs avec des valeurs par défaut { #data-with-values-for-fields-with-defaults }
Mais si vos données ont des valeurs pour les champs du modèle avec des valeurs par défaut, comme l'article avec l'ID `bar` :
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
elles seront incluses dans la réponse.
#### Données avec les mêmes valeurs que les valeurs par défaut { #data-with-the-same-values-as-the-defaults }
Si les données ont les mêmes valeurs que les valeurs par défaut, comme l'article avec l'ID `baz` :
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI est suffisamment intelligent (en fait, Pydantic lest) pour comprendre que, même si `description`, `tax` et `tags` ont les mêmes valeurs que les valeurs par défaut, elles ont été définies explicitement (au lieu d'être prises depuis les valeurs par défaut).
Elles seront donc incluses dans la réponse JSON.
/// tip | Astuce
Notez que les valeurs par défaut peuvent être n'importe quoi, pas seulement `None`.
Elles peuvent être une liste (`[]`), un `float` de `10.5`, etc.
///
### `response_model_include` et `response_model_exclude` { #response-model-include-and-response-model-exclude }
Vous pouvez également utiliser les paramètres du *décorateur de chemin d'accès* `response_model_include` et `response_model_exclude`.
Ils prennent un `set` de `str` avec les noms des attributs à inclure (en omettant le reste) ou à exclure (en incluant le reste).
Cela peut être utilisé comme un raccourci rapide si vous n'avez qu'un seul modèle Pydantic et que vous souhaitez supprimer certaines données de la sortie.
/// tip | Astuce
Mais il est toujours recommandé d'utiliser les idées ci-dessus, en utilisant plusieurs classes, plutôt que ces paramètres.
En effet, le JSON Schema généré dans lOpenAPI de votre application (et la documentation) restera celui du modèle complet, même si vous utilisez `response_model_include` ou `response_model_exclude` pour omettre certains attributs.
Cela s'applique également à `response_model_by_alias` qui fonctionne de manière similaire.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Astuce
La syntaxe `{"name", "description"}` crée un `set` avec ces deux valeurs.
Elle est équivalente à `set(["name", "description"])`.
///
#### Utiliser des `list` au lieu de `set` { #using-lists-instead-of-sets }
Si vous oubliez d'utiliser un `set` et utilisez une `list` ou un `tuple` à la place, FastAPI le convertira quand même en `set` et cela fonctionnera correctement :
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Récapitulatif { #recap }
Utilisez le paramètre du *décorateur de chemin d'accès* `response_model` pour définir les modèles de réponse et surtout pour garantir que les données privées sont filtrées.
Utilisez `response_model_exclude_unset` pour ne renvoyer que les valeurs définies explicitement.

View File

@ -0,0 +1,101 @@
# Code d'état de la réponse { #response-status-code }
De la même manière que vous pouvez spécifier un modèle de réponse, vous pouvez également déclarer le code d'état HTTP utilisé pour la réponse avec le paramètre `status_code` dans n'importe lequel des chemins d'accès :
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Remarque
Remarquez que `status_code` est un paramètre de la méthode « decorator » (`get`, `post`, etc.). Pas de votre fonction de chemin d'accès, comme tous les paramètres et le corps.
///
Le paramètre `status_code` reçoit un nombre correspondant au code d'état HTTP.
/// info
`status_code` peut aussi recevoir un `IntEnum`, comme le <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a> de Python.
///
Il va :
* Renvoyer ce code d'état dans la réponse.
* Le documenter comme tel dans le schéma OpenAPI (et donc dans les interfaces utilisateur) :
<img src="/img/tutorial/response-status-code/image01.png">
/// note | Remarque
Certains codes de réponse (voir la section suivante) indiquent que la réponse n'a pas de corps.
FastAPI le sait et produira une documentation OpenAPI indiquant qu'il n'y a pas de corps de réponse.
///
## À propos des codes d'état HTTP { #about-http-status-codes }
/// note | Remarque
Si vous savez déjà ce que sont les codes d'état HTTP, passez à la section suivante.
///
En HTTP, vous envoyez un code d'état numérique de 3 chiffres dans la réponse.
Ces codes d'état ont un nom associé pour les reconnaître, mais la partie importante est le nombre.
En bref :
* `100 - 199` sont pour « Information ». Vous les utilisez rarement directement. Les réponses avec ces codes d'état ne peuvent pas avoir de corps.
* **`200 - 299`** sont pour les réponses de « Succès ». Ce sont celles que vous utiliserez le plus.
* `200` est le code d'état par défaut, ce qui signifie que tout était « OK ».
* Un autre exemple est `201`, « Créé ». Il est couramment utilisé après la création d'un nouvel enregistrement dans la base de données.
* Un cas particulier est `204`, « Aucun contenu ». Cette réponse est utilisée lorsqu'il n'y a aucun contenu à renvoyer au client ; la réponse ne doit donc pas avoir de corps.
* **`300 - 399`** sont pour la « Redirection ». Les réponses avec ces codes d'état peuvent avoir ou non un corps, sauf `304`, « Non modifié », qui ne doit pas en avoir.
* **`400 - 499`** sont pour les réponses d'« Erreur côté client ». C'est probablement le deuxième type que vous utiliserez le plus.
* Un exemple est `404`, pour une réponse « Non trouvé ».
* Pour des erreurs génériques du client, vous pouvez simplement utiliser `400`.
* `500 - 599` sont pour les erreurs côté serveur. Vous ne les utilisez presque jamais directement. Lorsqu'un problème survient quelque part dans le code de votre application ou sur le serveur, il renverra automatiquement l'un de ces codes d'état.
/// tip | Astuce
Pour en savoir plus sur chaque code d'état et à quoi il correspond, consultez la <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Réseau des développeurs Mozilla">MDN</abbr> documentation about HTTP status codes</a>.
///
## Raccourci pour se souvenir des noms { #shortcut-to-remember-the-names }
Reprenons l'exemple précédent :
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` est le code d'état pour « Créé ».
Mais vous n'avez pas à mémoriser la signification de chacun de ces codes.
Vous pouvez utiliser les variables pratiques de `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Elles ne sont qu'une commodité, elles contiennent le même nombre, mais de cette façon vous pouvez utiliser l'autocomplétion de l'éditeur pour les trouver :
<img src="/img/tutorial/response-status-code/image02.png">
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette import status`.
FastAPI fournit le même `starlette.status` que `fastapi.status`, uniquement pour votre commodité de développeur. Mais cela vient directement de Starlette.
///
## Modifier la valeur par défaut { #changing-the-default }
Plus tard, dans le [Guide utilisateur avancé](../advanced/response-change-status-code.md){.internal-link target=_blank}, vous verrez comment renvoyer un code d'état différent de celui par défaut que vous déclarez ici.

Some files were not shown because too many files have changed in this diff Show More