Sync fastapi docs from 11614be9 on 2026-03-11
This commit is contained in:
@@ -0,0 +1,503 @@
|
||||
# Fichier de test LLM { #llm-test-file }
|
||||
|
||||
Ce document teste si le <abbr title="Large Language Model - Grand modèle de langage">LLM</abbr>, qui traduit la documentation, comprend le `general_prompt` dans `scripts/translate.py` et l’invite spécifique à la langue dans `docs/{language code}/llm-prompt.md`. L’invite spécifique à la langue est ajoutée à la fin de `general_prompt`.
|
||||
|
||||
Les tests ajoutés ici seront visibles par tous les concepteurs d’invites spécifiques à chaque langue.
|
||||
|
||||
Utiliser comme suit :
|
||||
|
||||
* Avoir une invite spécifique à la langue - `docs/{language code}/llm-prompt.md`.
|
||||
* Effectuer une nouvelle traduction de ce document dans votre langue cible souhaitée (voir par exemple la commande `translate-page` de `translate.py`). Cela créera la traduction sous `docs/{language code}/docs/_llm-test.md`.
|
||||
* Vérifier si tout est correct dans la traduction.
|
||||
* Si nécessaire, améliorer votre invite spécifique à la langue, l’invite générale, ou le document anglais.
|
||||
* Corriger ensuite manuellement les problèmes restants dans la traduction, afin que ce soit une bonne traduction.
|
||||
* Retraduire, en ayant la bonne traduction en place. Le résultat idéal serait que le LLM ne fasse plus aucun changement à la traduction. Cela signifie que l’invite générale et votre invite spécifique à la langue sont aussi bonnes que possible (il fera parfois quelques changements apparemment aléatoires, la raison étant que <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">les LLM ne sont pas des algorithmes déterministes</a>).
|
||||
|
||||
Les tests :
|
||||
|
||||
## Extraits de code { #code-snippets }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Ceci est un extrait de code : `foo`. Et ceci est un autre extrait de code : `bar`. Et encore un autre : `baz quux`.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Le contenu des extraits de code doit être laissé tel quel.
|
||||
|
||||
Voir la section `### Content of code snippets` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Guillemets { #quotes }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Hier, mon ami a écrit : « Si vous écrivez « incorrectly » correctement, vous l’avez écrit de façon incorrecte ». À quoi j’ai répondu : « Correct, mais ‘incorrectly’ est incorrectement non pas ‘« incorrectly »’ ».
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Le LLM traduira probablement ceci de manière erronée. Il est seulement intéressant de voir s’il conserve la traduction corrigée lors d’une retraduction.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Le concepteur de l’invite peut choisir s’il souhaite convertir les guillemets neutres en guillemets typographiques. Il est acceptable de les laisser tels quels.
|
||||
|
||||
Voir par exemple la section `### Quotes` dans `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
||||
## Guillemets dans les extraits de code { #quotes-in-code-snippets }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
`pip install "foo[bar]"`
|
||||
|
||||
Exemples de littéraux de chaîne dans des extraits de code : `"this"`, `'that'`.
|
||||
|
||||
Un exemple difficile de littéraux de chaîne dans des extraits de code : `f"I like {'oranges' if orange else "apples"}"`
|
||||
|
||||
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
... Cependant, les guillemets à l’intérieur des extraits de code doivent rester tels quels.
|
||||
|
||||
////
|
||||
|
||||
## Blocs de code { #code-blocks }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Un exemple de code Bash ...
|
||||
|
||||
```bash
|
||||
# Afficher un message de bienvenue à l'univers
|
||||
echo "Hello universe"
|
||||
```
|
||||
|
||||
... et un exemple de code console ...
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
|
||||
Searching for package file structure
|
||||
```
|
||||
|
||||
... et un autre exemple de code console ...
|
||||
|
||||
```console
|
||||
// Créer un répertoire "Code"
|
||||
$ mkdir code
|
||||
// Aller dans ce répertoire
|
||||
$ cd code
|
||||
```
|
||||
|
||||
... et un exemple de code Python ...
|
||||
|
||||
```Python
|
||||
wont_work() # Cela ne fonctionnera pas 😱
|
||||
works(foo="bar") # Cela fonctionne 🎉
|
||||
```
|
||||
|
||||
... et c’est tout.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Le code dans les blocs de code ne doit pas être modifié, à l’exception des commentaires.
|
||||
|
||||
Voir la section `### Content of code blocks` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Onglets et encadrés colorés { #tabs-and-colored-boxes }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
/// info | Info
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// note | Remarque
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// note | Détails techniques
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// check | Vérifications
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// tip | Astuce
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// warning | Alertes
|
||||
Du texte
|
||||
///
|
||||
|
||||
/// danger | Danger
|
||||
Du texte
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Les onglets et les blocs « Info »/« Note »/« Warning »/etc. doivent avoir la traduction de leur titre ajoutée après une barre verticale (« | »).
|
||||
|
||||
Voir les sections `### Special blocks` et `### Tab blocks` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Liens Web et internes { #web-and-internal-links }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Le texte du lien doit être traduit, l’adresse du lien doit rester inchangée :
|
||||
|
||||
* [Lien vers le titre ci-dessus](#code-snippets)
|
||||
* [Lien interne](index.md#installation){.internal-link target=_blank}
|
||||
* <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">Lien externe</a>
|
||||
* <a href="https://fastapi.tiangolo.com/css/styles.css" class="external-link" target="_blank">Lien vers une feuille de style</a>
|
||||
* <a href="https://fastapi.tiangolo.com/js/logic.js" class="external-link" target="_blank">Lien vers un script</a>
|
||||
* <a href="https://fastapi.tiangolo.com/img/foo.jpg" class="external-link" target="_blank">Lien vers une image</a>
|
||||
|
||||
Le texte du lien doit être traduit, l’adresse du lien doit pointer vers la traduction :
|
||||
|
||||
* <a href="https://fastapi.tiangolo.com/fr/" class="external-link" target="_blank">Lien FastAPI</a>
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Les liens doivent être traduits, mais leur adresse doit rester inchangée. Exception faite des liens absolus vers des pages de la documentation FastAPI. Dans ce cas, il faut pointer vers la traduction.
|
||||
|
||||
Voir la section `### Links` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Éléments HTML « abbr » { #html-abbr-elements }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Voici quelques éléments entourés d’un élément HTML « abbr » (certains sont inventés) :
|
||||
|
||||
### L’abbr fournit une expression complète { #the-abbr-gives-a-full-phrase }
|
||||
|
||||
* <abbr title="Getting Things Done - S'organiser pour réussir">GTD</abbr>
|
||||
* <abbr title="less than - inférieur à"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token - Jeton Web XML">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - Interface passerelle serveur parallèle">PSGI</abbr>
|
||||
|
||||
### L’abbr donne une expression complète et une explication { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network - Réseau des développeurs Mozilla: documentation pour les développeurs, écrite par l’équipe Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output - Entrée/Sortie: lecture ou écriture sur le disque, communications réseau.">I/O</abbr>.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Les attributs « title » des éléments « abbr » sont traduits en suivant des consignes spécifiques.
|
||||
|
||||
Les traductions peuvent ajouter leurs propres éléments « abbr » que le LLM ne doit pas supprimer. Par exemple pour expliquer des mots anglais.
|
||||
|
||||
Voir la section `### HTML abbr elements` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
////
|
||||
|
||||
## Éléments HTML « dfn » { #html-dfn-elements }
|
||||
|
||||
* <dfn title="Un groupe de machines configurées pour être connectées et travailler ensemble d’une certaine manière.">grappe</dfn>
|
||||
* <dfn title="Une méthode d’apprentissage automatique qui utilise des réseaux de neurones artificiels avec de nombreuses couches cachées entre les couches d’entrée et de sortie, développant ainsi une structure interne complète">Apprentissage profond</dfn>
|
||||
|
||||
## Titres { #headings }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
### Créer une application Web - un tutoriel { #develop-a-webapp-a-tutorial }
|
||||
|
||||
Bonjour.
|
||||
|
||||
### Annotations de type et indications de type { #type-hints-and-annotations }
|
||||
|
||||
Rebonjour.
|
||||
|
||||
### Superclasses et sous-classes { #super-and-subclasses }
|
||||
|
||||
Rebonjour.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
La seule règle stricte pour les titres est que le LLM laisse la partie hachage entre accolades inchangée, ce qui garantit que les liens ne se rompent pas.
|
||||
|
||||
Voir la section `### Headings` dans l’invite générale dans `scripts/translate.py`.
|
||||
|
||||
Pour certaines consignes spécifiques à la langue, voir par exemple la section `### Headings` dans `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
||||
## Termes utilisés dans les documents { #terms-used-in-the-docs }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
* vous
|
||||
* votre
|
||||
|
||||
* p. ex.
|
||||
* etc.
|
||||
|
||||
* `foo` en tant que `int`
|
||||
* `bar` en tant que `str`
|
||||
* `baz` en tant que `list`
|
||||
|
||||
* le Tutoriel - Guide utilisateur
|
||||
* le Guide utilisateur avancé
|
||||
* la documentation SQLModel
|
||||
* la documentation de l’API
|
||||
* la documentation automatique
|
||||
|
||||
* Data Science
|
||||
* Apprentissage profond
|
||||
* Apprentissage automatique
|
||||
* Injection de dépendances
|
||||
* authentification HTTP Basic
|
||||
* HTTP Digest
|
||||
* format ISO
|
||||
* la norme JSON Schema
|
||||
* le schéma JSON
|
||||
* la définition de schéma
|
||||
* Flux Password
|
||||
* Mobile
|
||||
|
||||
* déprécié
|
||||
* conçu
|
||||
* invalide
|
||||
* à la volée
|
||||
* standard
|
||||
* par défaut
|
||||
* sensible à la casse
|
||||
* insensible à la casse
|
||||
|
||||
* servir l’application
|
||||
* servir la page
|
||||
|
||||
* l’app
|
||||
* l’application
|
||||
|
||||
* la requête
|
||||
* la réponse
|
||||
* la réponse d’erreur
|
||||
|
||||
* le chemin d’accès
|
||||
* le décorateur de chemin d’accès
|
||||
* la fonction de chemin d’accès
|
||||
|
||||
* le corps
|
||||
* le corps de la requête
|
||||
* le corps de la réponse
|
||||
* le corps JSON
|
||||
* le corps de formulaire
|
||||
* le corps de fichier
|
||||
* le corps de la fonction
|
||||
|
||||
* le paramètre
|
||||
* le paramètre de corps
|
||||
* le paramètre de chemin
|
||||
* le paramètre de requête
|
||||
* le paramètre de cookie
|
||||
* le paramètre d’en-tête
|
||||
* le paramètre de formulaire
|
||||
* le paramètre de fonction
|
||||
|
||||
* l’événement
|
||||
* l’événement de démarrage
|
||||
* le démarrage du serveur
|
||||
* l’événement d’arrêt
|
||||
* l’événement de cycle de vie
|
||||
|
||||
* le gestionnaire
|
||||
* le gestionnaire d’événements
|
||||
* le gestionnaire d’exceptions
|
||||
* gérer
|
||||
|
||||
* le modèle
|
||||
* le modèle Pydantic
|
||||
* le modèle de données
|
||||
* le modèle de base de données
|
||||
* le modèle de formulaire
|
||||
* l’objet modèle
|
||||
|
||||
* la classe
|
||||
* la classe de base
|
||||
* la classe parente
|
||||
* la sous-classe
|
||||
* la classe enfant
|
||||
* la classe sœur
|
||||
* la méthode de classe
|
||||
|
||||
* l’en-tête
|
||||
* les en-têtes
|
||||
* l’en-tête d’autorisation
|
||||
* l’en-tête `Authorization`
|
||||
* l’en-tête transféré
|
||||
|
||||
* le système d’injection de dépendances
|
||||
* la dépendance
|
||||
* l’élément dépendable
|
||||
* le dépendant
|
||||
|
||||
* lié aux E/S
|
||||
* lié au processeur
|
||||
* concurrence
|
||||
* parallélisme
|
||||
* multi-traitement
|
||||
|
||||
* la variable d’env
|
||||
* la variable d’environnement
|
||||
* le `PATH`
|
||||
* la variable `PATH`
|
||||
|
||||
* l’authentification
|
||||
* le fournisseur d’authentification
|
||||
* l’autorisation
|
||||
* le formulaire d’autorisation
|
||||
* le fournisseur d’autorisation
|
||||
* l’utilisateur s’authentifie
|
||||
* le système authentifie l’utilisateur
|
||||
|
||||
* la CLI
|
||||
* l’interface en ligne de commande
|
||||
|
||||
* le serveur
|
||||
* le client
|
||||
|
||||
* le fournisseur cloud
|
||||
* le service cloud
|
||||
|
||||
* le développement
|
||||
* les étapes de développement
|
||||
|
||||
* le dict
|
||||
* le dictionnaire
|
||||
* l’énumération
|
||||
* l’enum
|
||||
* le membre d’enum
|
||||
|
||||
* l’encodeur
|
||||
* le décodeur
|
||||
* encoder
|
||||
* décoder
|
||||
|
||||
* l’exception
|
||||
* lever
|
||||
|
||||
* l’expression
|
||||
* l’instruction
|
||||
|
||||
* le frontend
|
||||
* le backend
|
||||
|
||||
* la discussion GitHub
|
||||
* le ticket GitHub
|
||||
|
||||
* la performance
|
||||
* l’optimisation des performances
|
||||
|
||||
* le type de retour
|
||||
* la valeur de retour
|
||||
|
||||
* la sécurité
|
||||
* le schéma de sécurité
|
||||
|
||||
* la tâche
|
||||
* la tâche d’arrière-plan
|
||||
* la fonction de tâche
|
||||
|
||||
* le template
|
||||
* le moteur de templates
|
||||
|
||||
* l’annotation de type
|
||||
* l’annotation de type
|
||||
|
||||
* le worker du serveur
|
||||
* le worker Uvicorn
|
||||
* le Worker Gunicorn
|
||||
* le processus worker
|
||||
* la classe de worker
|
||||
* la charge de travail
|
||||
|
||||
* le déploiement
|
||||
* déployer
|
||||
|
||||
* le SDK
|
||||
* le kit de développement logiciel
|
||||
|
||||
* le `APIRouter`
|
||||
* le `requirements.txt`
|
||||
* le jeton Bearer
|
||||
* le changement majeur incompatible
|
||||
* le bogue
|
||||
* le bouton
|
||||
* l’appelable
|
||||
* le code
|
||||
* le commit
|
||||
* le gestionnaire de contexte
|
||||
* la coroutine
|
||||
* la session de base de données
|
||||
* le disque
|
||||
* le domaine
|
||||
* le moteur
|
||||
* le faux X
|
||||
* la méthode HTTP GET
|
||||
* l’élément
|
||||
* la bibliothèque
|
||||
* le cycle de vie
|
||||
* le verrou
|
||||
* le middleware
|
||||
* l’application mobile
|
||||
* le module
|
||||
* le montage
|
||||
* le réseau
|
||||
* l’origine
|
||||
* la surcharge
|
||||
* le payload
|
||||
* le processeur
|
||||
* la propriété
|
||||
* le proxy
|
||||
* la pull request
|
||||
* la requête
|
||||
* la RAM
|
||||
* la machine distante
|
||||
* le code d’état
|
||||
* la chaîne
|
||||
* l’étiquette
|
||||
* le framework Web
|
||||
* le joker
|
||||
* retourner
|
||||
* valider
|
||||
|
||||
////
|
||||
|
||||
//// tab | Info
|
||||
|
||||
Il s’agit d’une liste non exhaustive et non normative de termes (principalement) techniques présents dans les documents. Elle peut aider le concepteur de l’invite à déterminer pour quels termes le LLM a besoin d’un coup de main. Par exemple, lorsqu’il continue de remplacer une bonne traduction par une traduction sous-optimale. Ou lorsqu’il a des difficultés à conjuguer/décliner un terme dans votre langue.
|
||||
|
||||
Voir par exemple la section `### List of English terms and their preferred German translations` dans `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
@@ -0,0 +1,3 @@
|
||||
# À propos { #about }
|
||||
|
||||
À propos de FastAPI, de sa conception, de ses sources d'inspiration et plus encore. 🤓
|
||||
@@ -1,6 +1,6 @@
|
||||
# Réponses supplémentaires dans OpenAPI
|
||||
# Réponses supplémentaires dans OpenAPI { #additional-responses-in-openapi }
|
||||
|
||||
/// warning | Attention
|
||||
/// warning | Alertes
|
||||
|
||||
Ceci concerne un sujet plutôt avancé.
|
||||
|
||||
@@ -8,15 +8,15 @@ 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.
|
||||
|
||||
Mais pour ces réponses supplémentaires, vous devez vous assurer de renvoyer directement une `Response` comme `JSONResponse`, avec votre code HTTP et votre contenu.
|
||||
|
||||
## Réponse supplémentaire avec `model`
|
||||
## Réponse supplémentaire avec `model` { #additional-response-with-model }
|
||||
|
||||
Vous pouvez ajouter à votre décorateur de *paramètre de chemin* un paramètre `responses`.
|
||||
Vous pouvez passer à vos décorateurs de *chemin d'accès* un paramètre `responses`.
|
||||
|
||||
Il prend comme valeur un `dict` dont les clés sont des codes HTTP pour chaque réponse, comme `200`, et la valeur de ces clés sont d'autres `dict` avec des informations pour chacun d'eux.
|
||||
|
||||
@@ -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.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
@@ -49,7 +49,7 @@ Le bon endroit est :
|
||||
|
||||
///
|
||||
|
||||
Les réponses générées au format OpenAPI pour cette *opération de chemin* seront :
|
||||
Les réponses générées au format OpenAPI pour ce *chemin d'accès* seront :
|
||||
|
||||
```JSON hl_lines="3-12"
|
||||
{
|
||||
@@ -169,13 +169,13 @@ Les schémas sont référencés à un autre endroit du modèle OpenAPI :
|
||||
}
|
||||
```
|
||||
|
||||
## Types de médias supplémentaires pour la réponse principale
|
||||
## Types de médias supplémentaires pour la réponse principale { #additional-media-types-for-the-main-response }
|
||||
|
||||
Vous pouvez utiliser ce même paramètre `responses` pour ajouter différents types de médias pour la même réponse principale.
|
||||
|
||||
Par exemple, vous pouvez ajouter un type de média supplémentaire `image/png`, en déclarant que votre *opération de chemin* peut renvoyer un objet JSON (avec le type de média `application/json`) ou une image PNG :
|
||||
Par exemple, vous pouvez ajouter un type de média supplémentaire `image/png`, en déclarant que votre *chemin d'accès* peut renvoyer un objet JSON (avec le type de média `application/json`) ou une image PNG :
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
|
||||
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
@@ -191,7 +191,7 @@ Mais si vous avez spécifié une classe de réponse personnalisée avec `None` c
|
||||
|
||||
///
|
||||
|
||||
## Combinaison d'informations
|
||||
## Combiner les informations { #combining-information }
|
||||
|
||||
Vous pouvez également combiner des informations de réponse provenant de plusieurs endroits, y compris les paramètres `response_model`, `status_code` et `responses`.
|
||||
|
||||
@@ -203,17 +203,17 @@ 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.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 :
|
||||
|
||||
<img src="/img/tutorial/additional-responses/image01.png">
|
||||
|
||||
## Combinez les réponses prédéfinies et les réponses personnalisées
|
||||
## Combinez les réponses prédéfinies et les réponses personnalisées { #combine-predefined-responses-and-custom-ones }
|
||||
|
||||
Vous voulez peut-être avoir des réponses prédéfinies qui s'appliquent à de nombreux *paramètre de chemin*, mais vous souhaitez les combiner avec des réponses personnalisées nécessaires à chaque *opération de chemin*.
|
||||
Vous voulez peut-être avoir des réponses prédéfinies qui s'appliquent à de nombreux *chemins d'accès*, mais vous souhaitez les combiner avec des réponses personnalisées nécessaires à chaque *chemin d'accès*.
|
||||
|
||||
Dans ces cas, vous pouvez utiliser la technique Python "d'affection par décomposition" (appelé _unpacking_ en anglais) d'un `dict` avec `**dict_to_unpack` :
|
||||
Dans ces cas, vous pouvez utiliser la technique Python « unpacking » d'un `dict` avec `**dict_to_unpack` :
|
||||
|
||||
```Python
|
||||
old_dict = {
|
||||
@@ -233,15 +233,15 @@ Ici, `new_dict` contiendra toutes les paires clé-valeur de `old_dict` plus la n
|
||||
}
|
||||
```
|
||||
|
||||
Vous pouvez utiliser cette technique pour réutiliser certaines réponses prédéfinies dans vos *paramètres de chemin* et les combiner avec des réponses personnalisées supplémentaires.
|
||||
Vous pouvez utiliser cette technique pour réutiliser certaines réponses prédéfinies dans vos *chemins d'accès* et les combiner avec des réponses personnalisées supplémentaires.
|
||||
|
||||
Par exemple:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
|
||||
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
|
||||
|
||||
## Plus d'informations sur les réponses OpenAPI
|
||||
## Plus d'informations sur les réponses OpenAPI { #more-information-about-openapi-responses }
|
||||
|
||||
Pour voir exactement ce que vous pouvez inclure dans les réponses, vous pouvez consulter ces sections dans la spécification OpenAPI :
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject" class="external-link" target="_blank">Objet Responses de OpenAPI </a>, il inclut le `Response Object`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject" class="external-link" target="_blank">Objet Response de OpenAPI </a>, vous pouvez inclure n'importe quoi directement dans chaque réponse à l'intérieur de votre paramètre `responses`. Y compris `description`, `headers`, `content` (à l'intérieur de cela, vous déclarez différents types de médias et schémas JSON) et `links`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">Objet Responses de OpenAPI</a>, il inclut le `Response Object`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">Objet Response de OpenAPI</a>, vous pouvez inclure n'importe quoi directement dans chaque réponse à l'intérieur de votre paramètre `responses`. Y compris `description`, `headers`, `content` (à l'intérieur de cela, vous déclarez différents types de médias et schémas JSON) et `links`.
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
# Codes HTTP supplémentaires
|
||||
# Codes HTTP supplémentaires { #additional-status-codes }
|
||||
|
||||
Par défaut, **FastAPI** renverra les réponses à l'aide d'une structure de données `JSONResponse`, en plaçant la réponse de votre *chemin d'accès* à l'intérieur de cette `JSONResponse`.
|
||||
|
||||
Il utilisera le code HTTP par défaut ou celui que vous avez défini dans votre *chemin d'accès*.
|
||||
|
||||
## Codes HTTP supplémentaires
|
||||
## Codes HTTP supplémentaires { #additional-status-codes_1 }
|
||||
|
||||
Si vous souhaitez renvoyer des codes HTTP supplémentaires en plus du code principal, vous pouvez le faire en renvoyant directement une `Response`, comme une `JSONResponse`, et en définissant directement le code HTTP supplémentaire.
|
||||
|
||||
Par exemple, disons que vous voulez avoir un *chemin d'accès* qui permet de mettre à jour les éléments et renvoie les codes HTTP 200 "OK" en cas de succès.
|
||||
Par exemple, disons que vous voulez avoir un *chemin d'accès* qui permet de mettre à jour les éléments et renvoie les codes HTTP 200 « OK » en cas de succès.
|
||||
|
||||
Mais vous voulez aussi qu'il accepte de nouveaux éléments. Et lorsque les éléments n'existaient pas auparavant, il les crée et renvoie un code HTTP de 201 "Créé".
|
||||
Mais vous voulez aussi qu'il accepte de nouveaux éléments. Et lorsque les éléments n'existaient pas auparavant, il les crée et renvoie un code HTTP de 201 « Créé ».
|
||||
|
||||
Pour y parvenir, importez `JSONResponse` et renvoyez-y directement votre contenu, en définissant le `status_code` que vous souhaitez :
|
||||
|
||||
{* ../../docs_src/additional_status_codes/tutorial001.py hl[4,25] *}
|
||||
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
|
||||
|
||||
/// warning | Attention
|
||||
/// warning | Alertes
|
||||
|
||||
Lorsque vous renvoyez une `Response` directement, comme dans l'exemple ci-dessus, elle sera renvoyée directement.
|
||||
|
||||
Elle ne sera pas sérialisée avec un modèle.
|
||||
|
||||
Assurez-vous qu'il contient les données souhaitées et que les valeurs soient dans un format JSON valides (si vous utilisez une `JSONResponse`).
|
||||
Assurez-vous qu'il contient les données souhaitées et que les valeurs sont dans un format JSON valide (si vous utilisez une `JSONResponse`).
|
||||
|
||||
///
|
||||
|
||||
@@ -30,12 +30,12 @@ Assurez-vous qu'il contient les données souhaitées et que les valeurs soient d
|
||||
|
||||
Vous pouvez également utiliser `from starlette.responses import JSONResponse`.
|
||||
|
||||
Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` sous forme d'un alias accessible par `fastapi.responses`. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en est de même avec l'objet `statut`.
|
||||
Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` sous forme d'un alias accessible par `fastapi.responses`. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en est de même avec `status`.
|
||||
|
||||
///
|
||||
|
||||
## Documents OpenAPI et API
|
||||
## 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 dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
# Dépendances avancées { #advanced-dependencies }
|
||||
|
||||
## Dépendances paramétrées { #parameterized-dependencies }
|
||||
|
||||
Toutes les dépendances que nous avons vues étaient des fonctions ou des classes fixes.
|
||||
|
||||
Mais il peut y avoir des cas où vous souhaitez pouvoir définir des paramètres sur la dépendance, sans devoir déclarer de nombreuses fonctions ou classes différentes.
|
||||
|
||||
Imaginons que nous voulions avoir une dépendance qui vérifie si le paramètre de requête `q` contient un contenu fixe.
|
||||
|
||||
Mais nous voulons pouvoir paramétrer ce contenu fixe.
|
||||
|
||||
## Une instance « callable » { #a-callable-instance }
|
||||
|
||||
En Python, il existe un moyen de rendre une instance de classe « callable ».
|
||||
|
||||
Pas la classe elle‑même (qui est déjà un callable), mais une instance de cette classe.
|
||||
|
||||
Pour cela, nous déclarons une méthode `__call__` :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
Dans ce cas, ce `__call__` est ce que **FastAPI** utilisera pour détecter des paramètres supplémentaires et des sous‑dépendances, et c’est ce qui sera appelé pour transmettre ensuite une valeur au paramètre dans votre *fonction de chemin d'accès*.
|
||||
|
||||
## Paramétrer l'instance { #parameterize-the-instance }
|
||||
|
||||
Et maintenant, nous pouvons utiliser `__init__` pour déclarer les paramètres de l’instance, que nous utiliserons pour « paramétrer » la dépendance :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
Dans ce cas, **FastAPI** n’accèdera pas à `__init__` et ne s’en souciera pas ; nous l’utiliserons directement dans notre code.
|
||||
|
||||
## Créer une instance { #create-an-instance }
|
||||
|
||||
Nous pouvons créer une instance de cette classe avec :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
Et de cette façon, nous pouvons « paramétrer » notre dépendance, qui contient maintenant « bar », en tant qu’attribut `checker.fixed_content`.
|
||||
|
||||
## Utiliser l'instance comme dépendance { #use-the-instance-as-a-dependency }
|
||||
|
||||
Ensuite, nous pourrions utiliser ce `checker` dans un `Depends(checker)`, au lieu de `Depends(FixedContentQueryChecker)`, car la dépendance est l’instance, `checker`, et non la classe elle‑même.
|
||||
|
||||
Et lors de la résolution de la dépendance, **FastAPI** appellera ce `checker` comme ceci :
|
||||
|
||||
```Python
|
||||
checker(q="somequery")
|
||||
```
|
||||
|
||||
... et passera ce que cela renvoie comme valeur de la dépendance à notre *fonction de chemin d'accès*, en tant que paramètre `fixed_content_included` :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Tout cela peut sembler artificiel. Et il n’est peut‑être pas encore très clair en quoi c’est utile.
|
||||
|
||||
Ces exemples sont volontairement simples, mais ils montrent comment tout cela fonctionne.
|
||||
|
||||
Dans les chapitres sur la sécurité, il existe des fonctions utilitaires implémentées de la même manière.
|
||||
|
||||
Si vous avez compris tout cela, vous savez déjà comment ces outils utilitaires pour la sécurité fonctionnent en interne.
|
||||
|
||||
///
|
||||
|
||||
## Dépendances avec `yield`, `HTTPException`, `except` et tâches d'arrière‑plan { #dependencies-with-yield-httpexception-except-and-background-tasks }
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Vous n’avez très probablement pas besoin de ces détails techniques.
|
||||
|
||||
Ces détails sont utiles principalement si vous aviez une application FastAPI antérieure à la version 0.121.0 et que vous rencontrez des problèmes avec des dépendances utilisant `yield`.
|
||||
|
||||
///
|
||||
|
||||
Les dépendances avec `yield` ont évolué au fil du temps pour couvrir différents cas d’utilisation et corriger certains problèmes ; voici un résumé de ce qui a changé.
|
||||
|
||||
### Dépendances avec `yield` et `scope` { #dependencies-with-yield-and-scope }
|
||||
|
||||
Dans la version 0.121.0, **FastAPI** a ajouté la prise en charge de `Depends(scope="function")` pour les dépendances avec `yield`.
|
||||
|
||||
Avec `Depends(scope="function")`, le code d’arrêt après `yield` s’exécute immédiatement après la fin de la *fonction de chemin d'accès*, avant que la réponse ne soit renvoyée au client.
|
||||
|
||||
Et lorsque vous utilisez `Depends(scope="request")` (valeur par défaut), le code d’arrêt après `yield` s’exécute après l’envoi de la réponse.
|
||||
|
||||
Vous pouvez en lire davantage dans les documents pour [Dépendances avec `yield` - Sortie anticipée et `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
|
||||
|
||||
### Dépendances avec `yield` et `StreamingResponse`, Détails techniques { #dependencies-with-yield-and-streamingresponse-technical-details }
|
||||
|
||||
Avant FastAPI 0.118.0, si vous utilisiez une dépendance avec `yield`, elle exécutait le code d’arrêt après que la *fonction de chemin d'accès* a retourné, mais juste avant d’envoyer la réponse.
|
||||
|
||||
L’objectif était d’éviter de conserver des ressources plus longtemps que nécessaire pendant que la réponse transitait sur le réseau.
|
||||
|
||||
Ce changement impliquait aussi que si vous retourniez une `StreamingResponse`, le code d’arrêt de la dépendance avec `yield` aurait déjà été exécuté.
|
||||
|
||||
Par exemple, si vous aviez une session de base de données dans une dépendance avec `yield`, la `StreamingResponse` ne pourrait pas utiliser cette session pendant le streaming des données, car la session aurait déjà été fermée dans le code d’arrêt après `yield`.
|
||||
|
||||
Ce comportement a été annulé en 0.118.0, afin que le code d’arrêt après `yield` s’exécute après l’envoi de la réponse.
|
||||
|
||||
/// info
|
||||
|
||||
Comme vous le verrez ci‑dessous, c’est très similaire au comportement avant la version 0.106.0, mais avec plusieurs améliorations et corrections de bogues pour des cas limites.
|
||||
|
||||
///
|
||||
|
||||
#### Cas d’utilisation avec sortie anticipée du code { #use-cases-with-early-exit-code }
|
||||
|
||||
Il existe certains cas d’utilisation avec des conditions spécifiques qui pourraient bénéficier de l’ancien comportement, où le code d’arrêt des dépendances avec `yield` s’exécute avant l’envoi de la réponse.
|
||||
|
||||
Par exemple, imaginez que vous ayez du code qui utilise une session de base de données dans une dépendance avec `yield` uniquement pour vérifier un utilisateur, mais que la session de base de données ne soit plus jamais utilisée dans la *fonction de chemin d'accès*, seulement dans la dépendance, et que la réponse mette longtemps à être envoyée, comme une `StreamingResponse` qui envoie les données lentement mais qui, pour une raison quelconque, n’utilise pas la base de données.
|
||||
|
||||
Dans ce cas, la session de base de données serait conservée jusqu’à la fin de l’envoi de la réponse, mais si vous ne l’utilisez pas, il ne serait pas nécessaire de la conserver.
|
||||
|
||||
Voici à quoi cela pourrait ressembler :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
|
||||
|
||||
Le code d’arrêt, la fermeture automatique de la `Session` dans :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
|
||||
|
||||
... serait exécuté après que la réponse a fini d’envoyer les données lentes :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
|
||||
|
||||
Mais comme `generate_stream()` n’utilise pas la session de base de données, il n’est pas vraiment nécessaire de garder la session ouverte pendant l’envoi de la réponse.
|
||||
|
||||
Si vous avez ce cas d’utilisation spécifique avec SQLModel (ou SQLAlchemy), vous pouvez fermer explicitement la session dès que vous n’en avez plus besoin :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
|
||||
|
||||
De cette manière, la session libérera la connexion à la base de données, afin que d’autres requêtes puissent l’utiliser.
|
||||
|
||||
Si vous avez un autre cas d’utilisation qui nécessite une sortie anticipée depuis une dépendance avec `yield`, veuillez créer une <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">Question de discussion GitHub</a> avec votre cas spécifique et pourquoi vous bénéficieriez d’une fermeture anticipée pour les dépendances avec `yield`.
|
||||
|
||||
S’il existe des cas d’utilisation convaincants pour une fermeture anticipée dans les dépendances avec `yield`, j’envisagerai d’ajouter une nouvelle façon d’y opter.
|
||||
|
||||
### Dépendances avec `yield` et `except`, Détails techniques { #dependencies-with-yield-and-except-technical-details }
|
||||
|
||||
Avant FastAPI 0.110.0, si vous utilisiez une dépendance avec `yield`, puis capturiez une exception avec `except` dans cette dépendance, et que vous ne relanciez pas l’exception, l’exception était automatiquement levée/transmise à tout gestionnaire d’exceptions ou au gestionnaire d’erreur interne du serveur.
|
||||
|
||||
Cela a été modifié dans la version 0.110.0 pour corriger une consommation de mémoire non gérée due aux exceptions transmises sans gestionnaire (erreurs internes du serveur), et pour rendre le comportement cohérent avec celui du code Python classique.
|
||||
|
||||
### Tâches d'arrière‑plan et dépendances avec `yield`, Détails techniques { #background-tasks-and-dependencies-with-yield-technical-details }
|
||||
|
||||
Avant FastAPI 0.106.0, lever des exceptions après `yield` n’était pas possible, le code d’arrêt dans les dépendances avec `yield` s’exécutait après l’envoi de la réponse, donc les [Gestionnaires d'exceptions](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} avaient déjà été exécutés.
|
||||
|
||||
Cela avait été conçu ainsi principalement pour permettre d’utiliser les mêmes objets « générés par yield » par les dépendances à l’intérieur de tâches d’arrière‑plan, car le code d’arrêt s’exécutait après la fin des tâches d’arrière‑plan.
|
||||
|
||||
Cela a été modifié dans FastAPI 0.106.0 afin de ne pas conserver des ressources pendant l’attente de la transmission de la réponse sur le réseau.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
De plus, une tâche d’arrière‑plan est normalement un ensemble de logique indépendant qui devrait être géré séparément, avec ses propres ressources (par ex. sa propre connexion à la base de données).
|
||||
|
||||
Ainsi, vous aurez probablement un code plus propre.
|
||||
|
||||
///
|
||||
|
||||
Si vous comptiez sur ce comportement, vous devez désormais créer les ressources pour les tâches d’arrière‑plan à l’intérieur de la tâche elle‑même, et n’utiliser en interne que des données qui ne dépendent pas des ressources des dépendances avec `yield`.
|
||||
|
||||
Par exemple, au lieu d’utiliser la même session de base de données, vous créeriez une nouvelle session de base de données à l’intérieur de la tâche d’arrière‑plan, et vous obtiendriez les objets depuis la base de données en utilisant cette nouvelle session. Puis, au lieu de passer l’objet obtenu depuis la base de données en paramètre à la fonction de tâche d’arrière‑plan, vous passeriez l’identifiant (ID) de cet objet et vous obtiendriez à nouveau l’objet à l’intérieur de la fonction de la tâche d’arrière‑plan.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Types Python avancés { #advanced-python-types }
|
||||
|
||||
Voici quelques idées supplémentaires qui peuvent être utiles lorsque vous travaillez avec les types Python.
|
||||
|
||||
## Utiliser `Union` ou `Optional` { #using-union-or-optional }
|
||||
|
||||
Si votre code ne peut pas utiliser `|` pour une raison quelconque, par exemple si ce n'est pas dans une annotation de type mais dans quelque chose comme `response_model=`, au lieu d'utiliser la barre verticale (`|`) vous pouvez utiliser `Union` de `typing`.
|
||||
|
||||
Par exemple, vous pourriez déclarer que quelque chose peut être un `str` ou `None` :
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` propose également un raccourci pour déclarer que quelque chose peut être `None`, avec `Optional`.
|
||||
|
||||
Voici un conseil issu de mon point de vue très subjectif :
|
||||
|
||||
- 🚨 Évitez d'utiliser `Optional[SomeType]`
|
||||
- À la place ✨ **utilisez `Union[SomeType, None]`** ✨.
|
||||
|
||||
Les deux sont équivalents et, en interne, identiques, mais je recommande `Union` plutôt que `Optional` parce que le mot « optional » semble impliquer que la valeur est facultative, alors qu'il signifie en réalité « elle peut être `None` », même si elle n'est pas facultative et reste requise.
|
||||
|
||||
Je pense que `Union[SomeType, None]` est plus explicite quant à sa signification.
|
||||
|
||||
Il ne s'agit que des mots et des noms. Mais ces mots peuvent influencer la manière dont vous et vos coéquipiers pensez au code.
|
||||
|
||||
À titre d'exemple, prenons cette fonction :
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Le paramètre `name` est défini comme `Optional[str]`, mais il n'est pas facultatif, vous ne pouvez pas appeler la fonction sans le paramètre :
|
||||
|
||||
```Python
|
||||
say_hi() # Oh non, cela lève une erreur ! 😱
|
||||
```
|
||||
|
||||
Le paramètre `name` est toujours requis (pas facultatif) car il n'a pas de valeur par défaut. En revanche, `name` accepte `None` comme valeur :
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Ceci fonctionne, None est valide 🎉
|
||||
```
|
||||
|
||||
La bonne nouvelle, c'est que, dans la plupart des cas, vous pourrez simplement utiliser `|` pour définir des unions de types :
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Ainsi, normalement, vous n'avez pas à vous préoccuper de noms comme `Optional` et `Union`. 😎
|
||||
@@ -0,0 +1,99 @@
|
||||
# Tests asynchrones { #async-tests }
|
||||
|
||||
Vous avez déjà vu comment tester vos applications **FastAPI** en utilisant le `TestClient` fourni. Jusqu'à présent, vous n'avez vu que comment écrire des tests synchrones, sans utiliser de fonctions `async`.
|
||||
|
||||
Pouvoir utiliser des fonctions asynchrones dans vos tests peut être utile, par exemple lorsque vous interrogez votre base de données de manière asynchrone. Imaginez que vous vouliez tester l'envoi de requêtes à votre application FastAPI puis vérifier que votre backend a bien écrit les bonnes données dans la base, tout en utilisant une bibliothèque de base de données asynchrone.
|
||||
|
||||
Voyons comment procéder.
|
||||
|
||||
## pytest.mark.anyio { #pytest-mark-anyio }
|
||||
|
||||
Si nous voulons appeler des fonctions asynchrones dans nos tests, nos fonctions de test doivent être asynchrones. AnyIO fournit un plug-in pratique qui nous permet d'indiquer que certaines fonctions de test doivent être appelées de manière asynchrone.
|
||||
|
||||
## HTTPX { #httpx }
|
||||
|
||||
Même si votre application **FastAPI** utilise des fonctions `def` normales au lieu de `async def`, c'est toujours une application `async` en interne.
|
||||
|
||||
Le `TestClient` fait un peu de magie pour appeler l'application FastAPI asynchrone depuis vos fonctions de test `def` normales, en utilisant pytest standard. Mais cette magie ne fonctionne plus lorsque nous l'utilisons dans des fonctions asynchrones. En exécutant nos tests de manière asynchrone, nous ne pouvons plus utiliser le `TestClient` dans nos fonctions de test.
|
||||
|
||||
Le `TestClient` est basé sur <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> et, heureusement, nous pouvons l'utiliser directement pour tester l'API.
|
||||
|
||||
## Exemple { #example }
|
||||
|
||||
Pour un exemple simple, considérons une structure de fichiers similaire à celle décrite dans [Applications plus grandes](../tutorial/bigger-applications.md){.internal-link target=_blank} et [Tests](../tutorial/testing.md){.internal-link target=_blank} :
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Le fichier `main.py` contiendrait :
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
Le fichier `test_main.py` contiendrait les tests pour `main.py`, il pourrait maintenant ressembler à ceci :
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## Exécuter { #run-it }
|
||||
|
||||
Vous pouvez lancer vos tests comme d'habitude via :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pytest
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## En détail { #in-detail }
|
||||
|
||||
Le marqueur `@pytest.mark.anyio` indique à pytest que cette fonction de test doit être appelée de manière asynchrone :
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Notez que la fonction de test est maintenant `async def` au lieu de simplement `def` comme auparavant avec le `TestClient`.
|
||||
|
||||
///
|
||||
|
||||
Nous pouvons ensuite créer un `AsyncClient` avec l'application et lui envoyer des requêtes asynchrones en utilisant `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
C'est l'équivalent de :
|
||||
|
||||
```Python
|
||||
response = client.get('/')
|
||||
```
|
||||
|
||||
... que nous utilisions pour faire nos requêtes avec le `TestClient`.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Notez que nous utilisons async/await avec le nouveau `AsyncClient` — la requête est asynchrone.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Si votre application s'appuie sur des événements de cycle de vie (lifespan), le `AsyncClient` ne déclenchera pas ces événements. Pour vous assurer qu'ils sont déclenchés, utilisez `LifespanManager` depuis <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>.
|
||||
|
||||
///
|
||||
|
||||
## Autres appels de fonctions asynchrones { #other-asynchronous-function-calls }
|
||||
|
||||
Comme la fonction de test est désormais asynchrone, vous pouvez également appeler (et `await`) d'autres fonctions `async` en plus d'envoyer des requêtes à votre application FastAPI dans vos tests, exactement comme vous le feriez ailleurs dans votre code.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous rencontrez une erreur `RuntimeError: Task attached to a different loop` lors de l'intégration d'appels de fonctions asynchrones dans vos tests (par exemple en utilisant <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MotorClient de MongoDB</a>), n'oubliez pas d'instancier les objets qui ont besoin d'une boucle d'événements uniquement dans des fonctions async, par exemple dans un callback `@app.on_event("startup")`.
|
||||
|
||||
///
|
||||
@@ -0,0 +1,466 @@
|
||||
# Être derrière un proxy { #behind-a-proxy }
|
||||
|
||||
Dans de nombreuses situations, vous utiliserez un **proxy** comme Traefik ou Nginx devant votre application FastAPI.
|
||||
|
||||
Ces proxies peuvent gérer les certificats HTTPS et d'autres aspects.
|
||||
|
||||
## En-têtes transférés par le proxy { #proxy-forwarded-headers }
|
||||
|
||||
Un **proxy** placé devant votre application définit normalement certains en-têtes à la volée avant d'envoyer les requêtes à votre **serveur**, afin d'indiquer au serveur que la requête a été **transférée** par le proxy, en lui donnant l'URL d'origine (publique), y compris le domaine, le fait qu'elle utilise HTTPS, etc.
|
||||
|
||||
Le programme **serveur** (par exemple **Uvicorn** via **FastAPI CLI**) est capable d'interpréter ces en‑têtes, puis de transmettre ces informations à votre application.
|
||||
|
||||
Mais, par sécurité, comme le serveur ne sait pas qu'il se trouve derrière un proxy de confiance, il n'interprétera pas ces en‑têtes.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
Les en-têtes du proxy sont :
|
||||
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
|
||||
|
||||
///
|
||||
|
||||
### Activer les en-têtes transférés par le proxy { #enable-proxy-forwarded-headers }
|
||||
|
||||
Vous pouvez démarrer FastAPI CLI avec l'option de CLI `--forwarded-allow-ips` et fournir les adresses IP à considérer comme fiables pour lire ces en‑têtes transférés.
|
||||
|
||||
Si vous la définissez à `--forwarded-allow-ips="*"`, elle fera confiance à toutes les IP entrantes.
|
||||
|
||||
Si votre **serveur** est derrière un **proxy** de confiance et que seul le proxy lui parle, cela fera accepter l'IP de ce **proxy**, quelle qu'elle soit.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Redirections avec HTTPS { #redirects-with-https }
|
||||
|
||||
Par exemple, disons que vous définissez un *chemin d'accès* `/items/` :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
Si le client essaie d'aller à `/items`, par défaut, il sera redirigé vers `/items/`.
|
||||
|
||||
Mais avant de définir l'option de CLI `--forwarded-allow-ips`, il pourrait rediriger vers `http://localhost:8000/items/`.
|
||||
|
||||
Mais peut‑être que votre application est hébergée à `https://mysuperapp.com`, et la redirection devrait être vers `https://mysuperapp.com/items/`.
|
||||
|
||||
En définissant `--proxy-headers`, FastAPI pourra désormais rediriger vers l'emplacement correct. 😎
|
||||
|
||||
```
|
||||
https://mysuperapp.com/items/
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous voulez en savoir plus sur HTTPS, consultez le guide [À propos de HTTPS](../deployment/https.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Comment fonctionnent les en‑têtes transférés par le proxy { #how-proxy-forwarded-headers-work }
|
||||
|
||||
Voici une représentation visuelle de la façon dont le **proxy** ajoute des en‑têtes transférés entre le client et le **serveur d'application** :
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Proxy as Proxy/Load Balancer
|
||||
participant Server as FastAPI Server
|
||||
|
||||
Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Proxy: Proxy adds forwarded headers
|
||||
|
||||
Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Server: Server interprets headers<br/>(if --forwarded-allow-ips is set)
|
||||
|
||||
Server->>Proxy: HTTP Response<br/>with correct HTTPS URLs
|
||||
|
||||
Proxy->>Client: HTTPS Response
|
||||
```
|
||||
|
||||
Le **proxy** intercepte la requête client d'origine et ajoute les en-têtes spéciaux *forwarded* (`X-Forwarded-*`) avant de transmettre la requête au **serveur d'application**.
|
||||
|
||||
Ces en‑têtes conservent des informations sur la requête d'origine qui seraient autrement perdues :
|
||||
|
||||
* **X-Forwarded-For** : l'adresse IP du client d'origine
|
||||
* **X-Forwarded-Proto** : le protocole d'origine (`https`)
|
||||
* **X-Forwarded-Host** : l'hôte d'origine (`mysuperapp.com`)
|
||||
|
||||
Lorsque **FastAPI CLI** est configurée avec `--forwarded-allow-ips`, elle fait confiance à ces en‑têtes et les utilise, par exemple pour générer les bonnes URL dans les redirections.
|
||||
|
||||
## Proxy avec un préfixe de chemin supprimé { #proxy-with-a-stripped-path-prefix }
|
||||
|
||||
Vous pouvez avoir un proxy qui ajoute un préfixe de chemin à votre application.
|
||||
|
||||
Dans ces cas, vous pouvez utiliser `root_path` pour configurer votre application.
|
||||
|
||||
Le `root_path` est un mécanisme fourni par la spécification ASGI (sur laquelle FastAPI est construit, via Starlette).
|
||||
|
||||
Le `root_path` est utilisé pour gérer ces cas spécifiques.
|
||||
|
||||
Et il est également utilisé en interne lors du montage de sous‑applications.
|
||||
|
||||
Avoir un proxy avec un préfixe de chemin supprimé, dans ce cas, signifie que vous pourriez déclarer un chemin à `/app` dans votre code, mais ensuite, vous ajoutez une couche au‑dessus (le proxy) qui place votre application **FastAPI** sous un chemin comme `/api/v1`.
|
||||
|
||||
Dans ce cas, le chemin original `/app` serait en réalité servi à `/api/v1/app`.
|
||||
|
||||
Même si tout votre code est écrit en supposant qu'il n'y a que `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
Et le proxy **« stripping »** le **préfixe de chemin** à la volée avant de transmettre la requête au serveur de l'application (probablement Uvicorn via FastAPI CLI), en gardant votre application convaincue qu'elle est servie à `/app`, afin que vous n'ayez pas à mettre à jour tout votre code pour inclure le préfixe `/api/v1`.
|
||||
|
||||
Jusqu'ici, tout fonctionnerait normalement.
|
||||
|
||||
Mais ensuite, lorsque vous ouvrez l'interface de documentation intégrée (le frontend), elle s'attendra à obtenir le schéma OpenAPI à `/openapi.json`, au lieu de `/api/v1/openapi.json`.
|
||||
|
||||
Ainsi, le frontend (qui s'exécute dans le navigateur) essaiera d'atteindre `/openapi.json` et ne pourra pas obtenir le schéma OpenAPI.
|
||||
|
||||
Parce que nous avons un proxy avec un préfixe de chemin `/api/v1` pour notre application, le frontend doit récupérer le schéma OpenAPI à `/api/v1/openapi.json`.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
|
||||
browser("Browser")
|
||||
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
|
||||
server["Server on http://127.0.0.1:8000/app"]
|
||||
|
||||
browser --> proxy
|
||||
proxy --> server
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
L'IP `0.0.0.0` est couramment utilisée pour signifier que le programme écoute sur toutes les IP disponibles de cette machine/serveur.
|
||||
|
||||
///
|
||||
|
||||
L'interface de documents doit également indiquer dans le schéma OpenAPI que ce `server` d'API se trouve à `/api/v1` (derrière le proxy). Par exemple :
|
||||
|
||||
```JSON hl_lines="4-8"
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
// Plus d'éléments ici
|
||||
"servers": [
|
||||
{
|
||||
"url": "/api/v1"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
// Plus d'éléments ici
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dans cet exemple, le « Proxy » pourrait être quelque chose comme **Traefik**. Et le serveur serait quelque chose comme FastAPI CLI avec **Uvicorn**, exécutant votre application FastAPI.
|
||||
|
||||
### Fournir le `root_path` { #providing-the-root-path }
|
||||
|
||||
Pour y parvenir, vous pouvez utiliser l'option de ligne de commande `--root-path` comme suit :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Si vous utilisez Hypercorn, il dispose également de l'option `--root-path`.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
La spécification ASGI définit un `root_path` pour ce cas d'usage.
|
||||
|
||||
Et l'option de ligne de commande `--root-path` fournit ce `root_path`.
|
||||
|
||||
///
|
||||
|
||||
### Vérifier le `root_path` actuel { #checking-the-current-root-path }
|
||||
|
||||
Vous pouvez obtenir le `root_path` actuel utilisé par votre application pour chaque requête, il fait partie du dictionnaire `scope` (qui fait partie de la spécification ASGI).
|
||||
|
||||
Ici, nous l'incluons dans le message uniquement à des fins de démonstration.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Ensuite, si vous démarrez Uvicorn avec :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
La réponse sera semblable à :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
### Définir le `root_path` dans l'application FastAPI { #setting-the-root-path-in-the-fastapi-app }
|
||||
|
||||
Autrement, si vous n'avez pas la possibilité de fournir une option de ligne de commande comme `--root-path` ou équivalent, vous pouvez définir le paramètre `root_path` lors de la création de votre application FastAPI :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Passer le `root_path` à `FastAPI` équivaut à passer l'option de ligne de commande `--root-path` à Uvicorn ou Hypercorn.
|
||||
|
||||
### À propos de `root_path` { #about-root-path }
|
||||
|
||||
Gardez à l'esprit que le serveur (Uvicorn) n'utilisera ce `root_path` que pour le transmettre à l'application.
|
||||
|
||||
Mais si vous allez avec votre navigateur sur <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, vous verrez la réponse normale :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
Donc, il ne s'attendra pas à être accessible à `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
Uvicorn s'attendra à ce que le proxy accède à Uvicorn sur `http://127.0.0.1:8000/app`, et ce sera ensuite la responsabilité du proxy d'ajouter le préfixe supplémentaire `/api/v1` au‑dessus.
|
||||
|
||||
## À propos des proxies avec un préfixe de chemin supprimé { #about-proxies-with-a-stripped-path-prefix }
|
||||
|
||||
Gardez à l'esprit qu'un proxy avec préfixe de chemin supprimé n'est qu'une des façons de le configurer.
|
||||
|
||||
Dans de nombreux cas, la valeur par défaut sera probablement que le proxy n'a pas de préfixe de chemin supprimé.
|
||||
|
||||
Dans un cas comme celui‑ci (sans préfixe de chemin supprimé), le proxy écoutera sur quelque chose comme `https://myawesomeapp.com`, puis si le navigateur va sur `https://myawesomeapp.com/api/v1/app` et que votre serveur (par ex. Uvicorn) écoute sur `http://127.0.0.1:8000`, le proxy (sans préfixe de chemin supprimé) accédera à Uvicorn au même chemin : `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
## Tester localement avec Traefik { #testing-locally-with-traefik }
|
||||
|
||||
Vous pouvez facilement faire l'expérience en local avec un préfixe de chemin supprimé en utilisant <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
|
||||
|
||||
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Téléchargez Traefik</a> ; c'est un binaire unique, vous pouvez extraire le fichier compressé et l'exécuter directement depuis le terminal.
|
||||
|
||||
Créez ensuite un fichier `traefik.toml` avec :
|
||||
|
||||
```TOML hl_lines="3"
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":9999"
|
||||
|
||||
[providers]
|
||||
[providers.file]
|
||||
filename = "routes.toml"
|
||||
```
|
||||
|
||||
Cela indique à Traefik d'écouter sur le port 9999 et d'utiliser un autre fichier `routes.toml`.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Nous utilisons le port 9999 au lieu du port HTTP standard 80 afin que vous n'ayez pas à l'exécuter avec des privilèges administrateur (`sudo`).
|
||||
|
||||
///
|
||||
|
||||
Créez maintenant cet autre fichier `routes.toml` :
|
||||
|
||||
```TOML hl_lines="5 12 20"
|
||||
[http]
|
||||
[http.middlewares]
|
||||
|
||||
[http.middlewares.api-stripprefix.stripPrefix]
|
||||
prefixes = ["/api/v1"]
|
||||
|
||||
[http.routers]
|
||||
|
||||
[http.routers.app-http]
|
||||
entryPoints = ["http"]
|
||||
service = "app"
|
||||
rule = "PathPrefix(`/api/v1`)"
|
||||
middlewares = ["api-stripprefix"]
|
||||
|
||||
[http.services]
|
||||
|
||||
[http.services.app]
|
||||
[http.services.app.loadBalancer]
|
||||
[[http.services.app.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:8000"
|
||||
```
|
||||
|
||||
Ce fichier configure Traefik pour utiliser le préfixe de chemin `/api/v1`.
|
||||
|
||||
Puis Traefik redirigera ses requêtes vers votre Uvicorn tournant sur `http://127.0.0.1:8000`.
|
||||
|
||||
Démarrez maintenant Traefik :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ ./traefik --configFile=traefik.toml
|
||||
|
||||
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Et démarrez maintenant votre application, en utilisant l'option `--root-path` :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Vérifier les réponses { #check-the-responses }
|
||||
|
||||
Maintenant, si vous allez à l'URL avec le port pour Uvicorn : <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, vous verrez la réponse normale :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez que même si vous y accédez via `http://127.0.0.1:8000/app`, il affiche le `root_path` de `/api/v1`, repris depuis l'option `--root-path`.
|
||||
|
||||
///
|
||||
|
||||
Et maintenant ouvrez l'URL avec le port pour Traefik, en incluant le préfixe de chemin : <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>.
|
||||
|
||||
Nous obtenons la même réponse :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"message": "Hello World",
|
||||
"root_path": "/api/v1"
|
||||
}
|
||||
```
|
||||
|
||||
mais cette fois à l'URL avec le préfixe fourni par le proxy : `/api/v1`.
|
||||
|
||||
Bien sûr, l'idée ici est que tout le monde accède à l'application via le proxy ; la version avec le préfixe de chemin `/api/v1` est donc la « correcte ».
|
||||
|
||||
Et la version sans préfixe de chemin (`http://127.0.0.1:8000/app`), fournie directement par Uvicorn, serait exclusivement destinée au _proxy_ (Traefik) pour y accéder.
|
||||
|
||||
Cela montre comment le Proxy (Traefik) utilise le préfixe de chemin et comment le serveur (Uvicorn) utilise le `root_path` fourni par l'option `--root-path`.
|
||||
|
||||
### Vérifier l'interface de documentation { #check-the-docs-ui }
|
||||
|
||||
Mais voici la partie intéressante. ✨
|
||||
|
||||
La manière « officielle » d'accéder à l'application serait via le proxy avec le préfixe de chemin que nous avons défini. Donc, comme on s'y attend, si vous essayez l'interface de documentation servie directement par Uvicorn, sans le préfixe de chemin dans l'URL, cela ne fonctionne pas, car elle s'attend à être accédée via le proxy.
|
||||
|
||||
Vous pouvez le vérifier sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> :
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image01.png">
|
||||
|
||||
Mais si nous accédons à l'interface de documents à l'URL « officielle » en utilisant le proxy avec le port `9999`, à `/api/v1/docs`, cela fonctionne correctement ! 🎉
|
||||
|
||||
Vous pouvez le vérifier sur <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> :
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image02.png">
|
||||
|
||||
Exactement comme nous le voulions. ✔️
|
||||
|
||||
C'est parce que FastAPI utilise ce `root_path` pour créer le `server` par défaut dans OpenAPI avec l'URL fournie par `root_path`.
|
||||
|
||||
## Serveurs supplémentaires { #additional-servers }
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Ceci est un cas d'utilisation plus avancé. N'hésitez pas à l'ignorer.
|
||||
|
||||
///
|
||||
|
||||
Par défaut, **FastAPI** créera un `server` dans le schéma OpenAPI avec l'URL correspondant au `root_path`.
|
||||
|
||||
Mais vous pouvez aussi fournir d'autres `servers` alternatifs, par exemple si vous voulez que la même interface de documents interagisse avec un environnement de staging et un environnement de production.
|
||||
|
||||
Si vous passez une liste personnalisée de `servers` et qu'il y a un `root_path` (parce que votre API vit derrière un proxy), **FastAPI** insérera un « server » avec ce `root_path` au début de la liste.
|
||||
|
||||
Par exemple :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
Générera un schéma OpenAPI comme :
|
||||
|
||||
```JSON hl_lines="5-7"
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
// Plus d'éléments ici
|
||||
"servers": [
|
||||
{
|
||||
"url": "/api/v1"
|
||||
},
|
||||
{
|
||||
"url": "https://stag.example.com",
|
||||
"description": "Staging environment"
|
||||
},
|
||||
{
|
||||
"url": "https://prod.example.com",
|
||||
"description": "Production environment"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
// Plus d'éléments ici
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez le serveur généré automatiquement avec une valeur `url` de `/api/v1`, reprise depuis le `root_path`.
|
||||
|
||||
///
|
||||
|
||||
Dans l'interface de documents sur <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>, cela ressemblera à ceci :
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image03.png">
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
L'interface de documents interagit avec le serveur que vous sélectionnez.
|
||||
|
||||
///
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
La propriété `servers` dans la spécification OpenAPI est facultative.
|
||||
|
||||
Si vous ne spécifiez pas le paramètre `servers` et que `root_path` est égal à `/`, la propriété `servers` dans le schéma OpenAPI généré sera entièrement omise par défaut, ce qui équivaut à un seul serveur avec une valeur `url` de `/`.
|
||||
|
||||
///
|
||||
|
||||
### Désactiver le serveur automatique issu de `root_path` { #disable-automatic-server-from-root-path }
|
||||
|
||||
Si vous ne voulez pas que **FastAPI** inclue un serveur automatique utilisant le `root_path`, vous pouvez utiliser le paramètre `root_path_in_servers=False` :
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
et il ne l'inclura alors pas dans le schéma OpenAPI.
|
||||
|
||||
## Monter une sous-application { #mounting-a-sub-application }
|
||||
|
||||
Si vous avez besoin de monter une sous‑application (comme décrit dans [Sous‑applications - montages](sub-applications.md){.internal-link target=_blank}) tout en utilisant un proxy avec `root_path`, vous pouvez le faire normalement, comme vous vous y attendez.
|
||||
|
||||
FastAPI utilisera intelligemment le `root_path` en interne, donc cela fonctionnera simplement. ✨
|
||||
@@ -0,0 +1,312 @@
|
||||
# Réponse personnalisée - HTML, flux, fichier, autres { #custom-response-html-stream-file-others }
|
||||
|
||||
Par défaut, **FastAPI** renverra les réponses en utilisant `JSONResponse`.
|
||||
|
||||
Vous pouvez le remplacer en renvoyant directement une `Response` comme expliqué dans [Renvoyer directement une Response](response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
Mais si vous renvoyez directement une `Response` (ou n'importe quelle sous-classe, comme `JSONResponse`), les données ne seront pas automatiquement converties (même si vous déclarez un `response_model`), et la documentation ne sera pas générée automatiquement (par exemple, l'inclusion du « media type » dans l'en-tête HTTP `Content-Type` comme partie de l'OpenAPI généré).
|
||||
|
||||
Vous pouvez aussi déclarer la `Response` que vous voulez utiliser (par ex. toute sous-classe de `Response`), dans le décorateur de chemin d'accès en utilisant le paramètre `response_class`.
|
||||
|
||||
Le contenu que vous renvoyez depuis votre fonction de chemin d'accès sera placé à l'intérieur de cette `Response`.
|
||||
|
||||
Et si cette `Response` a un « media type » JSON (`application/json`), comme c'est le cas avec `JSONResponse` et `UJSONResponse`, les données que vous renvoyez seront automatiquement converties (et filtrées) avec tout `response_model` Pydantic que vous avez déclaré dans le décorateur de chemin d'accès.
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Si vous utilisez une classe de réponse sans « media type », FastAPI s'attendra à ce que votre réponse n'ait pas de contenu ; il ne documentera donc pas le format de la réponse dans les documents OpenAPI générés.
|
||||
|
||||
///
|
||||
|
||||
## Utiliser `ORJSONResponse` { #use-orjsonresponse }
|
||||
|
||||
Par exemple, si vous cherchez à maximiser la performance, vous pouvez installer et utiliser <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> et définir la réponse sur `ORJSONResponse`.
|
||||
|
||||
Importez la classe (sous-classe) `Response` que vous voulez utiliser et déclarez-la dans le décorateur de chemin d'accès.
|
||||
|
||||
Pour de grandes réponses, renvoyer directement une `Response` est bien plus rapide que de renvoyer un dictionnaire.
|
||||
|
||||
Cela vient du fait que, par défaut, FastAPI inspectera chaque élément et s'assurera qu'il est sérialisable en JSON, en utilisant le même [Encodeur compatible JSON](../tutorial/encoder.md){.internal-link target=_blank} expliqué dans le didacticiel. C'est ce qui vous permet de renvoyer des objets arbitraires, par exemple des modèles de base de données.
|
||||
|
||||
Mais si vous êtes certain que le contenu que vous renvoyez est sérialisable en JSON, vous pouvez le passer directement à la classe de réponse et éviter le surcoût supplémentaire qu'aurait FastAPI en faisant passer votre contenu de retour par le `jsonable_encoder` avant de le transmettre à la classe de réponse.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info
|
||||
|
||||
Le paramètre `response_class` sera aussi utilisé pour définir le « media type » de la réponse.
|
||||
|
||||
Dans ce cas, l'en-tête HTTP `Content-Type` sera défini à `application/json`.
|
||||
|
||||
Et il sera documenté comme tel dans OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
`ORJSONResponse` est disponible uniquement dans FastAPI, pas dans Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Réponse HTML { #html-response }
|
||||
|
||||
Pour renvoyer une réponse avec du HTML directement depuis **FastAPI**, utilisez `HTMLResponse`.
|
||||
|
||||
- Importez `HTMLResponse`.
|
||||
- Passez `HTMLResponse` comme paramètre `response_class` de votre décorateur de chemin d'accès.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info
|
||||
|
||||
Le paramètre `response_class` sera aussi utilisé pour définir le « media type » de la réponse.
|
||||
|
||||
Dans ce cas, l'en-tête HTTP `Content-Type` sera défini à `text/html`.
|
||||
|
||||
Et il sera documenté comme tel dans OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
### Renvoyer une `Response` { #return-a-response }
|
||||
|
||||
Comme vu dans [Renvoyer directement une Response](response-directly.md){.internal-link target=_blank}, vous pouvez aussi remplacer la réponse directement dans votre chemin d'accès, en la renvoyant.
|
||||
|
||||
Le même exemple ci-dessus, renvoyant une `HTMLResponse`, pourrait ressembler à :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Une `Response` renvoyée directement par votre fonction de chemin d'accès ne sera pas documentée dans OpenAPI (par exemple, le `Content-Type` ne sera pas documenté) et ne sera pas visible dans les documents interactifs automatiques.
|
||||
|
||||
///
|
||||
|
||||
/// info
|
||||
|
||||
Bien sûr, l'en-tête `Content-Type` réel, le code d'état, etc., proviendront de l'objet `Response` que vous avez renvoyé.
|
||||
|
||||
///
|
||||
|
||||
### Documenter dans OpenAPI et remplacer `Response` { #document-in-openapi-and-override-response }
|
||||
|
||||
Si vous voulez remplacer la réponse depuis l'intérieur de la fonction mais en même temps documenter le « media type » dans OpenAPI, vous pouvez utiliser le paramètre `response_class` ET renvoyer un objet `Response`.
|
||||
|
||||
`response_class` sera alors utilisé uniquement pour documenter l'opération de chemin d'accès OpenAPI, mais votre `Response` sera utilisée telle quelle.
|
||||
|
||||
#### Renvoyer directement une `HTMLResponse` { #return-an-htmlresponse-directly }
|
||||
|
||||
Par exemple, cela pourrait être quelque chose comme :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
Dans cet exemple, la fonction `generate_html_response()` génère déjà et renvoie une `Response` au lieu de renvoyer le HTML dans une `str`.
|
||||
|
||||
En renvoyant le résultat de l'appel à `generate_html_response()`, vous renvoyez déjà une `Response` qui remplacera le comportement par défaut de **FastAPI**.
|
||||
|
||||
Mais comme vous avez aussi passé `HTMLResponse` dans `response_class`, **FastAPI** saura comment la documenter dans OpenAPI et les documents interactifs comme HTML avec `text/html` :
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## Réponses disponibles { #available-responses }
|
||||
|
||||
Voici certaines des réponses disponibles.
|
||||
|
||||
Gardez à l'esprit que vous pouvez utiliser `Response` pour renvoyer autre chose, ou même créer une sous-classe personnalisée.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
Vous pourriez aussi utiliser `from starlette.responses import HTMLResponse`.
|
||||
|
||||
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` simplement pour votre confort de développement. Mais la plupart des réponses disponibles viennent directement de Starlette.
|
||||
|
||||
///
|
||||
|
||||
### `Response` { #response }
|
||||
|
||||
La classe principale `Response`, toutes les autres réponses en héritent.
|
||||
|
||||
Vous pouvez la renvoyer directement.
|
||||
|
||||
Elle accepte les paramètres suivants :
|
||||
|
||||
- `content` - Une `str` ou des `bytes`.
|
||||
- `status_code` - Un code d'état HTTP de type `int`.
|
||||
- `headers` - Un `dict` de chaînes.
|
||||
- `media_type` - Une `str` donnant le media type. Par exemple « text/html ».
|
||||
|
||||
FastAPI (en fait Starlette) inclura automatiquement un en-tête Content-Length. Il inclura aussi un en-tête Content-Type, basé sur `media_type` et en ajoutant un charset pour les types textuels.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
Prend du texte ou des octets et renvoie une réponse HTML, comme vous l'avez lu ci-dessus.
|
||||
|
||||
### `PlainTextResponse` { #plaintextresponse }
|
||||
|
||||
Prend du texte ou des octets et renvoie une réponse en texte brut.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
Prend des données et renvoie une réponse encodée en `application/json`.
|
||||
|
||||
C'est la réponse par défaut utilisée dans **FastAPI**, comme vous l'avez lu ci-dessus.
|
||||
|
||||
### `ORJSONResponse` { #orjsonresponse }
|
||||
|
||||
Une réponse JSON alternative rapide utilisant <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, comme vous l'avez lu ci-dessus.
|
||||
|
||||
/// info
|
||||
|
||||
Cela nécessite l'installation de `orjson`, par exemple avec `pip install orjson`.
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse` { #ujsonresponse }
|
||||
|
||||
Une réponse JSON alternative utilisant <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
|
||||
|
||||
/// info
|
||||
|
||||
Cela nécessite l'installation de `ujson`, par exemple avec `pip install ujson`.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
`ujson` est moins rigoureux que l'implémentation intégrée de Python dans sa gestion de certains cas limites.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Il est possible que `ORJSONResponse` soit une alternative plus rapide.
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse` { #redirectresponse }
|
||||
|
||||
Renvoie une redirection HTTP. Utilise par défaut un code d'état 307 (Temporary Redirect).
|
||||
|
||||
Vous pouvez renvoyer directement une `RedirectResponse` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
Ou vous pouvez l'utiliser dans le paramètre `response_class` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
Si vous faites cela, vous pouvez alors renvoyer directement l'URL depuis votre fonction de chemin d'accès.
|
||||
|
||||
Dans ce cas, le `status_code` utilisé sera celui par défaut pour `RedirectResponse`, c'est-à-dire `307`.
|
||||
|
||||
---
|
||||
|
||||
Vous pouvez aussi utiliser le paramètre `status_code` combiné avec le paramètre `response_class` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Prend un générateur async ou un générateur/itérateur normal et diffuse le corps de la réponse.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### Utiliser `StreamingResponse` avec des objets de type fichier { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
Si vous avez un objet <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">de type fichier</a> (par ex. l'objet renvoyé par `open()`), vous pouvez créer une fonction génératrice pour itérer sur cet objet de type fichier.
|
||||
|
||||
De cette façon, vous n'avez pas à tout lire en mémoire au préalable, et vous pouvez passer cette fonction génératrice à `StreamingResponse`, puis la renvoyer.
|
||||
|
||||
Cela inclut de nombreuses bibliothèques pour interagir avec du stockage cloud, du traitement vidéo, et autres.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. C'est la fonction génératrice. C'est une « fonction génératrice » parce qu'elle contient des instructions `yield` à l'intérieur.
|
||||
2. En utilisant un bloc `with`, nous nous assurons que l'objet de type fichier est fermé après l'exécution de la fonction génératrice. Donc, après qu'elle a fini d'envoyer la réponse.
|
||||
3. Ce `yield from` indique à la fonction d'itérer sur l'objet nommé `file_like`. Puis, pour chaque partie itérée, de produire cette partie comme provenant de cette fonction génératrice (`iterfile`).
|
||||
|
||||
Ainsi, c'est une fonction génératrice qui transfère le travail de « génération » à autre chose en interne.
|
||||
|
||||
En procédant ainsi, nous pouvons la placer dans un bloc `with` et, de cette façon, garantir que l'objet de type fichier est fermé après la fin.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez qu'ici, comme nous utilisons le `open()` standard qui ne prend pas en charge `async` et `await`, nous déclarons le chemin d'accès avec un `def` normal.
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse` { #fileresponse }
|
||||
|
||||
Diffuse de façon asynchrone un fichier comme réponse.
|
||||
|
||||
Prend un ensemble de paramètres différent à l'instanciation par rapport aux autres types de réponse :
|
||||
|
||||
- `path` - Le chemin du fichier à diffuser.
|
||||
- `headers` - D'éventuels en-têtes personnalisés à inclure, sous forme de dictionnaire.
|
||||
- `media_type` - Une chaîne donnant le media type. Si non défini, le nom du fichier ou le chemin sera utilisé pour en déduire un media type.
|
||||
- `filename` - Si défini, sera inclus dans l'en-tête `Content-Disposition` de la réponse.
|
||||
|
||||
Les réponses de type fichier incluront les en-têtes appropriés `Content-Length`, `Last-Modified` et `ETag`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
Vous pouvez aussi utiliser le paramètre `response_class` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
Dans ce cas, vous pouvez renvoyer directement le chemin du fichier depuis votre fonction de chemin d'accès.
|
||||
|
||||
## Classe de réponse personnalisée { #custom-response-class }
|
||||
|
||||
Vous pouvez créer votre propre classe de réponse personnalisée, héritant de `Response`, et l'utiliser.
|
||||
|
||||
Par exemple, disons que vous voulez utiliser <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, mais avec certains réglages personnalisés non utilisés dans la classe `ORJSONResponse` incluse.
|
||||
|
||||
Disons que vous voulez renvoyer du JSON indenté et formaté, donc vous voulez utiliser l'option orjson `orjson.OPT_INDENT_2`.
|
||||
|
||||
Vous pourriez créer une `CustomORJSONResponse`. L'essentiel est de créer une méthode `Response.render(content)` qui renvoie le contenu en `bytes` :
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
Maintenant, au lieu de renvoyer :
|
||||
|
||||
```json
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
... cette réponse renverra :
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello World"
|
||||
}
|
||||
```
|
||||
|
||||
Bien sûr, vous trouverez probablement des moyens bien meilleurs de tirer parti de cela que de formater du JSON. 😉
|
||||
|
||||
## Classe de réponse par défaut { #default-response-class }
|
||||
|
||||
Lors de la création d'une instance de classe **FastAPI** ou d'un `APIRouter`, vous pouvez spécifier quelle classe de réponse utiliser par défaut.
|
||||
|
||||
Le paramètre qui le définit est `default_response_class`.
|
||||
|
||||
Dans l'exemple ci-dessous, **FastAPI** utilisera `ORJSONResponse` par défaut, dans tous les chemins d'accès, au lieu de `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous pouvez toujours remplacer `response_class` dans les chemins d'accès comme auparavant.
|
||||
|
||||
///
|
||||
|
||||
## Documentation supplémentaire { #additional-documentation }
|
||||
|
||||
Vous pouvez aussi déclarer le media type et de nombreux autres détails dans OpenAPI en utilisant `responses` : [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Utiliser des dataclasses { #using-dataclasses }
|
||||
|
||||
FastAPI est construit au‑dessus de **Pydantic**, et je vous ai montré comment utiliser des modèles Pydantic pour déclarer les requêtes et les réponses.
|
||||
|
||||
Mais FastAPI prend aussi en charge l'utilisation de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> de la même manière :
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
|
||||
|
||||
Cela fonctionne grâce à **Pydantic**, qui offre une <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">prise en charge interne des `dataclasses`</a>.
|
||||
|
||||
Ainsi, même avec le code ci‑dessus qui n'emploie pas explicitement Pydantic, FastAPI utilise Pydantic pour convertir ces dataclasses standard en la variante de dataclasses de Pydantic.
|
||||
|
||||
Et bien sûr, cela prend en charge la même chose :
|
||||
|
||||
* validation des données
|
||||
* sérialisation des données
|
||||
* documentation des données, etc.
|
||||
|
||||
Cela fonctionne de la même manière qu'avec les modèles Pydantic. Et, en réalité, c'est mis en œuvre de la même façon en interne, en utilisant Pydantic.
|
||||
|
||||
/// info | Info
|
||||
|
||||
Gardez à l'esprit que les dataclasses ne peuvent pas tout ce que peuvent faire les modèles Pydantic.
|
||||
|
||||
Vous pourriez donc avoir encore besoin d'utiliser des modèles Pydantic.
|
||||
|
||||
Mais si vous avez déjà un ensemble de dataclasses sous la main, c'est une astuce pratique pour les utiliser afin d'alimenter une API Web avec FastAPI. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Utiliser des dataclasses dans `response_model` { #dataclasses-in-response-model }
|
||||
|
||||
Vous pouvez aussi utiliser `dataclasses` dans le paramètre `response_model` :
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
|
||||
|
||||
La dataclass sera automatiquement convertie en dataclass Pydantic.
|
||||
|
||||
Ainsi, son schéma apparaîtra dans l'interface utilisateur de la documentation de l'API :
|
||||
|
||||
<img src="/img/tutorial/dataclasses/image01.png">
|
||||
|
||||
## Utiliser des dataclasses dans des structures de données imbriquées { #dataclasses-in-nested-data-structures }
|
||||
|
||||
Vous pouvez aussi combiner `dataclasses` avec d'autres annotations de type pour créer des structures de données imbriquées.
|
||||
|
||||
Dans certains cas, vous devrez peut‑être encore utiliser la version `dataclasses` de Pydantic. Par exemple, si vous rencontrez des erreurs avec la documentation d'API générée automatiquement.
|
||||
|
||||
Dans ce cas, vous pouvez simplement remplacer les `dataclasses` standard par `pydantic.dataclasses`, qui est un remplacement drop‑in :
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
|
||||
|
||||
1. Nous continuons à importer `field` depuis les `dataclasses` standard.
|
||||
|
||||
2. `pydantic.dataclasses` est un remplacement drop‑in pour `dataclasses`.
|
||||
|
||||
3. La dataclass `Author` inclut une liste de dataclasses `Item`.
|
||||
|
||||
4. La dataclass `Author` est utilisée comme paramètre `response_model`.
|
||||
|
||||
5. Vous pouvez utiliser d'autres annotations de type standard avec des dataclasses comme corps de la requête.
|
||||
|
||||
Dans ce cas, il s'agit d'une liste de dataclasses `Item`.
|
||||
|
||||
6. Ici, nous renvoyons un dictionnaire qui contient `items`, qui est une liste de dataclasses.
|
||||
|
||||
FastAPI est toujours capable de <dfn title="convertir les données dans un format pouvant être transmis">sérialiser</dfn> les données en JSON.
|
||||
|
||||
7. Ici, `response_model` utilise une annotation de type correspondant à une liste de dataclasses `Author`.
|
||||
|
||||
Là encore, vous pouvez combiner `dataclasses` avec des annotations de type standard.
|
||||
|
||||
8. Notez que cette *fonction de chemin d'accès* utilise un `def` classique au lieu de `async def`.
|
||||
|
||||
Comme toujours, avec FastAPI vous pouvez combiner `def` et `async def` selon vos besoins.
|
||||
|
||||
Si vous avez besoin d'un rappel sur quand utiliser l'un ou l'autre, consultez la section _« In a hurry? »_ dans la documentation à propos de [`async` et `await`](../async.md#in-a-hurry){.internal-link target=_blank}.
|
||||
|
||||
9. Cette *fonction de chemin d'accès* ne renvoie pas des dataclasses (même si elle le pourrait), mais une liste de dictionnaires contenant des données internes.
|
||||
|
||||
FastAPI utilisera le paramètre `response_model` (qui inclut des dataclasses) pour convertir la réponse.
|
||||
|
||||
Vous pouvez combiner `dataclasses` avec d'autres annotations de type, selon de nombreuses combinaisons, pour former des structures de données complexes.
|
||||
|
||||
Reportez‑vous aux annotations dans le code ci‑dessus pour voir plus de détails spécifiques.
|
||||
|
||||
## En savoir plus { #learn-more }
|
||||
|
||||
Vous pouvez aussi combiner `dataclasses` avec d'autres modèles Pydantic, en hériter, les inclure dans vos propres modèles, etc.
|
||||
|
||||
Pour en savoir plus, consultez la <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">documentation Pydantic sur les dataclasses</a>.
|
||||
|
||||
## Version { #version }
|
||||
|
||||
C'est disponible depuis FastAPI version `0.67.0`. 🔖
|
||||
@@ -0,0 +1,165 @@
|
||||
# Événements de cycle de vie { #lifespan-events }
|
||||
|
||||
Vous pouvez définir une logique (du code) qui doit être exécutée avant que l'application ne **démarre**. Cela signifie que ce code sera exécuté **une seule fois**, **avant** que l'application ne **commence à recevoir des requêtes**.
|
||||
|
||||
De la même manière, vous pouvez définir une logique (du code) qui doit être exécutée lorsque l'application **s'arrête**. Dans ce cas, ce code sera exécuté **une seule fois**, **après** avoir traité potentiellement **de nombreuses requêtes**.
|
||||
|
||||
Comme ce code est exécuté avant que l'application ne **commence** à recevoir des requêtes, et juste après qu'elle **termine** de les traiter, il couvre tout le **cycle de vie** de l'application (le mot « lifespan » va être important dans un instant 😉).
|
||||
|
||||
Cela peut être très utile pour configurer des **ressources** dont vous avez besoin pour l'ensemble de l'application, qui sont **partagées** entre les requêtes, et/ou que vous devez **nettoyer** ensuite. Par exemple, un pool de connexions à une base de données, ou le chargement d'un modèle d'apprentissage automatique partagé.
|
||||
|
||||
## Cas d'utilisation { #use-case }
|
||||
|
||||
Commençons par un exemple de **cas d'utilisation**, puis voyons comment le résoudre avec ceci.
|
||||
|
||||
Imaginons que vous ayez des **modèles d'apprentissage automatique** que vous souhaitez utiliser pour traiter des requêtes. 🤖
|
||||
|
||||
Les mêmes modèles sont partagés entre les requêtes, ce n'est donc pas un modèle par requête, ni un par utilisateur, ou quelque chose de similaire.
|
||||
|
||||
Imaginons que le chargement du modèle puisse **prendre pas mal de temps**, car il doit lire beaucoup de **données depuis le disque**. Vous ne voulez donc pas le faire pour chaque requête.
|
||||
|
||||
Vous pourriez le charger au niveau supérieur du module/fichier, mais cela signifierait aussi qu'il **chargerait le modèle** même si vous exécutez simplement un test automatisé simple ; ce test serait alors **lent** car il devrait attendre le chargement du modèle avant de pouvoir exécuter une partie indépendante du code.
|
||||
|
||||
C'est ce que nous allons résoudre : chargeons le modèle avant que les requêtes ne soient traitées, mais seulement juste avant que l'application ne commence à recevoir des requêtes, pas pendant le chargement du code.
|
||||
|
||||
## Cycle de vie { #lifespan }
|
||||
|
||||
Vous pouvez définir cette logique de *démarrage* et d'*arrêt* en utilisant le paramètre `lifespan` de l'application `FastAPI`, et un « gestionnaire de contexte » (je vais vous montrer ce que c'est dans un instant).
|
||||
|
||||
Commençons par un exemple, puis voyons-le en détail.
|
||||
|
||||
Nous créons une fonction async `lifespan()` avec `yield` comme ceci :
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
Ici, nous simulons l'opération de *démarrage* coûteuse de chargement du modèle en plaçant la fonction (factice) du modèle dans le dictionnaire avec les modèles d'apprentissage automatique avant le `yield`. Ce code sera exécuté **avant** que l'application ne **commence à recevoir des requêtes**, pendant le *démarrage*.
|
||||
|
||||
Puis, juste après le `yield`, nous déchargeons le modèle. Ce code sera exécuté **après** que l'application **a fini de traiter les requêtes**, juste avant l'*arrêt*. Cela pourrait, par exemple, libérer des ressources comme la mémoire ou un GPU.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
L’« arrêt » se produit lorsque vous **arrêtez** l'application.
|
||||
|
||||
Peut-être devez-vous démarrer une nouvelle version, ou vous en avez simplement assez de l'exécuter. 🤷
|
||||
|
||||
///
|
||||
|
||||
### Fonction de cycle de vie { #lifespan-function }
|
||||
|
||||
La première chose à remarquer est que nous définissons une fonction async avec `yield`. C'est très similaire aux Dépendances avec `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
La première partie de la fonction, avant le `yield`, sera exécutée **avant** le démarrage de l'application.
|
||||
|
||||
Et la partie après le `yield` sera exécutée **après** que l'application a terminé.
|
||||
|
||||
### Gestionnaire de contexte asynchrone { #async-context-manager }
|
||||
|
||||
Si vous regardez, la fonction est décorée avec `@asynccontextmanager`.
|
||||
|
||||
Cela convertit la fonction en quelque chose appelé un « **gestionnaire de contexte asynchrone** ».
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
Un **gestionnaire de contexte** en Python est quelque chose que vous pouvez utiliser dans une instruction `with`. Par exemple, `open()` peut être utilisé comme gestionnaire de contexte :
|
||||
|
||||
```Python
|
||||
with open("file.txt") as file:
|
||||
file.read()
|
||||
```
|
||||
|
||||
Dans les versions récentes de Python, il existe aussi un **gestionnaire de contexte asynchrone**. Vous l'utiliseriez avec `async with` :
|
||||
|
||||
```Python
|
||||
async with lifespan(app):
|
||||
await do_stuff()
|
||||
```
|
||||
|
||||
Quand vous créez un gestionnaire de contexte ou un gestionnaire de contexte asynchrone comme ci-dessus, ce qu'il fait, c'est qu'avant d'entrer dans le bloc `with`, il exécute le code avant le `yield`, et après être sorti du bloc `with`, il exécute le code après le `yield`.
|
||||
|
||||
Dans notre exemple de code ci-dessus, nous ne l'utilisons pas directement, mais nous le transmettons à FastAPI pour qu'il l'utilise.
|
||||
|
||||
Le paramètre `lifespan` de l'application `FastAPI` accepte un **gestionnaire de contexte asynchrone**, nous pouvons donc lui passer notre nouveau gestionnaire de contexte asynchrone `lifespan`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## Événements alternatifs (déprécié) { #alternative-events-deprecated }
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
La méthode recommandée pour gérer le *démarrage* et l'*arrêt* est d'utiliser le paramètre `lifespan` de l'application `FastAPI` comme décrit ci-dessus. Si vous fournissez un paramètre `lifespan`, les gestionnaires d'événements `startup` et `shutdown` ne seront plus appelés. C'est soit tout en `lifespan`, soit tout en événements, pas les deux.
|
||||
|
||||
Vous pouvez probablement passer cette partie.
|
||||
|
||||
///
|
||||
|
||||
Il existe une autre manière de définir cette logique à exécuter au *démarrage* et à l'*arrêt*.
|
||||
|
||||
Vous pouvez définir des gestionnaires d'événements (fonctions) qui doivent être exécutés avant le démarrage de l'application, ou lorsque l'application s'arrête.
|
||||
|
||||
Ces fonctions peuvent être déclarées avec `async def` ou un `def` normal.
|
||||
|
||||
### Événement `startup` { #startup-event }
|
||||
|
||||
Pour ajouter une fonction qui doit être exécutée avant le démarrage de l'application, déclarez-la avec l'événement « startup » :
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Dans ce cas, la fonction gestionnaire de l'événement `startup` initialisera la « base de données » des items (juste un `dict`) avec quelques valeurs.
|
||||
|
||||
Vous pouvez ajouter plusieurs fonctions de gestion d'événements.
|
||||
|
||||
Et votre application ne commencera pas à recevoir des requêtes avant que tous les gestionnaires de l'événement `startup` aient terminé.
|
||||
|
||||
### Événement `shutdown` { #shutdown-event }
|
||||
|
||||
Pour ajouter une fonction qui doit être exécutée lorsque l'application s'arrête, déclarez-la avec l'événement « shutdown » :
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
Ici, la fonction gestionnaire de l'événement `shutdown` écrira une ligne de texte « Application shutdown » dans un fichier `log.txt`.
|
||||
|
||||
/// info
|
||||
|
||||
Dans la fonction `open()`, le `mode="a"` signifie « append » (ajouter) ; la ligne sera donc ajoutée après ce qui se trouve déjà dans ce fichier, sans écraser le contenu précédent.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Notez que dans ce cas, nous utilisons une fonction Python standard `open()` qui interagit avec un fichier.
|
||||
|
||||
Cela implique des E/S (input/output), qui nécessitent « d'attendre » que des choses soient écrites sur le disque.
|
||||
|
||||
Mais `open()` n'utilise pas `async` et `await`.
|
||||
|
||||
Nous déclarons donc la fonction gestionnaire d'événement avec un `def` standard plutôt qu'avec `async def`.
|
||||
|
||||
///
|
||||
|
||||
### `startup` et `shutdown` ensemble { #startup-and-shutdown-together }
|
||||
|
||||
Il y a de fortes chances que la logique de votre *démarrage* et de votre *arrêt* soit liée : vous pourriez vouloir démarrer quelque chose puis le terminer, acquérir une ressource puis la libérer, etc.
|
||||
|
||||
Faire cela dans des fonctions séparées qui ne partagent pas de logique ni de variables est plus difficile, car vous devriez stocker des valeurs dans des variables globales ou recourir à des astuces similaires.
|
||||
|
||||
Pour cette raison, il est désormais recommandé d'utiliser plutôt le `lifespan` comme expliqué ci-dessus.
|
||||
|
||||
## Détails techniques { #technical-details }
|
||||
|
||||
Juste un détail technique pour les nerds curieux. 🤓
|
||||
|
||||
Sous le capot, dans la spécification technique ASGI, cela fait partie du <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">protocole Lifespan</a>, et il y définit des événements appelés `startup` et `shutdown`.
|
||||
|
||||
/// info
|
||||
|
||||
Vous pouvez en lire plus sur les gestionnaires `lifespan` de Starlette dans la <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">documentation « Lifespan » de Starlette</a>.
|
||||
|
||||
Y compris comment gérer l'état de cycle de vie qui peut être utilisé dans d'autres parties de votre code.
|
||||
|
||||
///
|
||||
|
||||
## Sous-applications { #sub-applications }
|
||||
|
||||
🚨 Gardez à l'esprit que ces événements de cycle de vie (démarrage et arrêt) ne seront exécutés que pour l'application principale, pas pour [Sous-applications - Montages](sub-applications.md){.internal-link target=_blank}.
|
||||
@@ -0,0 +1,208 @@
|
||||
# Générer des SDK { #generating-sdks }
|
||||
|
||||
Parce que **FastAPI** est basé sur la spécification **OpenAPI**, ses API peuvent être décrites dans un format standard compris par de nombreux outils.
|
||||
|
||||
Cela facilite la génération de **documentation** à jour, de bibliothèques clientes (<abbr title="Software Development Kits - Kits de développement logiciel">**SDKs**</abbr>) dans plusieurs langages, ainsi que de **tests** ou de **workflows d’automatisation** qui restent synchronisés avec votre code.
|
||||
|
||||
Dans ce guide, vous apprendrez à générer un **SDK TypeScript** pour votre backend FastAPI.
|
||||
|
||||
## Générateurs de SDK open source { #open-source-sdk-generators }
|
||||
|
||||
Une option polyvalente est <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>, qui prend en charge **de nombreux langages de programmation** et peut générer des SDK à partir de votre spécification OpenAPI.
|
||||
|
||||
Pour les **clients TypeScript**, <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> est une solution dédiée, offrant une expérience optimisée pour l’écosystème TypeScript.
|
||||
|
||||
Vous pouvez découvrir davantage de générateurs de SDK sur <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
FastAPI génère automatiquement des spécifications **OpenAPI 3.1**, donc tout outil que vous utilisez doit prendre en charge cette version.
|
||||
|
||||
///
|
||||
|
||||
## Générateurs de SDK par les sponsors de FastAPI { #sdk-generators-from-fastapi-sponsors }
|
||||
|
||||
Cette section met en avant des solutions **soutenues par des fonds** et **par des entreprises** qui sponsorisent FastAPI. Ces produits offrent **des fonctionnalités supplémentaires** et **des intégrations** en plus de SDK de haute qualité générés.
|
||||
|
||||
En ✨ [**sponsorisant FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, ces entreprises contribuent à garantir que le framework et son **écosystème** restent sains et **durables**.
|
||||
|
||||
Leur sponsoring démontre également un fort engagement envers la **communauté** FastAPI (vous), montrant qu’elles se soucient non seulement d’offrir un **excellent service**, mais aussi de soutenir un **framework robuste et florissant**, FastAPI. 🙇
|
||||
|
||||
Par exemple, vous pourriez essayer :
|
||||
|
||||
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
|
||||
* <a href="https://www.stainless.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
|
||||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
|
||||
|
||||
Certaines de ces solutions peuvent aussi être open source ou proposer des niveaux gratuits, afin que vous puissiez les essayer sans engagement financier. D’autres générateurs de SDK commerciaux existent et peuvent être trouvés en ligne. 🤓
|
||||
|
||||
## Créer un SDK TypeScript { #create-a-typescript-sdk }
|
||||
|
||||
Commençons par une application FastAPI simple :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Remarquez que les *chemins d'accès* définissent les modèles qu’ils utilisent pour le payload de requête et le payload de réponse, en utilisant les modèles `Item` et `ResponseMessage`.
|
||||
|
||||
### Documentation de l’API { #api-docs }
|
||||
|
||||
Si vous allez sur `/docs`, vous verrez qu’elle contient les **schémas** pour les données à envoyer dans les requêtes et reçues dans les réponses :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image01.png">
|
||||
|
||||
Vous voyez ces schémas parce qu’ils ont été déclarés avec les modèles dans l’application.
|
||||
|
||||
Ces informations sont disponibles dans le **schéma OpenAPI** de l’application, puis affichées dans la documentation de l’API.
|
||||
|
||||
Ces mêmes informations issues des modèles, incluses dans OpenAPI, peuvent être utilisées pour **générer le code client**.
|
||||
|
||||
### Hey API { #hey-api }
|
||||
|
||||
Une fois que vous avez une application FastAPI avec les modèles, vous pouvez utiliser Hey API pour générer un client TypeScript. Le moyen le plus rapide de le faire est via npx.
|
||||
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
||||
```
|
||||
|
||||
Cela générera un SDK TypeScript dans `./src/client`.
|
||||
|
||||
Vous pouvez apprendre à <a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">installer `@hey-api/openapi-ts`</a> et lire à propos du <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">résultat généré</a> sur leur site.
|
||||
|
||||
### Utiliser le SDK { #using-the-sdk }
|
||||
|
||||
Vous pouvez maintenant importer et utiliser le code client. Cela pourrait ressembler à ceci, remarquez que vous obtenez l’autocomplétion pour les méthodes :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image02.png">
|
||||
|
||||
Vous obtiendrez également l’autocomplétion pour le payload à envoyer :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image03.png">
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez l’autocomplétion pour `name` et `price`, qui a été définie dans l’application FastAPI, dans le modèle `Item`.
|
||||
|
||||
///
|
||||
|
||||
Vous aurez des erreurs en ligne pour les données que vous envoyez :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image04.png">
|
||||
|
||||
L’objet de réponse aura également l’autocomplétion :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image05.png">
|
||||
|
||||
## Application FastAPI avec des tags { #fastapi-app-with-tags }
|
||||
|
||||
Dans de nombreux cas, votre application FastAPI sera plus grande, et vous utiliserez probablement des tags pour séparer différents groupes de *chemins d'accès*.
|
||||
|
||||
Par exemple, vous pourriez avoir une section pour les **items** et une autre section pour les **users**, et elles pourraient être séparées par des tags :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### Générer un client TypeScript avec des tags { #generate-a-typescript-client-with-tags }
|
||||
|
||||
Si vous générez un client pour une application FastAPI utilisant des tags, il séparera normalement aussi le code client en fonction des tags.
|
||||
|
||||
De cette façon, vous pourrez avoir les éléments ordonnés et correctement groupés côté client :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image06.png">
|
||||
|
||||
Dans ce cas, vous avez :
|
||||
|
||||
* `ItemsService`
|
||||
* `UsersService`
|
||||
|
||||
### Noms des méthodes du client { #client-method-names }
|
||||
|
||||
À l’heure actuelle, les noms de méthodes générés comme `createItemItemsPost` ne sont pas très propres :
|
||||
|
||||
```TypeScript
|
||||
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
|
||||
```
|
||||
|
||||
... c’est parce que le générateur de client utilise l’**operation ID** interne OpenAPI pour chaque *chemin d'accès*.
|
||||
|
||||
OpenAPI exige que chaque operation ID soit unique parmi tous les *chemins d'accès*, donc FastAPI utilise le **nom de la fonction**, le **chemin**, et la **méthode/opération HTTP** pour générer cet operation ID, car de cette façon il peut s’assurer que les operation IDs sont uniques.
|
||||
|
||||
Mais je vais vous montrer comment améliorer cela ensuite. 🤓
|
||||
|
||||
## IDs d’opération personnalisés et meilleurs noms de méthodes { #custom-operation-ids-and-better-method-names }
|
||||
|
||||
Vous pouvez **modifier** la façon dont ces operation IDs sont **générés** pour les simplifier et obtenir des **noms de méthodes plus simples** dans les clients.
|
||||
|
||||
Dans ce cas, vous devez vous assurer que chaque operation ID est **unique** d’une autre manière.
|
||||
|
||||
Par exemple, vous pouvez vous assurer que chaque *chemin d'accès* a un tag, puis générer l’operation ID à partir du **tag** et du **nom** du *chemin d'accès* (le nom de la fonction).
|
||||
|
||||
### Fonction personnalisée de génération d’ID unique { #custom-generate-unique-id-function }
|
||||
|
||||
FastAPI utilise un **ID unique** pour chaque *chemin d'accès*, qui est utilisé pour l’**operation ID** et également pour les noms des modèles personnalisés nécessaires, pour les requêtes ou les réponses.
|
||||
|
||||
Vous pouvez personnaliser cette fonction. Elle prend un `APIRoute` et retourne une chaîne.
|
||||
|
||||
Par exemple, ici elle utilise le premier tag (vous n’en aurez probablement qu’un) et le nom du *chemin d'accès* (le nom de la fonction).
|
||||
|
||||
Vous pouvez ensuite passer cette fonction personnalisée à **FastAPI** via le paramètre `generate_unique_id_function` :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### Générer un client TypeScript avec des IDs d’opération personnalisés { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
Maintenant, si vous régénérez le client, vous verrez qu’il possède des noms de méthodes améliorés :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image07.png">
|
||||
|
||||
Comme vous le voyez, les noms de méthodes contiennent maintenant le tag puis le nom de la fonction ; ils n’incluent plus d’informations provenant du chemin d’URL et de l’opération HTTP.
|
||||
|
||||
### Prétraiter la spécification OpenAPI pour le générateur de client { #preprocess-the-openapi-specification-for-the-client-generator }
|
||||
|
||||
Le code généré contient encore des **informations dupliquées**.
|
||||
|
||||
Nous savons déjà que cette méthode est liée aux **items** parce que ce mot figure dans `ItemsService` (issu du tag), mais nous avons encore le nom du tag préfixé dans le nom de la méthode. 😕
|
||||
|
||||
Nous voudrons probablement le conserver pour OpenAPI en général, car cela garantira que les operation IDs sont **uniques**.
|
||||
|
||||
Mais pour le client généré, nous pourrions **modifier** les operation IDs d’OpenAPI juste avant de générer les clients, simplement pour rendre ces noms de méthodes plus agréables et **plus clairs**.
|
||||
|
||||
Nous pourrions télécharger le JSON OpenAPI dans un fichier `openapi.json` puis **supprimer ce tag préfixé** avec un script comme celui-ci :
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
```Javascript
|
||||
{!> ../../docs_src/generate_clients/tutorial004.js!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Avec cela, les operation IDs seraient renommés de `items-get_items` en simplement `get_items`, de sorte que le générateur de client puisse produire des noms de méthodes plus simples.
|
||||
|
||||
### Générer un client TypeScript avec l’OpenAPI prétraité { #generate-a-typescript-client-with-the-preprocessed-openapi }
|
||||
|
||||
Puisque le résultat final se trouve maintenant dans un fichier `openapi.json`, vous devez mettre à jour l’emplacement d’entrée :
|
||||
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
|
||||
```
|
||||
|
||||
Après avoir généré le nouveau client, vous aurez désormais des **noms de méthodes propres**, avec toute l’**autocomplétion**, les **erreurs en ligne**, etc. :
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image08.png">
|
||||
|
||||
## Avantages { #benefits }
|
||||
|
||||
En utilisant les clients générés automatiquement, vous obtiendrez de l’**autocomplétion** pour :
|
||||
|
||||
* Méthodes.
|
||||
* Payloads de requête dans le corps, paramètres de requête, etc.
|
||||
* Payloads de réponse.
|
||||
|
||||
Vous auriez également des **erreurs en ligne** pour tout.
|
||||
|
||||
Et chaque fois que vous mettez à jour le code du backend et **régénérez** le frontend, il inclura les nouveaux *chemins d'accès* disponibles en tant que méthodes, supprimera les anciens, et tout autre changement sera reflété dans le code généré. 🤓
|
||||
|
||||
Cela signifie aussi que si quelque chose change, cela sera **reflété** automatiquement dans le code client. Et si vous **bâtissez** le client, il échouera en cas de **discordance** dans les données utilisées.
|
||||
|
||||
Ainsi, vous **détecterez de nombreuses erreurs** très tôt dans le cycle de développement au lieu d’attendre qu’elles apparaissent pour vos utilisateurs finaux en production puis de tenter de déboguer l’origine du problème. ✨
|
||||
@@ -1,27 +1,21 @@
|
||||
# Guide de l'utilisateur avancé
|
||||
# Guide de l'utilisateur avancé { #advanced-user-guide }
|
||||
|
||||
## Caractéristiques supplémentaires
|
||||
## Caractéristiques supplémentaires { #additional-features }
|
||||
|
||||
Le [Tutoriel - Guide de l'utilisateur](../tutorial/index.md){.internal-link target=_blank} devrait suffire à vous faire découvrir toutes les fonctionnalités principales de **FastAPI**.
|
||||
|
||||
Dans les sections suivantes, vous verrez des options, configurations et fonctionnalités supplémentaires.
|
||||
|
||||
/// note | Remarque
|
||||
/// tip | Astuce
|
||||
|
||||
Les sections de ce chapitre ne sont **pas nécessairement "avancées"**.
|
||||
Les sections suivantes ne sont **pas nécessairement « avancées »**.
|
||||
|
||||
Et il est possible que pour votre cas d'utilisation, la solution se trouve dans l'un d'entre eux.
|
||||
Et il est possible que, pour votre cas d'utilisation, la solution se trouve dans l'une d'entre elles.
|
||||
|
||||
///
|
||||
|
||||
## Lisez d'abord le didacticiel
|
||||
## Lire d'abord le tutoriel { #read-the-tutorial-first }
|
||||
|
||||
Vous pouvez utiliser la plupart des fonctionnalités de **FastAPI** grâce aux connaissances du [Tutoriel - Guide de l'utilisateur](../tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
Et les sections suivantes supposent que vous l'avez lu et que vous en connaissez les idées principales.
|
||||
|
||||
## Cours TestDriven.io
|
||||
|
||||
Si vous souhaitez suivre un cours pour débutants avancés pour compléter cette section de la documentation, vous pouvez consulter : <a href="https://testdrive.io/courses/tdd-fastapi/" class="external- link" target="_blank">Développement piloté par les tests avec FastAPI et Docker</a> par **TestDriven.io**.
|
||||
|
||||
10 % de tous les bénéfices de ce cours sont reversés au développement de **FastAPI**. 🎉 😄
|
||||
|
||||
@@ -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>.
|
||||
@@ -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* s’appelle 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`.
|
||||
|
||||
L’utilisateur 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 l’argent.
|
||||
* Renvoyer une notification à l’utilisateur de l’API (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 (c’est le « callback »).
|
||||
|
||||
## L’application **FastAPI** normale { #the-normal-fastapi-app }
|
||||
|
||||
Voyons d’abord à quoi ressemble l’application API normale avant d’ajouter 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 l’URL 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 c’est 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 d’une application à l’autre.
|
||||
|
||||
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 l’utilisateur 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, c’est 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 n’implé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 n’est qu’une 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 l’API externe devrait implémenter (ceux que votre API appellera).
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Lorsque vous écrivez le code pour documenter un callback, il peut être utile d’imaginer 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 qu’il doit recevoir, par exemple `body: InvoiceEvent`.
|
||||
* Et il pourrait aussi déclarer la réponse qu’il 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 n’a pas besoin d’avoir de code réel, car votre application n’appellera 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*.
|
||||
|
||||
### L’expression 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, c’est la `str` :
|
||||
|
||||
```Python
|
||||
"{$callback_url}/invoices/{$request.body.id}"
|
||||
```
|
||||
|
||||
Ainsi, si l’utilisateur 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 s’attendra à une réponse de cette *API externe* avec un corps JSON comme :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez que l’URL de callback utilisée contient l’URL reçue en paramètre de requête dans `callback_url` (`https://www.external.org/events`) et aussi l’`id` de la facture à l’inté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 l’attribut `.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 l’attribut `.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">
|
||||
@@ -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">
|
||||
@@ -1,106 +1,108 @@
|
||||
# Configuration avancée des paramètres de chemin
|
||||
# Configuration avancée des chemins d'accès { #path-operation-advanced-configuration }
|
||||
|
||||
## ID d'opération OpenAPI
|
||||
## ID d’opération OpenAPI { #openapi-operationid }
|
||||
|
||||
/// warning | Attention
|
||||
/// warning | Alertes
|
||||
|
||||
Si vous n'êtes pas un "expert" en OpenAPI, vous n'en avez probablement pas besoin.
|
||||
Si vous n’êtes pas un « expert » d’OpenAPI, vous n’en avez probablement pas besoin.
|
||||
|
||||
///
|
||||
|
||||
Dans OpenAPI, les chemins sont des ressources, tels que /users/ ou /items/, exposées par votre API, et les opérations sont les méthodes HTTP utilisées pour manipuler ces chemins, telles que GET, POST ou DELETE. Les operationId sont des chaînes uniques facultatives utilisées pour identifier une opération d'un chemin. Vous pouvez définir l'OpenAPI `operationId` à utiliser dans votre *opération de chemin* avec le paramètre `operation_id`.
|
||||
Vous pouvez définir l’OpenAPI `operationId` à utiliser dans votre chemin d’accès avec le paramètre `operation_id`.
|
||||
|
||||
Vous devez vous assurer qu'il est unique pour chaque opération.
|
||||
Vous devez vous assurer qu’il est unique pour chaque opération.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
|
||||
|
||||
### Utilisation du nom *path operation function* comme operationId
|
||||
### Utiliser le nom de la fonction de chemin d’accès comme operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
Si vous souhaitez utiliser les noms de fonction de vos API comme `operationId`, vous pouvez les parcourir tous et remplacer chaque `operation_id` de l'*opération de chemin* en utilisant leur `APIRoute.name`.
|
||||
Si vous souhaitez utiliser les noms de fonction de vos API comme `operationId`, vous pouvez les parcourir tous et remplacer l’`operation_id` de chaque chemin d’accès en utilisant leur `APIRoute.name`.
|
||||
|
||||
Vous devriez le faire après avoir ajouté toutes vos *paramètres de chemin*.
|
||||
Vous devez le faire après avoir ajouté tous vos chemins d’accès.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12:21,24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous appelez manuellement `app.openapi()`, vous devez mettre à jour les `operationId` avant.
|
||||
Si vous appelez manuellement `app.openapi()`, vous devez mettre à jour les `operationId` avant cela.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Attention
|
||||
/// warning | Alertes
|
||||
|
||||
Pour faire cela, vous devez vous assurer que chacun de vos *chemin* ait un nom unique.
|
||||
Si vous faites cela, vous devez vous assurer que chacune de vos fonctions de chemin d’accès a un nom unique.
|
||||
|
||||
Même s'ils se trouvent dans des modules différents (fichiers Python).
|
||||
Même si elles se trouvent dans des modules différents (fichiers Python).
|
||||
|
||||
///
|
||||
|
||||
## Exclusion d'OpenAPI
|
||||
## Exclusion d’OpenAPI { #exclude-from-openapi }
|
||||
|
||||
Pour exclure un *chemin* du schéma OpenAPI généré (et donc des systèmes de documentation automatiques), utilisez le paramètre `include_in_schema` et assignez-lui la valeur `False` :
|
||||
Pour exclure un chemin d’accè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.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
|
||||
|
||||
## Description avancée de docstring
|
||||
## Description avancée depuis la docstring { #advanced-description-from-docstring }
|
||||
|
||||
Vous pouvez limiter le texte utilisé de la docstring d'une *fonction de chemin* qui sera affiché sur OpenAPI.
|
||||
Vous pouvez limiter les lignes utilisées de la docstring d’une fonction de chemin d’accès pour OpenAPI.
|
||||
|
||||
L'ajout d'un `\f` (un caractère d'échappement "form feed") va permettre à **FastAPI** de tronquer la sortie utilisée pour OpenAPI à ce stade.
|
||||
L’ajout d’un `\f` (un caractère « saut de page » échappé) amène **FastAPI** à tronquer la sortie utilisée pour OpenAPI à cet endroit.
|
||||
|
||||
Il n'apparaîtra pas dans la documentation, mais d'autres outils (tel que Sphinx) pourront utiliser le reste.
|
||||
Cela n’apparaîtra pas dans la documentation, mais d’autres outils (comme Sphinx) pourront utiliser le reste.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
|
||||
|
||||
## Réponses supplémentaires
|
||||
## Réponses supplémentaires { #additional-responses }
|
||||
|
||||
Vous avez probablement vu comment déclarer le `response_model` et le `status_code` pour une *opération de chemin*.
|
||||
Vous avez probablement vu comment déclarer le `response_model` et le `status_code` pour un chemin d’accès.
|
||||
|
||||
Cela définit les métadonnées sur la réponse principale d'une *opération de chemin*.
|
||||
Cela définit les métadonnées sur la réponse principale d’un chemin d’accès.
|
||||
|
||||
Vous pouvez également déclarer des réponses supplémentaires avec leurs modèles, codes de statut, etc.
|
||||
|
||||
Il y a un chapitre entier ici dans la documentation à ce sujet, vous pouvez le lire sur [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
Il y a un chapitre entier dans la documentation à ce sujet, vous pouvez le lire dans [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
## OpenAPI supplémentaire
|
||||
## OpenAPI supplémentaire { #openapi-extra }
|
||||
|
||||
Lorsque vous déclarez un *chemin* dans votre application, **FastAPI** génère automatiquement les métadonnées concernant ce *chemin* à inclure dans le schéma OpenAPI.
|
||||
Lorsque vous déclarez un chemin d’accès dans votre application, **FastAPI** génère automatiquement les métadonnées pertinentes à propos de ce chemin d’accès à inclure dans le schéma OpenAPI.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
La spécification OpenAPI appelle ces métadonnées des <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objets d'opération</a>.
|
||||
Dans la spécification OpenAPI, cela s’appelle l’<a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">objet Operation</a>.
|
||||
|
||||
///
|
||||
|
||||
Il contient toutes les informations sur le *chemin* et est utilisé pour générer automatiquement la documentation.
|
||||
Il contient toutes les informations sur le chemin d’accès et est utilisé pour générer la documentation automatique.
|
||||
|
||||
Il inclut les `tags`, `parameters`, `requestBody`, `responses`, etc.
|
||||
|
||||
Ce schéma OpenAPI spécifique aux *operations* est normalement généré automatiquement par **FastAPI**, mais vous pouvez également l'étendre.
|
||||
Ce schéma OpenAPI spécifique à un chemin d’accès est normalement généré automatiquement par **FastAPI**, mais vous pouvez également l’étendre.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous avez seulement besoin de déclarer des réponses supplémentaires, un moyen plus pratique de le faire est d'utiliser les [réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
Ceci est un point d’extension de bas niveau.
|
||||
|
||||
Si vous avez seulement besoin de déclarer des réponses supplémentaires, un moyen plus pratique de le faire est d’utiliser [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
Vous pouvez étendre le schéma OpenAPI pour une *opération de chemin* en utilisant le paramètre `openapi_extra`.
|
||||
Vous pouvez étendre le schéma OpenAPI pour un chemin d’accès en utilisant le paramètre `openapi_extra`.
|
||||
|
||||
### Extensions OpenAPI
|
||||
### Extensions OpenAPI { #openapi-extensions }
|
||||
|
||||
Cet `openapi_extra` peut être utile, par exemple, pour déclarer [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) :
|
||||
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.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
|
||||
|
||||
Si vous ouvrez la documentation automatique de l'API, votre extension apparaîtra au bas du *chemin* spécifique.
|
||||
Si vous ouvrez la documentation automatique de l’API, votre extension apparaîtra en bas du chemin d’accès spécifique.
|
||||
|
||||
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
|
||||
|
||||
Et dans le fichier openapi généré (`/openapi.json`), vous verrez également votre extension dans le cadre du *chemin* spécifique :
|
||||
Et si vous consultez l’OpenAPI résultant (à `/openapi.json` dans votre API), vous verrez également votre extension comme partie du chemin d’accès spécifique :
|
||||
|
||||
```JSON hl_lines="22"
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
@@ -127,44 +129,44 @@ Et dans le fichier openapi généré (`/openapi.json`), vous verrez également v
|
||||
}
|
||||
```
|
||||
|
||||
### Personnalisation du Schéma OpenAPI pour un chemin
|
||||
### Personnaliser le schéma OpenAPI d’un chemin d’accès { #custom-openapi-path-operation-schema }
|
||||
|
||||
Le dictionnaire contenu dans la variable `openapi_extra` sera fusionné avec le schéma OpenAPI généré automatiquement pour l'*opération de chemin*.
|
||||
Le dictionnaire dans `openapi_extra` sera fusionné en profondeur avec le schéma OpenAPI généré automatiquement pour le chemin d’accès.
|
||||
|
||||
Ainsi, vous pouvez ajouter des données supplémentaires au schéma généré automatiquement.
|
||||
|
||||
Par exemple, vous pouvez décider de lire et de valider la requête avec votre propre code, sans utiliser les fonctionnalités automatiques de validation proposée par Pydantic, mais vous pouvez toujours définir la requête dans le schéma OpenAPI.
|
||||
Par exemple, vous pourriez décider de lire et de valider la requête avec votre propre code, sans utiliser les fonctionnalités automatiques de FastAPI avec Pydantic, mais vous pourriez tout de même vouloir définir la requête dans le schéma OpenAPI.
|
||||
|
||||
Vous pouvez le faire avec `openapi_extra` :
|
||||
Vous pourriez le faire avec `openapi_extra` :
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[20:37,39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
|
||||
|
||||
Dans cet exemple, nous n'avons déclaré aucun modèle Pydantic. En fait, le corps de la requête n'est même pas <abbr title="converti d'un format simple, comme des octets, en objets Python">parsé</abbr> en tant que JSON, il est lu directement en tant que `bytes`, et la fonction `magic_data_reader()` serait chargé de l'analyser d'une manière ou d'une autre.
|
||||
Dans cet exemple, nous n’avons déclaré aucun modèle Pydantic. En fait, le corps de la requête n’est 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 l’analyser d’une manière ou d’une autre.
|
||||
|
||||
Néanmoins, nous pouvons déclarer le schéma attendu pour le corps de la requête.
|
||||
|
||||
### Type de contenu OpenAPI personnalisé
|
||||
### Type de contenu OpenAPI personnalisé { #custom-openapi-content-type }
|
||||
|
||||
En utilisant cette même astuce, vous pouvez utiliser un modèle Pydantic pour définir le schéma JSON qui est ensuite inclus dans la section de schéma OpenAPI personnalisée pour le *chemin* concerné.
|
||||
En utilisant cette même astuce, vous pourriez utiliser un modèle Pydantic pour définir le JSON Schema qui est ensuite inclus dans la section de schéma OpenAPI personnalisée pour le chemin d’accès.
|
||||
|
||||
Et vous pouvez le faire même si le type de données dans la requête n'est pas au format JSON.
|
||||
Et vous pourriez le faire même si le type de données dans la requête n’est pas du JSON.
|
||||
|
||||
Dans cet exemple, nous n'utilisons pas les fonctionnalités de FastAPI pour extraire le schéma JSON des modèles Pydantic ni la validation automatique pour JSON. En fait, nous déclarons le type de contenu de la requête en tant que YAML, et non JSON :
|
||||
Par exemple, dans cette application nous n’utilisons 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.py hl[17:22,24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
|
||||
|
||||
Néanmoins, bien que nous n'utilisions pas la fonctionnalité par défaut, nous utilisons toujours un modèle Pydantic pour générer manuellement le schéma JSON pour les données que nous souhaitons recevoir en YAML.
|
||||
Néanmoins, bien que nous n’utilisions 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.
|
||||
|
||||
Ensuite, nous utilisons directement la requête et extrayons son contenu en tant qu'octets. Cela signifie que FastAPI n'essaiera même pas d'analyser le payload de la requête en tant que JSON.
|
||||
Ensuite, nous utilisons directement la requête et extrayons le corps en tant que `bytes`. Cela signifie que FastAPI n’essaiera même pas d’analyser le payload de la requête en JSON.
|
||||
|
||||
Et nous analysons directement ce contenu YAML, puis nous utilisons à nouveau le même modèle Pydantic pour valider le contenu YAML :
|
||||
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.py hl[26:33] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Ici, nous réutilisons le même modèle Pydantic.
|
||||
|
||||
Mais nous aurions pu tout aussi bien pu le valider d'une autre manière.
|
||||
Mais de la même manière, nous aurions pu le valider autrement.
|
||||
|
||||
///
|
||||
|
||||
@@ -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.
|
||||
@@ -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>.
|
||||
@@ -1,20 +1,20 @@
|
||||
# Renvoyer directement une réponse
|
||||
# Renvoyer directement une réponse { #return-a-response-directly }
|
||||
|
||||
Lorsque vous créez une *opération de chemins* **FastAPI**, vous pouvez normalement retourner n'importe quelle donnée : un `dict`, une `list`, un modèle Pydantic, un modèle de base de données, etc.
|
||||
Lorsque vous créez un *chemin d'accès* **FastAPI**, vous pouvez normalement retourner n'importe quelle donnée : un `dict`, une `list`, un modèle Pydantic, un modèle de base de données, etc.
|
||||
|
||||
Par défaut, **FastAPI** convertirait automatiquement cette valeur de retour en JSON en utilisant le `jsonable_encoder` expliqué dans [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
|
||||
Par défaut, **FastAPI** convertirait automatiquement cette valeur de retour en JSON en utilisant le `jsonable_encoder` expliqué dans [Encodeur compatible JSON](../tutorial/encoder.md){.internal-link target=_blank}.
|
||||
|
||||
Ensuite, en arrière-plan, il mettra ces données JSON-compatible (par exemple un `dict`) à l'intérieur d'un `JSONResponse` qui sera utilisé pour envoyer la réponse au client.
|
||||
|
||||
Mais vous pouvez retourner une `JSONResponse` directement à partir de vos *opérations de chemin*.
|
||||
Mais vous pouvez retourner une `JSONResponse` directement à partir de vos *chemins d'accès*.
|
||||
|
||||
Cela peut être utile, par exemple, pour retourner des en-têtes personnalisés ou des cookies.
|
||||
|
||||
## Renvoyer une `Response`
|
||||
## Renvoyer une `Response` { #return-a-response }
|
||||
|
||||
En fait, vous pouvez retourner n'importe quelle `Response` ou n'importe quelle sous-classe de celle-ci.
|
||||
|
||||
/// note | Remarque
|
||||
/// tip | Astuce
|
||||
|
||||
`JSONResponse` est elle-même une sous-classe de `Response`.
|
||||
|
||||
@@ -22,29 +22,29 @@ 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.
|
||||
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.
|
||||
|
||||
## Utiliser le `jsonable_encoder` dans une `Response`
|
||||
## Utiliser le `jsonable_encoder` dans une `Response` { #using-the-jsonable-encoder-in-a-response }
|
||||
|
||||
Parce que **FastAPI** n'apporte aucune modification à une `Response` que vous retournez, vous devez vous assurer que son contenu est prêt à être utilisé (sérialisable).
|
||||
Parce que **FastAPI** n'apporte aucune modification à une `Response` que vous retournez, vous devez vous assurer que son contenu est prêt pour cela.
|
||||
|
||||
Par exemple, vous ne pouvez pas mettre un modèle Pydantic dans une `JSONResponse` sans d'abord le convertir en un `dict` avec tous les types de données (comme `datetime`, `UUID`, etc.) convertis en types compatibles avec JSON.
|
||||
|
||||
Pour ces cas, vous pouvez spécifier un appel à `jsonable_encoder` pour convertir vos données avant de les passer à une réponse :
|
||||
Pour ces cas, vous pouvez utiliser le `jsonable_encoder` pour convertir vos données avant de les passer à une réponse :
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
|
||||
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
Vous pouvez aussi utiliser `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** fournit le même objet `starlette.responses` que `fastapi.responses` juste par commodité pour le développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
|
||||
**FastAPI** fournit le même `starlette.responses` que `fastapi.responses` juste par commodité pour vous, le développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Renvoyer une `Response` personnalisée
|
||||
## Renvoyer une `Response` personnalisée { #returning-a-custom-response }
|
||||
|
||||
L'exemple ci-dessus montre toutes les parties dont vous avez besoin, mais il n'est pas encore très utile, car vous auriez pu retourner l'`item` directement, et **FastAPI** l'aurait mis dans une `JSONResponse` pour vous, en le convertissant en `dict`, etc. Tout cela par défaut.
|
||||
|
||||
@@ -54,9 +54,9 @@ 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.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
## Notes
|
||||
## Notes { #notes }
|
||||
|
||||
Lorsque vous renvoyez une `Response` directement, ses données ne sont pas validées, converties (sérialisées), ni documentées automatiquement.
|
||||
|
||||
|
||||
@@ -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>.
|
||||
@@ -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] *}
|
||||
@@ -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 d’utilisation, la solution se trouve dans l’une d’entre elles.
|
||||
|
||||
///
|
||||
|
||||
## Lire d’abord 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 s’appuient toutes sur les mêmes concepts, mais permettent des fonctionnalités supplémentaires.
|
||||
@@ -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 d’avoir un système d’autorisations plus fin, conforme au standard OAuth2, intégré à votre application OpenAPI (et à la documentation de l’API).
|
||||
|
||||
OAuth2 avec scopes est le mécanisme utilisé par de nombreux grands fournisseurs d’authentification, comme Facebook, Google, GitHub, Microsoft, X (Twitter), etc. Ils l’utilisent 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 l’authentification et l’autorisation avec le même OAuth2 avec scopes dans votre application **FastAPI**.
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
C’est une section plus ou moins avancée. Si vous débutez, vous pouvez la passer.
|
||||
|
||||
Vous n’avez pas nécessairement besoin des scopes OAuth2, et vous pouvez gérer l’authentification et l’autorisation comme vous le souhaitez.
|
||||
|
||||
Mais OAuth2 avec scopes peut s’intégrer élégamment à votre API (avec OpenAPI) et à votre documentation d’API.
|
||||
|
||||
Néanmoins, c’est 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 n’importe quel format, mais ne doit pas contenir d’espaces.
|
||||
|
||||
Ces scopes représentent des « permissions ».
|
||||
|
||||
Dans OpenAPI (par ex. la documentation de l’API), vous pouvez définir des « schémas de sécurité ».
|
||||
|
||||
Lorsqu’un 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 s’il contient d’autres caractères comme `:` ou si c’est une URL.
|
||||
|
||||
Ces détails dépendent de l’implémentation.
|
||||
|
||||
Pour OAuth2, ce ne sont que des chaînes.
|
||||
|
||||
///
|
||||
|
||||
## Vue d’ensemble { #global-view }
|
||||
|
||||
Voyons d’abord 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 l’API lorsque vous vous authentifiez/autorisez.
|
||||
|
||||
Et vous pourrez sélectionner à quels scopes vous souhaitez accorder l’accès : `me` et `items`.
|
||||
|
||||
C’est 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 d’accè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 n’ajouter que les scopes que l’utilisateur 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 d’accès et les dépendances { #declare-scopes-in-path-operations-and-dependencies }
|
||||
|
||||
Nous déclarons maintenant que le *chemin d’accè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 d’exigences de scopes.
|
||||
|
||||
Dans ce cas, elle nécessite le scope `me` (elle pourrait en exiger plusieurs).
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Vous n’avez pas nécessairement besoin d’ajouter 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 n’a qu’un paramètre supplémentaire que nous verrons plus tard.
|
||||
|
||||
Mais en utilisant `Security` au lieu de `Depends`, **FastAPI** saura qu’il peut déclarer des scopes de sécurité, les utiliser en interne et documenter l’API avec OpenAPI.
|
||||
|
||||
Cependant, lorsque vous importez `Query`, `Path`, `Depends`, `Security` et d’autres 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`.
|
||||
|
||||
C’est celle utilisée par les dépendances ci-dessus.
|
||||
|
||||
C’est 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 n’a pas elle-même d’exigences de scope, nous pouvons utiliser `Depends` avec `oauth2_scheme`, nous n’avons pas à utiliser `Security` quand nous n’avons 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 l’objet 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 l’utilisent comme sous-dépendance. Cela signifie, tous les « dépendants » ... cela peut paraître déroutant, c’est expliqué à nouveau plus bas.
|
||||
|
||||
L’objet `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 l’utiliser).
|
||||
|
||||
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 l’en-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 l’exception `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, d’un `dict`, ou autre chose, ce qui pourrait casser l’application plus tard et constituer un risque de sécurité.
|
||||
|
||||
Nous vérifions également que nous avons un utilisateur avec ce nom d’utilisateur, 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 d’accè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 d’accè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 d’accè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 d’accès* `read_own_items`.
|
||||
* `security_scopes.scopes` contiendra `["me"]` pour le *chemin d’accè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 d’accès* `read_system_status`, car il n’a 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 d’accès*.
|
||||
|
||||
Tout dépend des `scopes` déclarés dans chaque *chemin d’accès* et chaque dépendance dans l’arbre de dépendances pour ce *chemin d’accès* spécifique.
|
||||
|
||||
///
|
||||
|
||||
## Détails supplémentaires sur `SecurityScopes` { #more-details-about-securityscopes }
|
||||
|
||||
Vous pouvez utiliser `SecurityScopes` à n’importe quel endroit, et à de multiples endroits, il n’a 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 d’accè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 l’utiliser pour vérifier qu’un 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 d’accès*.
|
||||
|
||||
Elles seront vérifiées indépendamment pour chaque *chemin d’accès*.
|
||||
|
||||
## Tester { #check-it }
|
||||
|
||||
Si vous ouvrez la documentation de l’API, 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 d’accéder à `/users/me/` ou `/users/me/items/`, vous obtiendrez une erreur indiquant que vous n’avez 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/`.
|
||||
|
||||
C’est ce qui arriverait à une application tierce qui tenterait d’accéder à l’un de ces *chemins d’accès* avec un jeton fourni par un utilisateur, selon le nombre de permissions que l’utilisateur a accordées à l’application.
|
||||
|
||||
## À propos des intégrations tierces { #about-third-party-integrations }
|
||||
|
||||
Dans cet exemple, nous utilisons le flux OAuth2 « password ».
|
||||
|
||||
C’est 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 d’autres se connecteraient (c.-à-d., si vous construisez un fournisseur d’authentification équivalent à Facebook, Google, GitHub, etc.), vous devez utiliser l’un 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 d’authentification 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 d’authentification 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 d’accès](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), vous pouvez aussi utiliser `Security` avec des `scopes` à cet endroit.
|
||||
@@ -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.
|
||||
@@ -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) sous‑application(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 sous‑application.
|
||||
|
||||
### 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 sous‑application et ses *chemins d'accès*.
|
||||
|
||||
Cette sous‑application 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 sous‑application, `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 sous‑application à <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 sous‑application, n'incluant que ses propres _chemins d'accès_, tous sous le préfixe de sous‑chemin 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 sous‑application spécifique.
|
||||
|
||||
### Détails techniques : `root_path` { #technical-details-root-path }
|
||||
|
||||
Lorsque vous montez une sous‑application comme ci‑dessus, FastAPI se charge de communiquer le chemin de montage à la sous‑application au moyen d'un mécanisme de la spécification ASGI appelé `root_path`.
|
||||
|
||||
De cette manière, la sous‑application saura utiliser ce préfixe de chemin pour l'interface de documentation.
|
||||
|
||||
La sous‑application peut également avoir ses propres sous‑applications 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}.
|
||||
@@ -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** à l’URL `/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>.
|
||||
@@ -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 sous‑dé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ù l’on utilisait celle de la dépendance originale.
|
||||
|
||||
### Cas d’usage : service externe { #use-cases-external-service }
|
||||
|
||||
Par exemple, vous avez un fournisseur d’authentification externe à appeler.
|
||||
|
||||
Vous lui envoyez un token et il renvoie un utilisateur authentifié.
|
||||
|
||||
Ce fournisseur peut vous facturer à la requête, et l’appeler 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 l’appeler 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 l’attribut `app.dependency_overrides` { #use-the-app-dependency-overrides-attribute }
|
||||
|
||||
Pour ces cas, votre **FastAPI** application possède un attribut `app.dependency_overrides` ; c’est 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 n’importe 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 n’utilisez 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).
|
||||
|
||||
///
|
||||
@@ -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] *}
|
||||
@@ -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>.
|
||||
|
||||
///
|
||||
@@ -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.
|
||||
|
||||
///
|
||||
@@ -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>.
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
@@ -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 « 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.
|
||||
|
||||
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 « micro‑framework » 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 micro‑framework. 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 :
|
||||
L’opé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 micro‑framework 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 multi‑processus 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}.
|
||||
|
||||
+95
-85
@@ -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 l’option 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**) s’appuie 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 d’usage de concurrence avancés qui nécessitent des schémas plus élaborés dans votre propre code.
|
||||
|
||||
Et même si vous n’utilisiez 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 »).
|
||||
|
||||
J’ai créé une autre bibliothèque au-dessus d’AnyIO, 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.
|
||||
|
||||
+18
-18
@@ -1,34 +1,34 @@
|
||||
# Test de performance
|
||||
# Tests de performance { #benchmarks }
|
||||
|
||||
Les tests de performance de TechEmpower montrent que les applications **FastAPI** tournant sous Uvicorn comme <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">étant l'un des frameworks Python les plus rapides disponibles</a>, seulement inférieur à Starlette et Uvicorn (tous deux utilisés au cœur de FastAPI). (*)
|
||||
Les benchmarks indépendants de TechEmpower montrent que les applications **FastAPI** s’exécutant avec Uvicorn sont <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">parmi les frameworks Python les plus rapides disponibles</a>, seulement en dessous de Starlette et Uvicorn eux‑mêmes (tous deux utilisés en interne par FastAPI).
|
||||
|
||||
Mais en prêtant attention aux tests de performance et aux comparaisons, il faut tenir compte de ce qu'il suit.
|
||||
Mais en prêtant attention aux tests de performance et aux comparaisons, vous devez tenir compte de ce qui suit.
|
||||
|
||||
## Tests de performance et rapidité
|
||||
## Tests de performance et rapidité { #benchmarks-and-speed }
|
||||
|
||||
Lorsque vous vérifiez les tests de performance, il est commun de voir plusieurs outils de différents types comparés comme équivalents.
|
||||
|
||||
En particulier, on voit Uvicorn, Starlette et FastAPI comparés (parmi de nombreux autres outils).
|
||||
|
||||
Plus le problème résolu par un outil est simple, mieux seront les performances obtenues. Et la plupart des tests de performance ne prennent pas en compte les fonctionnalités additionnelles fournies par les outils.
|
||||
Plus le problème résolu par un outil est simple, meilleures seront les performances obtenues. Et la plupart des tests de performance ne prennent pas en compte les fonctionnalités additionnelles fournies par les outils.
|
||||
|
||||
La hiérarchie est la suivante :
|
||||
|
||||
* **Uvicorn** : un serveur ASGI
|
||||
* **Starlette** : (utilise Uvicorn) un micro-framework web
|
||||
* **FastAPI**: (utilise Starlette) un micro-framework pour API disposant de fonctionnalités additionnelles pour la création d'API, avec la validation des données, etc.
|
||||
* **Starlette** : (utilise Uvicorn) un microframework web
|
||||
* **FastAPI**: (utilise Starlette) un microframework pour API disposant de fonctionnalités additionnelles pour la création d'API, avec la validation des données, etc.
|
||||
|
||||
* **Uvicorn** :
|
||||
* A les meilleures performances, étant donné qu'il n'a pas beaucoup de code mis-à-part le serveur en lui-même.
|
||||
* On n'écrit pas une application avec uniquement Uvicorn. Cela signifie que le code devrait inclure plus ou moins, au minimum, tout le code offert par Starlette (ou **FastAPI**). Et si on fait cela, l'application finale apportera les mêmes complications que si on avait utilisé un framework et que l'on avait minimisé la quantité de code et de bugs.
|
||||
* Si on compare Uvicorn, il faut le comparer à d'autre applications de serveurs comme Daphne, Hypercorn, uWSGI, etc.
|
||||
* A les meilleures performances, étant donné qu'il n'a pas beaucoup de code mis à part le serveur en lui‑même.
|
||||
* On n'écrit pas une application directement avec Uvicorn. Cela signifie que le code devrait inclure, au minimum, plus ou moins tout le code offert par Starlette (ou **FastAPI**). Et si on fait cela, l'application finale aura la même surcharge que si on avait utilisé un framework, tout en minimisant la quantité de code et les bugs.
|
||||
* Si on compare Uvicorn, il faut le comparer à d'autres serveurs d'applications comme Daphne, Hypercorn, uWSGI, etc.
|
||||
* **Starlette** :
|
||||
* A les seconde meilleures performances après Uvicorn. Starlette utilise en réalité Uvicorn. De ce fait, il ne peut qu’être plus "lent" qu'Uvicorn car il requiert l'exécution de plus de code.
|
||||
* Cependant il nous apporte les outils pour construire une application web simple, avec un routage basé sur des chemins, etc.
|
||||
* Si on compare Starlette, il faut le comparer à d'autres frameworks web (ou micorframework) comme Sanic, Flask, Django, etc.
|
||||
* A les secondes meilleures performances après Uvicorn. En réalité, Starlette utilise Uvicorn. De ce fait, il ne peut qu’être plus « lent » qu'Uvicorn car il requiert l'exécution de plus de code.
|
||||
* Cependant, il apporte les outils pour construire une application web simple, avec un routage basé sur des chemins, etc.
|
||||
* Si on compare Starlette, il faut le comparer à d'autres frameworks web (ou microframeworks) comme Sanic, Flask, Django, etc.
|
||||
* **FastAPI** :
|
||||
* Comme Starlette, FastAPI utilise Uvicorn et ne peut donc pas être plus rapide que ce dernier.
|
||||
* FastAPI apporte des fonctionnalités supplémentaires à Starlette. Des fonctionnalités qui sont nécessaires presque systématiquement lors de la création d'une API, comme la validation des données, la sérialisation. En utilisant FastAPI, on obtient une documentation automatiquement (qui ne requiert aucune manipulation pour être mise en place).
|
||||
* Si on n'utilisait pas FastAPI mais directement Starlette (ou un outil équivalent comme Sanic, Flask, Responder, etc) il faudrait implémenter la validation des données et la sérialisation par nous-même. Le résultat serait donc le même dans les deux cas mais du travail supplémentaire serait à réaliser avec Starlette, surtout en considérant que la validation des données et la sérialisation représentent la plus grande quantité de code à écrire dans une application.
|
||||
* De ce fait, en utilisant FastAPI on minimise le temps de développement, les bugs, le nombre de lignes de code, et on obtient les mêmes performances (si ce n'est de meilleurs performances) que l'on aurait pu avoir sans ce framework (en ayant à implémenter de nombreuses fonctionnalités importantes par nous-mêmes).
|
||||
* Si on compare FastAPI, il faut le comparer à d'autres frameworks web (ou ensemble d'outils) qui fournissent la validation des données, la sérialisation et la documentation, comme Flask-apispec, NestJS, Molten, etc.
|
||||
* Comme Starlette utilise Uvicorn et ne peut donc pas être plus rapide que lui, **FastAPI** utilise Starlette et ne peut donc pas être plus rapide que lui.
|
||||
* FastAPI apporte des fonctionnalités supplémentaires à Starlette. Des fonctionnalités dont vous avez presque toujours besoin lors de la création d'une API, comme la validation des données et la sérialisation. En l'utilisant, vous obtenez une documentation automatique « gratuitement » (la documentation automatique n'ajoute même pas de surcharge à l’exécution, elle est générée au démarrage).
|
||||
* Si on n'utilisait pas FastAPI mais directement Starlette (ou un autre outil comme Sanic, Flask, Responder, etc.), il faudrait implémenter toute la validation des données et la sérialisation soi‑même. L'application finale aurait donc la même surcharge que si elle avait été construite avec FastAPI. Et dans de nombreux cas, cette validation des données et cette sérialisation représentent la plus grande quantité de code écrite dans les applications.
|
||||
* De ce fait, en utilisant FastAPI on minimise le temps de développement, les bugs, le nombre de lignes de code, et on obtient probablement les mêmes performances (voire de meilleures performances) que l'on aurait pu avoir sans ce framework (car il aurait fallu tout implémenter dans votre code).
|
||||
* Si on compare FastAPI, il faut le comparer à d'autres frameworks d’application web (ou ensembles d'outils) qui fournissent la validation des données, la sérialisation et la documentation, comme Flask-apispec, NestJS, Molten, etc. Des frameworks avec validation des données, sérialisation et documentation automatiques intégrées.
|
||||
|
||||
@@ -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>
|
||||
@@ -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 d’outils 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 vous‑mê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 ci‑dessus) :
|
||||
|
||||
* 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 moment‑là, 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 d’outils 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.
|
||||
|
||||
Concentrons‑nous 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 elle‑mê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 d’outils 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 }
|
||||
|
||||
Rappelez‑vous, 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 d’outils 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 elle‑mê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 voulez‑vous 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. 🚀
|
||||
@@ -1,75 +1,150 @@
|
||||
# Déployer avec Docker
|
||||
# FastAPI dans des conteneurs - Docker { #fastapi-in-containers-docker }
|
||||
|
||||
Dans cette section, vous verrez des instructions et des liens vers des guides pour savoir comment :
|
||||
Lors du déploiement d'applications FastAPI, une approche courante consiste à construire une **image de conteneur Linux**. C'est généralement fait avec <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Vous pouvez ensuite déployer cette image de conteneur de plusieurs façons possibles.
|
||||
|
||||
* Faire de votre application **FastAPI** une image/conteneur Docker avec une performance maximale. En environ **5 min**.
|
||||
* (Optionnellement) comprendre ce que vous, en tant que développeur, devez savoir sur HTTPS.
|
||||
* Configurer un cluster en mode Docker Swarm avec HTTPS automatique, même sur un simple serveur à 5 dollars US/mois. En environ **20 min**.
|
||||
* Générer et déployer une application **FastAPI** complète, en utilisant votre cluster Docker Swarm, avec HTTPS, etc. En environ **10 min**.
|
||||
|
||||
Vous pouvez utiliser <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> pour le déploiement. Il présente plusieurs avantages comme la sécurité, la réplicabilité, la simplicité de développement, etc.
|
||||
|
||||
Si vous utilisez Docker, vous pouvez utiliser l'image Docker officielle :
|
||||
|
||||
## <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>
|
||||
|
||||
Cette image est dotée d'un mécanisme d'"auto-tuning", de sorte qu'il vous suffit d'ajouter votre code pour obtenir automatiquement des performances très élevées. Et sans faire de sacrifices.
|
||||
|
||||
Mais vous pouvez toujours changer et mettre à jour toutes les configurations avec des variables d'environnement ou des fichiers de configuration.
|
||||
L'utilisation de conteneurs Linux présente plusieurs avantages, notamment la **sécurité**, la **réplicabilité**, la **simplicité**, entre autres.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Pour voir toutes les configurations et options, rendez-vous sur la page de l'image Docker : <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
Vous êtes pressé et vous connaissez déjà tout ça ? Allez directement au [`Dockerfile` ci-dessous 👇](#build-a-docker-image-for-fastapi).
|
||||
|
||||
///
|
||||
|
||||
## Créer un `Dockerfile`
|
||||
|
||||
* Allez dans le répertoire de votre projet.
|
||||
* Créez un `Dockerfile` avec :
|
||||
<details>
|
||||
<summary>Aperçu du Dockerfile 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
FROM python:3.14
|
||||
|
||||
COPY ./app /app
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
COPY ./app /code/app
|
||||
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
|
||||
# Si vous exécutez derrière un proxy comme Nginx ou Traefik, ajoutez --proxy-headers
|
||||
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
|
||||
```
|
||||
|
||||
### Applications plus larges
|
||||
</details>
|
||||
|
||||
Si vous avez suivi la section sur la création d' [Applications avec plusieurs fichiers](../tutorial/bigger-applications.md){.internal-link target=_blank}, votre `Dockerfile` pourrait ressembler à ceci :
|
||||
## Qu'est-ce qu'un conteneur { #what-is-a-container }
|
||||
|
||||
```Dockerfile
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
|
||||
Les conteneurs (principalement les conteneurs Linux) sont un moyen très **léger** d'empaqueter des applications, y compris toutes leurs dépendances et les fichiers nécessaires, tout en les isolant des autres conteneurs (autres applications ou composants) dans le même système.
|
||||
|
||||
COPY ./app /app/app
|
||||
Les conteneurs Linux s'exécutent en utilisant le même noyau Linux que l'hôte (machine, machine virtuelle, serveur cloud, etc.). Cela signifie simplement qu'ils sont très légers (comparés à des machines virtuelles complètes émulant un système d'exploitation entier).
|
||||
|
||||
Ainsi, les conteneurs consomment **peu de ressources**, une quantité comparable à l'exécution directe des processus (alors qu'une machine virtuelle consommerait beaucoup plus).
|
||||
|
||||
Les conteneurs ont également leurs propres processus d'exécution **isolés** (généralement un seul processus), leur système de fichiers et leur réseau, ce qui simplifie le déploiement, la sécurité, le développement, etc.
|
||||
|
||||
## Qu'est-ce qu'une image de conteneur { #what-is-a-container-image }
|
||||
|
||||
Un **conteneur** s'exécute à partir d'une **image de conteneur**.
|
||||
|
||||
Une image de conteneur est une version **statique** de tous les fichiers, des variables d'environnement et de la commande/le programme par défaut devant être présents dans un conteneur. Ici, **statique** signifie que l'**image** du conteneur ne s'exécute pas, elle n'est pas en cours d'exécution, ce ne sont que les fichiers et métadonnées empaquetés.
|
||||
|
||||
Par opposition à une « **image de conteneur** » qui correspond aux contenus statiques stockés, un « **conteneur** » fait normalement référence à l'instance en cours d'exécution, la chose qui est **exécutée**.
|
||||
|
||||
Lorsque le **conteneur** est démarré et en cours d'exécution (démarré à partir d'une **image de conteneur**), il peut créer ou modifier des fichiers, des variables d'environnement, etc. Ces changements n'existeront que dans ce conteneur, mais ne persisteront pas dans l'image de conteneur sous-jacente (ils ne seront pas enregistrés sur le disque).
|
||||
|
||||
Une image de conteneur est comparable au **programme** et à ses contenus, par exemple `python` et un fichier `main.py`.
|
||||
|
||||
Et le **conteneur** lui-même (par opposition à l'**image de conteneur**) est l'instance en cours d'exécution réelle de l'image, comparable à un **processus**. En fait, un conteneur ne fonctionne que lorsqu'il a un **processus en cours d'exécution** (et normalement, il s'agit d'un seul processus). Le conteneur s'arrête lorsqu'aucun processus n'y est en cours d'exécution.
|
||||
|
||||
## Images de conteneur { #container-images }
|
||||
|
||||
Docker a été l'un des principaux outils pour créer et gérer des **images de conteneur** et des **conteneurs**.
|
||||
|
||||
Et il existe un <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> public avec des **images de conteneur officielles** pré-construites pour de nombreux outils, environnements, bases de données et applications.
|
||||
|
||||
Par exemple, il existe une <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">image Python officielle</a>.
|
||||
|
||||
Et il existe beaucoup d'autres images pour différentes choses comme des bases de données, par exemple :
|
||||
|
||||
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
|
||||
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
|
||||
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
|
||||
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a>, etc.
|
||||
|
||||
En utilisant une image de conteneur pré-construite, il est très facile de **combiner** et d'utiliser différents outils. Par exemple, pour essayer une nouvelle base de données. Dans la plupart des cas, vous pouvez utiliser les **images officielles** et simplement les configurer avec des variables d'environnement.
|
||||
|
||||
Ainsi, dans de nombreux cas, vous pouvez apprendre les conteneurs et Docker et réutiliser ces connaissances avec de nombreux outils et composants différents.
|
||||
|
||||
Vous exécuteriez donc **plusieurs conteneurs** avec des éléments différents, comme une base de données, une application Python, un serveur web avec une application frontend React, et les connecter entre eux via leur réseau interne.
|
||||
|
||||
Tous les systèmes de gestion de conteneurs (comme Docker ou Kubernetes) disposent de ces fonctionnalités réseau intégrées.
|
||||
|
||||
## Conteneurs et processus { #containers-and-processes }
|
||||
|
||||
Une **image de conteneur** inclut normalement dans ses métadonnées le programme/la commande par défaut à exécuter lorsque le **conteneur** est démarré et les paramètres à transmettre à ce programme. Très similaire à ce que vous utiliseriez en ligne de commande.
|
||||
|
||||
Lorsqu'un **conteneur** est démarré, il exécutera cette commande/ce programme (bien que vous puissiez la/le remplacer et faire exécuter une autre commande/un autre programme).
|
||||
|
||||
Un conteneur fonctionne tant que le **processus principal** (commande ou programme) est en cours d'exécution.
|
||||
|
||||
Un conteneur a normalement un **seul processus**, mais il est aussi possible de démarrer des sous-processus à partir du processus principal, et ainsi vous aurez **plusieurs processus** dans le même conteneur.
|
||||
|
||||
Mais il n'est pas possible d'avoir un conteneur en cours d'exécution sans **au moins un processus en cours**. Si le processus principal s'arrête, le conteneur s'arrête.
|
||||
|
||||
## Construire une image Docker pour FastAPI { #build-a-docker-image-for-fastapi }
|
||||
|
||||
Très bien, construisons quelque chose maintenant ! 🚀
|
||||
|
||||
Je vais vous montrer comment construire une **image Docker** pour FastAPI **à partir de zéro**, basée sur l'image **officielle Python**.
|
||||
|
||||
C'est ce que vous voudrez faire dans **la plupart des cas**, par exemple :
|
||||
|
||||
* Utiliser **Kubernetes** ou des outils similaires
|
||||
* Exécuter sur un **Raspberry Pi**
|
||||
* Utiliser un service cloud qui exécuterait une image de conteneur pour vous, etc.
|
||||
|
||||
### Dépendances des paquets { #package-requirements }
|
||||
|
||||
Vous aurez normalement les **dépendances des paquets** de votre application dans un fichier.
|
||||
|
||||
Cela dépendra principalement de l'outil que vous utilisez pour **installer** ces dépendances.
|
||||
|
||||
La manière la plus courante consiste à avoir un fichier `requirements.txt` avec les noms des paquets et leurs versions, un par ligne.
|
||||
|
||||
Vous utiliserez bien sûr les mêmes idées que vous avez lues dans [À propos des versions de FastAPI](versions.md){.internal-link target=_blank} pour définir les plages de versions.
|
||||
|
||||
Par exemple, votre `requirements.txt` pourrait ressembler à :
|
||||
|
||||
```
|
||||
fastapi[standard]>=0.113.0,<0.114.0
|
||||
pydantic>=2.7.0,<3.0.0
|
||||
```
|
||||
|
||||
### Raspberry Pi et autres architectures
|
||||
Et vous installerez normalement ces dépendances de paquets avec `pip`, par exemple :
|
||||
|
||||
Si vous utilisez Docker sur un Raspberry Pi (qui a un processeur ARM) ou toute autre architecture, vous pouvez créer un `Dockerfile` à partir de zéro, basé sur une image de base Python (qui est multi-architecture) et utiliser Uvicorn seul.
|
||||
<div class="termy">
|
||||
|
||||
Dans ce cas, votre `Dockerfile` pourrait ressembler à ceci :
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.7
|
||||
|
||||
RUN pip install fastapi uvicorn
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
COPY ./app /app
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
```console
|
||||
$ pip install -r requirements.txt
|
||||
---> 100%
|
||||
Successfully installed fastapi pydantic
|
||||
```
|
||||
|
||||
## Créer le code **FastAPI**.
|
||||
</div>
|
||||
|
||||
* Créer un répertoire `app` et y entrer.
|
||||
* Créez un fichier `main.py` avec :
|
||||
/// info
|
||||
|
||||
Il existe d'autres formats et outils pour définir et installer des dépendances de paquets.
|
||||
|
||||
///
|
||||
|
||||
### Créer le code **FastAPI** { #create-the-fastapi-code }
|
||||
|
||||
* Créez un répertoire `app` et entrez dedans.
|
||||
* Créez un fichier vide `__init__.py`.
|
||||
* Créez un fichier `main.py` avec :
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -81,22 +156,168 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
* Vous devriez maintenant avoir une structure de répertoire telle que :
|
||||
### Dockerfile { #dockerfile }
|
||||
|
||||
Maintenant, dans le même répertoire de projet, créez un fichier `Dockerfile` avec :
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)!
|
||||
FROM python:3.14
|
||||
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
|
||||
# (3)!
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
# (4)!
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (5)!
|
||||
COPY ./app /code/app
|
||||
|
||||
# (6)!
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
1. Démarrer à partir de l'image de base Python officielle.
|
||||
|
||||
2. Définir le répertoire de travail courant sur `/code`.
|
||||
|
||||
C'est là que nous placerons le fichier `requirements.txt` et le répertoire `app`.
|
||||
|
||||
3. Copier le fichier des dépendances vers le répertoire `/code`.
|
||||
|
||||
Copier **uniquement** le fichier des dépendances en premier, pas le reste du code.
|
||||
|
||||
Comme ce fichier **ne change pas souvent**, Docker le détectera et utilisera le **cache** pour cette étape, ce qui activera le cache pour l'étape suivante aussi.
|
||||
|
||||
4. Installer les dépendances listées dans le fichier des dépendances.
|
||||
|
||||
L'option `--no-cache-dir` indique à `pip` de ne pas enregistrer localement les paquets téléchargés, car cela ne sert que si `pip` devait être relancé pour installer les mêmes paquets, mais ce n'est pas le cas lorsque l'on travaille avec des conteneurs.
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Le `--no-cache-dir` concerne uniquement `pip`, cela n'a rien à voir avec Docker ou les conteneurs.
|
||||
|
||||
///
|
||||
|
||||
L'option `--upgrade` indique à `pip` de mettre à niveau les paquets s'ils sont déjà installés.
|
||||
|
||||
Comme l'étape précédente de copie du fichier peut être détectée par le **cache Docker**, cette étape **utilisera également le cache Docker** lorsqu'il est disponible.
|
||||
|
||||
L'utilisation du cache à cette étape vous **fera gagner** beaucoup de **temps** lors de la reconstruction de l'image encore et encore pendant le développement, au lieu de **télécharger et installer** toutes les dépendances **à chaque fois**.
|
||||
|
||||
5. Copier le répertoire `./app` dans le répertoire `/code`.
|
||||
|
||||
Comme cela contient tout le code qui est ce qui **change le plus fréquemment**, le **cache** Docker ne sera pas facilement utilisé pour cette étape ou pour les **étapes suivantes**.
|
||||
|
||||
Il est donc important de placer cela **vers la fin** du `Dockerfile`, pour optimiser les temps de construction de l'image de conteneur.
|
||||
|
||||
6. Définir la **commande** pour utiliser `fastapi run`, qui utilise Uvicorn sous le capot.
|
||||
|
||||
`CMD` prend une liste de chaînes, chacune de ces chaînes correspond à ce que vous taperiez en ligne de commande séparé par des espaces.
|
||||
|
||||
Cette commande sera exécutée à partir du **répertoire de travail courant**, le même répertoire `/code` que vous avez défini plus haut avec `WORKDIR /code`.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Passez en revue ce que fait chaque ligne en cliquant sur chaque bulle numérotée dans le code. 👆
|
||||
|
||||
///
|
||||
|
||||
/// warning | Alertes
|
||||
|
||||
Vous devez vous assurer d'utiliser **toujours** la **forme exec** de l'instruction `CMD`, comme expliqué ci-dessous.
|
||||
|
||||
///
|
||||
|
||||
#### Utiliser `CMD` - Forme Exec { #use-cmd-exec-form }
|
||||
|
||||
L'instruction Docker <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> peut être écrite sous deux formes :
|
||||
|
||||
✅ Forme **Exec** :
|
||||
|
||||
```Dockerfile
|
||||
# ✅ À faire
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
⛔️ Forme **Shell** :
|
||||
|
||||
```Dockerfile
|
||||
# ⛔️ À ne pas faire
|
||||
CMD fastapi run app/main.py --port 80
|
||||
```
|
||||
|
||||
Assurez-vous d'utiliser toujours la forme **exec** pour garantir que FastAPI peut s'arrêter proprement et que les [événements de cycle de vie](../advanced/events.md){.internal-link target=_blank} sont déclenchés.
|
||||
|
||||
Vous pouvez en lire davantage dans la <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">documentation Docker sur les formes shell et exec</a>.
|
||||
|
||||
Cela peut être très visible lors de l'utilisation de `docker compose`. Voir cette section de la FAQ Docker Compose pour plus de détails techniques : <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Pourquoi mes services mettent-ils 10 secondes à se recréer ou à s'arrêter ?</a>.
|
||||
|
||||
#### Structure du répertoire { #directory-structure }
|
||||
|
||||
Vous devriez maintenant avoir une structure de répertoire comme :
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ └── main.py
|
||||
└── Dockerfile
|
||||
├── Dockerfile
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
## Construire l'image Docker
|
||||
#### Derrière un proxy de terminaison TLS { #behind-a-tls-termination-proxy }
|
||||
|
||||
* Allez dans le répertoire du projet (dans lequel se trouve votre `Dockerfile`, contenant votre répertoire `app`).
|
||||
Si vous exécutez votre conteneur derrière un proxy de terminaison TLS (load balancer) comme Nginx ou Traefik, ajoutez l'option `--proxy-headers`, cela indiquera à Uvicorn (via la CLI FastAPI) de faire confiance aux en-têtes envoyés par ce proxy lui indiquant que l'application s'exécute derrière HTTPS, etc.
|
||||
|
||||
```Dockerfile
|
||||
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
|
||||
```
|
||||
|
||||
#### Cache Docker { #docker-cache }
|
||||
|
||||
Il y a une astuce importante dans ce `Dockerfile`, nous copions d'abord **le fichier des dépendances seul**, pas le reste du code. Laissez-moi vous expliquer pourquoi.
|
||||
|
||||
```Dockerfile
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
```
|
||||
|
||||
Docker et d'autres outils **construisent** ces images de conteneur **de manière incrémentale**, en ajoutant **une couche au-dessus de l'autre**, en commençant par le haut du `Dockerfile` et en ajoutant tous les fichiers créés par chacune des instructions du `Dockerfile`.
|
||||
|
||||
Docker et des outils similaires utilisent également un **cache interne** lors de la construction de l'image : si un fichier n'a pas changé depuis la dernière construction de l'image de conteneur, alors il va **réutiliser la même couche** créée la dernière fois, au lieu de recopier le fichier et créer une nouvelle couche à partir de zéro.
|
||||
|
||||
Éviter simplement la copie des fichiers n'améliore pas nécessairement les choses de manière significative, mais comme il a utilisé le cache pour cette étape, il peut **utiliser le cache pour l'étape suivante**. Par exemple, il peut utiliser le cache pour l'instruction qui installe les dépendances avec :
|
||||
|
||||
```Dockerfile
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
```
|
||||
|
||||
Le fichier des dépendances **ne changera pas fréquemment**. Ainsi, en copiant uniquement ce fichier, Docker pourra **utiliser le cache** pour cette étape.
|
||||
|
||||
Et ensuite, Docker pourra **utiliser le cache pour l'étape suivante** qui télécharge et installe ces dépendances. Et c'est là que nous **gagnons beaucoup de temps**. ✨ ... et évitons l'ennui en attendant. 😪😆
|
||||
|
||||
Télécharger et installer les dépendances de paquets **peut prendre des minutes**, mais utiliser le **cache** ne **prendra que quelques secondes** au plus.
|
||||
|
||||
Et comme vous reconstruirez l'image de conteneur encore et encore pendant le développement pour vérifier que vos modifications de code fonctionnent, cela vous fera gagner beaucoup de temps cumulé.
|
||||
|
||||
Ensuite, vers la fin du `Dockerfile`, nous copions tout le code. Comme c'est ce qui **change le plus fréquemment**, nous le plaçons vers la fin, car presque toujours, tout ce qui suit cette étape ne pourra pas utiliser le cache.
|
||||
|
||||
```Dockerfile
|
||||
COPY ./app /code/app
|
||||
```
|
||||
|
||||
### Construire l'image Docker { #build-the-docker-image }
|
||||
|
||||
Maintenant que tous les fichiers sont en place, construisons l'image de conteneur.
|
||||
|
||||
* Allez dans le répertoire du projet (là où se trouve votre `Dockerfile`, contenant votre répertoire `app`).
|
||||
* Construisez votre image FastAPI :
|
||||
|
||||
<div class="termy">
|
||||
@@ -109,9 +330,17 @@ $ docker build -t myimage .
|
||||
|
||||
</div>
|
||||
|
||||
## Démarrer le conteneur Docker
|
||||
/// tip | Astuce
|
||||
|
||||
* Exécutez un conteneur basé sur votre image :
|
||||
Remarquez le `.` à la fin, équivalent à `./`, il indique à Docker le répertoire à utiliser pour construire l'image de conteneur.
|
||||
|
||||
Dans ce cas, c'est le même répertoire courant (`.`).
|
||||
|
||||
///
|
||||
|
||||
### Démarrer le conteneur Docker { #start-the-docker-container }
|
||||
|
||||
* Exécutez un conteneur basé sur votre image :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -121,65 +350,269 @@ $ docker run -d --name mycontainer -p 80:80 myimage
|
||||
|
||||
</div>
|
||||
|
||||
Vous disposez maintenant d'un serveur FastAPI optimisé dans un conteneur Docker. Configuré automatiquement pour votre
|
||||
serveur actuel (et le nombre de cœurs du CPU).
|
||||
## Vérifier { #check-it }
|
||||
|
||||
## Vérifier
|
||||
Vous devriez pouvoir le vérifier via l'URL de votre conteneur Docker, par exemple : <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> ou <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous devriez pouvoir accéder à votre application via l'URL de votre conteneur Docker, par exemple : <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> ou <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez quelque chose comme :
|
||||
Vous verrez quelque chose comme :
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
## Documentation interactive de l'API
|
||||
## Documentation interactive de l'API { #interactive-api-docs }
|
||||
|
||||
Vous pouvez maintenant visiter <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> ou <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
Vous pouvez maintenant aller sur <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> ou <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez la documentation interactive automatique de l'API (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
|
||||
Vous verrez la documentation interactive automatique de l'API (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
|
||||
|
||||

|
||||
|
||||
## Documentation de l'API alternative
|
||||
## Documentation alternative de l'API { #alternative-api-docs }
|
||||
|
||||
Et vous pouvez également aller sur <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> ou <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
Et vous pouvez aussi aller sur <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> ou <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (ou équivalent, en utilisant votre hôte Docker).
|
||||
|
||||
Vous verrez la documentation automatique alternative (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
|
||||
Vous verrez la documentation automatique alternative (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
|
||||
|
||||

|
||||
|
||||
## Traefik
|
||||
## Construire une image Docker avec un FastAPI mono-fichier { #build-a-docker-image-with-a-single-file-fastapi }
|
||||
|
||||
<a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a> est un reverse proxy/load balancer
|
||||
haute performance. Il peut faire office de "Proxy de terminaison TLS" (entre autres fonctionnalités).
|
||||
Si votre FastAPI est un seul fichier, par exemple `main.py` sans répertoire `./app`, votre structure de fichiers pourrait ressembler à ceci :
|
||||
|
||||
Il est intégré à Let's Encrypt. Ainsi, il peut gérer toutes les parties HTTPS, y compris l'acquisition et le renouvellement des certificats.
|
||||
```
|
||||
.
|
||||
├── Dockerfile
|
||||
├── main.py
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
Il est également intégré à Docker. Ainsi, vous pouvez déclarer vos domaines dans les configurations de chaque application et faire en sorte qu'elles lisent ces configurations, génèrent les certificats HTTPS et servent via HTTPS à votre application automatiquement, sans nécessiter aucune modification de leurs configurations.
|
||||
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.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
# (1)!
|
||||
COPY ./main.py /code/
|
||||
|
||||
# (2)!
|
||||
CMD ["fastapi", "run", "main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
1. Copier le fichier `main.py` directement dans le répertoire `/code` (sans répertoire `./app`).
|
||||
|
||||
2. Utiliser `fastapi run` pour servir votre application dans le fichier unique `main.py`.
|
||||
|
||||
Lorsque vous passez le fichier à `fastapi run`, il détectera automatiquement qu'il s'agit d'un fichier unique et non d'un package et saura comment l'importer et servir votre application FastAPI. 😎
|
||||
|
||||
## Concepts de déploiement { #deployment-concepts }
|
||||
|
||||
Parlons à nouveau de certains des mêmes [Concepts de déploiement](concepts.md){.internal-link target=_blank} en termes de conteneurs.
|
||||
|
||||
Les conteneurs sont principalement un outil pour simplifier le processus de **construction et de déploiement** d'une application, mais ils n'imposent pas une approche particulière pour gérer ces **concepts de déploiement**, et il existe plusieurs stratégies possibles.
|
||||
|
||||
La **bonne nouvelle**, c'est qu'avec chaque stratégie différente, il existe un moyen de couvrir tous les concepts de déploiement. 🎉
|
||||
|
||||
Passons en revue ces **concepts de déploiement** en termes de conteneurs :
|
||||
|
||||
* HTTPS
|
||||
* Exécution au démarrage
|
||||
* Redémarrages
|
||||
* Réplication (le nombre de processus en cours d'exécution)
|
||||
* Mémoire
|
||||
* Étapes préalables au démarrage
|
||||
|
||||
## HTTPS { #https }
|
||||
|
||||
Si l'on se concentre uniquement sur l'**image de conteneur** pour une application FastAPI (et plus tard sur le **conteneur** en cours d'exécution), HTTPS serait normalement géré **à l'extérieur** par un autre outil.
|
||||
|
||||
Cela pourrait être un autre conteneur, par exemple avec <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, gérant **HTTPS** et l'acquisition **automatique** des **certificats**.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Traefik s'intègre avec Docker, Kubernetes, et d'autres, donc il est très facile de configurer HTTPS pour vos conteneurs avec lui.
|
||||
|
||||
///
|
||||
|
||||
Alternativement, HTTPS pourrait être géré par un fournisseur cloud comme l'un de leurs services (tout en exécutant l'application dans un conteneur).
|
||||
|
||||
## Exécution au démarrage et redémarrages { #running-on-startup-and-restarts }
|
||||
|
||||
Il y a normalement un autre outil chargé de **démarrer et exécuter** votre conteneur.
|
||||
|
||||
Cela pourrait être **Docker** directement, **Docker Compose**, **Kubernetes**, un **service cloud**, etc.
|
||||
|
||||
Dans la plupart (ou toutes) des situations, il existe une option simple pour activer l'exécution du conteneur au démarrage et activer les redémarrages en cas d'échec. Par exemple, dans Docker, c'est l'option de ligne de commande `--restart`.
|
||||
|
||||
Sans utiliser de conteneurs, faire en sorte que les applications s'exécutent au démarrage et avec redémarrages peut être fastidieux et difficile. Mais en **travaillant avec des conteneurs**, dans la plupart des cas, cette fonctionnalité est incluse par défaut. ✨
|
||||
|
||||
## Réplication - Nombre de processus { #replication-number-of-processes }
|
||||
|
||||
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**.
|
||||
|
||||
Dans ces cas, vous voudrez probablement construire une **image Docker à partir de zéro** comme [expliqué ci-dessus](#dockerfile), en installant vos dépendances et en exécutant **un seul processus Uvicorn** au lieu d'utiliser plusieurs workers Uvicorn.
|
||||
|
||||
### Équilibreur de charge { #load-balancer }
|
||||
|
||||
Lors de l'utilisation de conteneurs, vous aurez normalement un composant **à l'écoute sur le port principal**. Cela pourrait être un autre conteneur qui est également un **proxy de terminaison TLS** pour gérer **HTTPS** ou un outil similaire.
|
||||
|
||||
Comme ce composant prend la **charge** des requêtes et la distribue entre les workers de façon (espérons-le) **équilibrée**, on l'appelle également communément un **équilibreur de charge**.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Le même composant de **proxy de terminaison TLS** utilisé pour HTTPS sera probablement aussi un **équilibreur de charge**.
|
||||
|
||||
///
|
||||
|
||||
Et en travaillant avec des conteneurs, le même système que vous utilisez pour les démarrer et les gérer dispose déjà d'outils internes pour transmettre la **communication réseau** (par ex. les requêtes HTTP) depuis cet **équilibreur de charge** (qui peut aussi être un **proxy de terminaison TLS**) vers le ou les conteneurs avec votre application.
|
||||
|
||||
### Un équilibreur de charge - Plusieurs conteneurs worker { #one-load-balancer-multiple-worker-containers }
|
||||
|
||||
Lorsque vous travaillez avec **Kubernetes** ou des systèmes de gestion de conteneurs distribués similaires, l'utilisation de leurs mécanismes réseau internes permet au **seul équilibreur de charge** à l'écoute sur le **port** principal de transmettre la communication (les requêtes) vers potentiellement **plusieurs conteneurs** exécutant votre application.
|
||||
|
||||
Chacun de ces conteneurs exécutant votre application aura normalement **un seul processus** (par ex. un processus Uvicorn exécutant votre application FastAPI). Ils seront tous des **conteneurs identiques**, exécutant la même chose, mais chacun avec son propre processus, sa mémoire, etc. De cette façon, vous profiterez de la **parallélisation** sur **différents cœurs** du CPU, voire sur **différentes machines**.
|
||||
|
||||
Et le système de conteneurs distribués avec l'**équilibreur de charge** **distribuera les requêtes** à chacun des conteneurs exécutant votre application **à tour de rôle**. Ainsi, chaque requête pourrait être traitée par l'un des multiples **conteneurs répliqués** exécutant votre application.
|
||||
|
||||
Et normalement cet **équilibreur de charge** pourra gérer des requêtes qui vont vers *d'autres* applications dans votre cluster (par ex. vers un autre domaine, ou sous un autre préfixe de chemin d'URL), et transmettra cette communication aux bons conteneurs pour *cette autre* application s'exécutant dans votre cluster.
|
||||
|
||||
### Un processus par conteneur { #one-process-per-container }
|
||||
|
||||
Dans ce type de scénario, vous voudrez probablement avoir **un seul processus (Uvicorn) par conteneur**, puisque vous gérez déjà la réplication au niveau du cluster.
|
||||
|
||||
Donc, dans ce cas, vous **ne voudrez pas** avoir plusieurs workers dans le conteneur, par exemple avec l'option de ligne de commande `--workers`. Vous voudrez avoir **un seul processus Uvicorn** par conteneur (mais probablement plusieurs conteneurs).
|
||||
|
||||
Avoir un autre gestionnaire de processus à l'intérieur du conteneur (comme ce serait le cas avec plusieurs workers) n'ajouterait que de la **complexité inutile** que vous gérez très probablement déjà avec votre système de cluster.
|
||||
|
||||
### Conteneurs avec plusieurs processus et cas particuliers { #containers-with-multiple-processes-and-special-cases }
|
||||
|
||||
Bien sûr, il existe des **cas particuliers** où vous pourriez vouloir avoir **un conteneur** avec plusieurs **processus worker Uvicorn** à l'intérieur.
|
||||
|
||||
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.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./requirements.txt /code/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||
|
||||
COPY ./app /code/app
|
||||
|
||||
# (1)!
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
|
||||
```
|
||||
|
||||
1. Ici, nous utilisons l'option de ligne de commande `--workers` pour définir le nombre de workers à 4.
|
||||
|
||||
Voici quelques exemples où cela pourrait avoir du sens :
|
||||
|
||||
#### Une application simple { #a-simple-app }
|
||||
|
||||
Vous pourriez vouloir un gestionnaire de processus dans le conteneur si votre application est **suffisamment simple** pour s'exécuter sur un **seul serveur**, pas un cluster.
|
||||
|
||||
#### Docker Compose { #docker-compose }
|
||||
|
||||
Vous pourriez déployer sur un **seul serveur** (pas un cluster) avec **Docker Compose**, donc vous n'auriez pas un moyen simple de gérer la réplication des conteneurs (avec Docker Compose) tout en préservant le réseau partagé et l'**équilibrage de charge**.
|
||||
|
||||
Vous pourriez alors vouloir avoir **un seul conteneur** avec un **gestionnaire de processus** qui démarre **plusieurs processus worker** à l'intérieur.
|
||||
|
||||
---
|
||||
|
||||
Avec ces informations et ces outils, passez à la section suivante pour tout combiner.
|
||||
L'idée principale est que **rien** de tout cela ne sont des **règles gravées dans la pierre** que vous devez suivre aveuglément. Vous pouvez utiliser ces idées pour **évaluer votre propre cas d'usage** et décider de la meilleure approche pour votre système, en vérifiant comment gérer les concepts suivants :
|
||||
|
||||
## Cluster en mode Docker Swarm avec Traefik et HTTPS
|
||||
* 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 au démarrage
|
||||
|
||||
Vous pouvez avoir un cluster en mode Docker Swarm configuré en quelques minutes (environ 20 min) avec un processus Traefik principal gérant HTTPS (y compris l'acquisition et le renouvellement des certificats).
|
||||
## Mémoire { #memory }
|
||||
|
||||
En utilisant le mode Docker Swarm, vous pouvez commencer par un "cluster" d'une seule machine (il peut même s'agir
|
||||
d'un serveur à 5 USD/mois) et ensuite vous pouvez vous développer autant que vous le souhaitez en ajoutant d'autres serveurs.
|
||||
Si vous exécutez **un seul processus par conteneur**, vous aurez une quantité de mémoire consommée plus ou moins bien définie, stable et limitée par chacun de ces conteneurs (plus d'un s'ils sont répliqués).
|
||||
|
||||
Pour configurer un cluster en mode Docker Swarm avec Traefik et la gestion de HTTPS, suivez ce guide :
|
||||
Vous pouvez alors définir ces mêmes limites et exigences de mémoire dans vos configurations pour votre système de gestion de conteneurs (par exemple dans **Kubernetes**). De cette façon, il pourra **répliquer les conteneurs** sur les **machines disponibles** en tenant compte de la quantité de mémoire dont ils ont besoin et de la quantité disponible sur les machines du cluster.
|
||||
|
||||
### <a href="https://medium.com/@tiangolo/docker-swarm-mode-and-traefik-for-a-https-cluster-20328dba6232" class="external-link" target="_blank">Docker Swarm Mode et Traefik pour un cluster HTTPS</a>
|
||||
Si votre application est **simple**, cela ne sera probablement **pas un problème**, et vous n'aurez peut-être pas besoin de spécifier des limites de mémoire strictes. Mais si vous **utilisez beaucoup de mémoire** (par exemple avec des modèles de **machine learning**), vous devez vérifier combien de mémoire vous consommez et ajuster le **nombre de conteneurs** qui s'exécutent sur **chaque machine** (et peut-être ajouter plus de machines à votre cluster).
|
||||
|
||||
### Déployer une application FastAPI
|
||||
Si vous exécutez **plusieurs processus par conteneur**, vous devez vous assurer que le nombre de processus démarrés ne **consomme pas plus de mémoire** que ce qui est disponible.
|
||||
|
||||
La façon la plus simple de tout mettre en place, serait d'utiliser les [**Générateurs de projet FastAPI**](../project-generation.md){.internal-link target=_blank}.
|
||||
## Étapes préalables au démarrage et conteneurs { #previous-steps-before-starting-and-containers }
|
||||
|
||||
Le génerateur de projet adéquat est conçu pour être intégré à ce cluster Docker Swarm avec Traefik et HTTPS décrit ci-dessus.
|
||||
Si vous utilisez des conteneurs (par ex. Docker, Kubernetes), alors il existe deux approches principales que vous pouvez utiliser.
|
||||
|
||||
Vous pouvez générer un projet en 2 min environ.
|
||||
### Plusieurs conteneurs { #multiple-containers }
|
||||
|
||||
Le projet généré a des instructions pour le déployer et le faire prend 2 min de plus.
|
||||
Si vous avez **plusieurs conteneurs**, probablement chacun exécutant un **seul processus** (par exemple, dans un cluster **Kubernetes**), alors vous voudrez probablement avoir un **conteneur séparé** effectuant le travail des **étapes préalables** dans un seul conteneur, exécutant un seul processus, **avant** d'exécuter les conteneurs worker répliqués.
|
||||
|
||||
/// info
|
||||
|
||||
Si vous utilisez Kubernetes, ce sera probablement un <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>.
|
||||
|
||||
///
|
||||
|
||||
Si, dans votre cas d'usage, il n'y a pas de problème à exécuter ces étapes préalables **plusieurs fois en parallèle** (par exemple si vous n'exécutez pas de migrations de base de données, mais vérifiez simplement si la base de données est prête), alors vous pourriez aussi simplement les mettre dans chaque conteneur juste avant de démarrer le processus principal.
|
||||
|
||||
### Un seul conteneur { #single-container }
|
||||
|
||||
Si vous avez une configuration simple, avec **un seul conteneur** qui démarre ensuite plusieurs **processus worker** (ou un seul processus aussi), vous pouvez alors exécuter ces étapes préalables dans le même conteneur, juste avant de démarrer le processus avec l'application.
|
||||
|
||||
### Image Docker de base { #base-docker-image }
|
||||
|
||||
Il existait une image Docker officielle FastAPI : <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Mais elle est désormais dépréciée. ⛔️
|
||||
|
||||
Vous ne devriez probablement **pas** utiliser cette image Docker de base (ni aucune autre similaire).
|
||||
|
||||
Si vous utilisez **Kubernetes** (ou autres) et que vous définissez déjà la **réplication** au niveau du cluster, avec plusieurs **conteneurs**. Dans ces cas, il est préférable de **construire une image à partir de zéro** comme décrit ci-dessus : [Construire une image Docker pour FastAPI](#build-a-docker-image-for-fastapi).
|
||||
|
||||
Et si vous devez avoir plusieurs workers, vous pouvez simplement utiliser l'option de ligne de commande `--workers`.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
L'image Docker a été créée à une époque où Uvicorn ne supportait pas la gestion et le redémarrage des workers morts, il fallait donc utiliser Gunicorn avec Uvicorn, ce qui ajoutait pas mal de complexité, uniquement pour que Gunicorn gère et redémarre les processus worker Uvicorn.
|
||||
|
||||
Mais maintenant qu'Uvicorn (et la commande `fastapi`) supporte l'usage de `--workers`, il n'y a plus de raison d'utiliser une image Docker de base au lieu de construire la vôtre (c'est à peu près la même quantité de code 😅).
|
||||
|
||||
///
|
||||
|
||||
## Déployer l'image de conteneur { #deploy-the-container-image }
|
||||
|
||||
Après avoir une image de conteneur (Docker), il existe plusieurs façons de la déployer.
|
||||
|
||||
Par exemple :
|
||||
|
||||
* Avec **Docker Compose** sur un seul serveur
|
||||
* Avec un cluster **Kubernetes**
|
||||
* Avec un cluster Docker Swarm Mode
|
||||
* Avec un autre outil comme Nomad
|
||||
* Avec un service cloud qui prend votre image de conteneur et la déploie
|
||||
|
||||
## Image Docker avec `uv` { #docker-image-with-uv }
|
||||
|
||||
Si vous utilisez <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> pour installer et gérer votre projet, vous pouvez suivre leur <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">guide Docker pour uv</a>.
|
||||
|
||||
## Récapitulatif { #recap }
|
||||
|
||||
Avec les systèmes de conteneurs (par ex. avec **Docker** et **Kubernetes**), il devient assez simple de gérer tous les **concepts de déploiement** :
|
||||
|
||||
* HTTPS
|
||||
* Exécution au démarrage
|
||||
* Redémarrages
|
||||
* Réplication (le nombre de processus en cours d'exécution)
|
||||
* Mémoire
|
||||
* Étapes préalables au démarrage
|
||||
|
||||
Dans la plupart des cas, vous ne voudrez probablement pas utiliser d'image de base, et au contraire **construire une image de conteneur à partir de zéro** basée sur l'image Docker Python officielle.
|
||||
|
||||
En prenant soin de l'**ordre** des instructions dans le `Dockerfile` et du **cache Docker**, vous pouvez **minimiser les temps de construction**, maximiser votre productivité (et éviter l'ennui). 😎
|
||||
|
||||
@@ -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 d’attente si ce n’est 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 d’attente 😉).
|
||||
|
||||
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>
|
||||
|
||||
C’est 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 à l’origine de **FastAPI**.
|
||||
|
||||
Cela simplifie le processus de **création**, de **déploiement** et **d’accè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 d’une 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 d’autres fournisseurs cloud { #deploy-to-other-cloud-providers }
|
||||
|
||||
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI sur n’importe 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. 🤓
|
||||
@@ -1,56 +1,231 @@
|
||||
# À propos de HTTPS
|
||||
# À propos de HTTPS { #about-https }
|
||||
|
||||
Il est facile de penser que HTTPS peut simplement être "activé" ou non.
|
||||
Il est facile de supposer que HTTPS est quelque chose qui est simplement « activé » ou non.
|
||||
|
||||
Mais c'est beaucoup plus complexe que cela.
|
||||
|
||||
/// tip
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous êtes pressé ou si cela ne vous intéresse pas, passez aux sections suivantes pour obtenir des instructions étape par étape afin de tout configurer avec différentes techniques.
|
||||
Si vous êtes pressé ou si cela ne vous intéresse pas, continuez avec les sections suivantes pour obtenir des instructions étape par étape afin de tout configurer avec différentes techniques.
|
||||
|
||||
///
|
||||
|
||||
Pour apprendre les bases du HTTPS, du point de vue d'un utilisateur, consultez <a href="https://howhttps.works/"
|
||||
class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
Pour apprendre les bases du HTTPS, du point de vue d'un utilisateur, consultez <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
|
||||
Maintenant, du point de vue d'un développeur, voici plusieurs choses à avoir en tête en pensant au HTTPS :
|
||||
|
||||
* Pour le HTTPS, le serveur a besoin de "certificats" générés par une tierce partie.
|
||||
* Ces certificats sont en fait acquis auprès de la tierce partie, et non "générés".
|
||||
* Les certificats ont une durée de vie.
|
||||
* Ils expirent.
|
||||
* Puis ils doivent être renouvelés et acquis à nouveau auprès de la tierce partie.
|
||||
* Le cryptage de la connexion se fait au niveau du protocole TCP.
|
||||
* C'est une couche en dessous de HTTP.
|
||||
* Donc, le certificat et le traitement du cryptage sont faits avant HTTP.
|
||||
* TCP ne connaît pas les "domaines", seulement les adresses IP.
|
||||
* L'information sur le domaine spécifique demandé se trouve dans les données HTTP.
|
||||
* Les certificats HTTPS "certifient" un certain domaine, mais le protocole et le cryptage se font au niveau TCP, avant de savoir quel domaine est traité.
|
||||
* Par défaut, cela signifie que vous ne pouvez avoir qu'un seul certificat HTTPS par adresse IP.
|
||||
* Quelle que soit la taille de votre serveur ou la taille de chacune des applications qu'il contient.
|
||||
* Il existe cependant une solution à ce problème.
|
||||
* Il existe une extension du protocole TLS (celui qui gère le cryptage au niveau TCP, avant HTTP) appelée <a
|
||||
href="https://fr.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr
|
||||
title="Server Name Indication (indication du nom du serveur)">SNI (indication du nom du serveur)</abbr></a>.
|
||||
* Cette extension SNI permet à un seul serveur (avec une seule adresse IP) d'avoir plusieurs certificats HTTPS et de servir plusieurs domaines/applications HTTPS.
|
||||
* Pour que cela fonctionne, un seul composant (programme) fonctionnant sur le serveur, écoutant sur l'adresse IP publique, doit avoir tous les certificats HTTPS du serveur.
|
||||
* Après avoir obtenu une connexion sécurisée, le protocole de communication est toujours HTTP.
|
||||
* Le contenu est crypté, même s'il est envoyé avec le protocole HTTP.
|
||||
* Pour le HTTPS, **le serveur** doit **disposer de « certificats »** générés par une **tierce partie**.
|
||||
* Ces certificats sont en réalité **acquis** auprès de la tierce partie, et non « générés ».
|
||||
* Les certificats ont une **durée de vie**.
|
||||
* Ils **expirent**.
|
||||
* Puis ils doivent être **renouvelés**, **acquis à nouveau** auprès de la tierce partie.
|
||||
* Le cryptage de la connexion se fait au **niveau TCP**.
|
||||
* C'est une couche **en dessous de HTTP**.
|
||||
* Donc, la gestion du **certificat et du cryptage** est effectuée **avant HTTP**.
|
||||
* **TCP ne connaît pas les « domaines »**. Il ne connaît que les adresses IP.
|
||||
* L'information sur le **domaine spécifique** demandé se trouve dans les **données HTTP**.
|
||||
* Les **certificats HTTPS** « certifient » un **certain domaine**, mais le protocole et le cryptage se font au niveau TCP, **avant de savoir** quel domaine est traité.
|
||||
* **Par défaut**, cela signifie que vous ne pouvez avoir qu'**un seul certificat HTTPS par adresse IP**.
|
||||
* Quelle que soit la taille de votre serveur ou la petitesse de chacune des applications qu'il contient.
|
||||
* Il existe cependant une **solution** à ce problème.
|
||||
* Il existe une **extension** du protocole **TLS** (celui qui gère le cryptage au niveau TCP, avant HTTP) appelée **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication - Indication du nom du serveur">SNI</abbr></a>**.
|
||||
* Cette extension SNI permet à un seul serveur (avec une **seule adresse IP**) d'avoir **plusieurs certificats HTTPS** et de servir **plusieurs domaines/applications HTTPS**.
|
||||
* Pour que cela fonctionne, un **seul** composant (programme) fonctionnant sur le serveur, écoutant sur l'**adresse IP publique**, doit avoir **tous les certificats HTTPS** du serveur.
|
||||
* **Après** l'établissement d'une connexion sécurisée, le protocole de communication est **toujours HTTP**.
|
||||
* Le contenu est **crypté**, même s'il est envoyé avec le **protocole HTTP**.
|
||||
|
||||
Il est courant d'avoir un seul programme/serveur HTTP fonctionnant sur le serveur (la machine, l'hôte, etc.) et
|
||||
gérant toutes les parties HTTPS : envoyer les requêtes HTTP décryptées à l'application HTTP réelle fonctionnant sur
|
||||
le même serveur (dans ce cas, l'application **FastAPI**), prendre la réponse HTTP de l'application, la crypter en utilisant le certificat approprié et la renvoyer au client en utilisant HTTPS. Ce serveur est souvent appelé un <a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Proxy de terminaison TLS</a>.
|
||||
Il est courant d'avoir **un seul programme/serveur HTTP** fonctionnant sur le serveur (la machine, l'hôte, etc.) et **gérant toutes les parties HTTPS** : recevoir les **requêtes HTTPS chiffrées**, envoyer les **requêtes HTTP déchiffrées** à l'application HTTP réelle fonctionnant sur le même serveur (l'application **FastAPI**, dans ce cas), prendre la **réponse HTTP** de l'application, la **chiffrer** en utilisant le **certificat HTTPS** approprié et la renvoyer au client en utilisant **HTTPS**. Ce serveur est souvent appelé un **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Proxy de terminaison TLS</a>**.
|
||||
|
||||
## Let's Encrypt
|
||||
Parmi les options que vous pourriez utiliser comme Proxy de terminaison TLS :
|
||||
|
||||
Avant Let's Encrypt, ces certificats HTTPS étaient vendus par des tiers de confiance.
|
||||
* Traefik (qui peut également gérer les renouvellements de certificats)
|
||||
* Caddy (qui peut également gérer les renouvellements de certificats)
|
||||
* Nginx
|
||||
* HAProxy
|
||||
|
||||
Le processus d'acquisition d'un de ces certificats était auparavant lourd, nécessitait pas mal de paperasses et les certificats étaient assez chers.
|
||||
## Let's Encrypt { #lets-encrypt }
|
||||
|
||||
Mais ensuite, <a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a> a été créé.
|
||||
Avant Let's Encrypt, ces **certificats HTTPS** étaient vendus par des tiers de confiance.
|
||||
|
||||
Il s'agit d'un projet de la Fondation Linux. Il fournit des certificats HTTPS gratuitement. De manière automatisée. Ces certificats utilisent toutes les sécurités cryptographiques standard et ont une durée de vie courte (environ 3 mois), de sorte que la sécurité est en fait meilleure en raison de leur durée de vie réduite.
|
||||
Le processus d'acquisition de l'un de ces certificats était auparavant lourd, nécessitait pas mal de paperasses et les certificats étaient assez chers.
|
||||
|
||||
Mais ensuite, **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** a été créé.
|
||||
|
||||
Il s'agit d'un projet de la Fondation Linux. Il fournit **des certificats HTTPS gratuitement**, de manière automatisée. Ces certificats utilisent toutes les sécurités cryptographiques standard et ont une durée de vie courte (environ 3 mois), de sorte que la **sécurité est en fait meilleure** en raison de leur durée de vie réduite.
|
||||
|
||||
Les domaines sont vérifiés de manière sécurisée et les certificats sont générés automatiquement. Cela permet également d'automatiser le renouvellement de ces certificats.
|
||||
|
||||
L'idée est d'automatiser l'acquisition et le renouvellement de ces certificats, afin que vous puissiez disposer d'un HTTPS sécurisé, gratuitement et pour toujours.
|
||||
L'idée est d'automatiser l'acquisition et le renouvellement de ces certificats, afin que vous puissiez disposer d'un **HTTPS sécurisé, gratuitement et pour toujours**.
|
||||
|
||||
## HTTPS pour les développeurs { #https-for-developers }
|
||||
|
||||
Voici un exemple de ce à quoi pourrait ressembler une API HTTPS, étape par étape, en portant principalement attention aux idées importantes pour les développeurs.
|
||||
|
||||
### Nom de domaine { #domain-name }
|
||||
|
||||
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 <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**.
|
||||
|
||||
Vous feriez probablement cela une seule fois, la première fois, lors de la mise en place de l'ensemble.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Cette partie relative au nom de domaine intervient bien avant HTTPS, mais comme tout dépend du domaine et de l'adresse IP, il vaut la peine de la mentionner ici.
|
||||
|
||||
///
|
||||
|
||||
### DNS { #dns }
|
||||
|
||||
Concentrons-nous maintenant sur toutes les parties réellement liées à HTTPS.
|
||||
|
||||
D'abord, le navigateur vérifierait auprès des **serveurs DNS** quelle est l'**IP du domaine**, dans ce cas, `someapp.example.com`.
|
||||
|
||||
Les serveurs DNS indiqueraient au navigateur d'utiliser une **adresse IP** spécifique. Ce serait l'adresse IP publique utilisée par votre serveur, celle que vous avez configurée dans les serveurs DNS.
|
||||
|
||||
<img src="/img/deployment/https/https01.drawio.svg">
|
||||
|
||||
### Début de la négociation TLS (Handshake) { #tls-handshake-start }
|
||||
|
||||
Le navigateur communiquerait ensuite avec cette adresse IP sur le **port 443** (le port HTTPS).
|
||||
|
||||
La première partie de la communication consiste simplement à établir la connexion entre le client et le serveur et à décider des clés cryptographiques qu'ils utiliseront, etc.
|
||||
|
||||
<img src="/img/deployment/https/https02.drawio.svg">
|
||||
|
||||
Cette interaction entre le client et le serveur pour établir la connexion TLS s'appelle la **négociation TLS (TLS handshake)**.
|
||||
|
||||
### TLS avec l'extension SNI { #tls-with-sni-extension }
|
||||
|
||||
**Un seul processus** sur le serveur peut écouter sur un **port** spécifique d'une **adresse IP** spécifique. Il pourrait y avoir d'autres processus écoutant sur d'autres ports de la même adresse IP, mais un seul pour chaque combinaison d'adresse IP et de port.
|
||||
|
||||
TLS (HTTPS) utilise par défaut le port spécifique `443`. C'est donc le port dont nous aurions besoin.
|
||||
|
||||
Comme un seul processus peut écouter sur ce port, le processus qui le ferait serait le **Proxy de terminaison TLS**.
|
||||
|
||||
Le Proxy de terminaison TLS aurait accès à un ou plusieurs **certificats TLS** (certificats HTTPS).
|
||||
|
||||
En utilisant l'**extension SNI** mentionnée plus haut, le Proxy de terminaison TLS vérifierait lequel des certificats TLS (HTTPS) disponibles il devrait utiliser pour cette connexion, en choisissant celui qui correspond au domaine attendu par le client.
|
||||
|
||||
Dans ce cas, il utiliserait le certificat pour `someapp.example.com`.
|
||||
|
||||
<img src="/img/deployment/https/https03.drawio.svg">
|
||||
|
||||
Le client **fait déjà confiance** à l'entité qui a généré ce certificat TLS (dans ce cas Let's Encrypt, mais nous y reviendrons plus tard), il peut donc **vérifier** que le certificat est valide.
|
||||
|
||||
Ensuite, en utilisant le certificat, le client et le Proxy de terminaison TLS **décident comment chiffrer** le reste de la **communication TCP**. Cela termine la partie **négociation TLS**.
|
||||
|
||||
Après cela, le client et le serveur disposent d'une **connexion TCP chiffrée**, c'est ce que fournit TLS. Ils peuvent alors utiliser cette connexion pour démarrer la **communication HTTP** proprement dite.
|
||||
|
||||
Et c'est ce qu'est **HTTPS** : c'est simplement du **HTTP** à l'intérieur d'une **connexion TLS sécurisée** au lieu d'une connexion TCP pure (non chiffrée).
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Remarquez que le cryptage de la communication se produit au **niveau TCP**, pas au niveau HTTP.
|
||||
|
||||
///
|
||||
|
||||
### Requête HTTPS { #https-request }
|
||||
|
||||
Maintenant que le client et le serveur (spécifiquement le navigateur et le Proxy de terminaison TLS) ont une **connexion TCP chiffrée**, ils peuvent démarrer la **communication HTTP**.
|
||||
|
||||
Ainsi, le client envoie une **requête HTTPS**. Ce n'est qu'une requête HTTP à travers une connexion TLS chiffrée.
|
||||
|
||||
<img src="/img/deployment/https/https04.drawio.svg">
|
||||
|
||||
### Déchiffrer la requête { #decrypt-the-request }
|
||||
|
||||
Le Proxy de terminaison TLS utiliserait le chiffrement convenu pour **déchiffrer la requête**, et transmettrait la **requête HTTP en clair (déchiffrée)** au processus exécutant l'application (par exemple un processus avec Uvicorn exécutant l'application FastAPI).
|
||||
|
||||
<img src="/img/deployment/https/https05.drawio.svg">
|
||||
|
||||
### Réponse HTTP { #http-response }
|
||||
|
||||
L'application traiterait la requête et enverrait une **réponse HTTP en clair (non chiffrée)** au Proxy de terminaison TLS.
|
||||
|
||||
<img src="/img/deployment/https/https06.drawio.svg">
|
||||
|
||||
### Réponse HTTPS { #https-response }
|
||||
|
||||
Le Proxy de terminaison TLS **chiffrerait ensuite la réponse** en utilisant la cryptographie convenue auparavant (qui a commencé avec le certificat pour `someapp.example.com`), et la renverrait au navigateur.
|
||||
|
||||
Ensuite, le navigateur vérifierait que la réponse est valide et chiffrée avec la bonne clé cryptographique, etc. Il **déchiffrerait la réponse** et la traiterait.
|
||||
|
||||
<img src="/img/deployment/https/https07.drawio.svg">
|
||||
|
||||
Le client (navigateur) saura que la réponse provient du bon serveur parce qu'elle utilise la cryptographie convenue auparavant à l'aide du **certificat HTTPS**.
|
||||
|
||||
### Applications multiples { #multiple-applications }
|
||||
|
||||
Sur le même serveur (ou les mêmes serveurs), il pourrait y avoir **plusieurs applications**, par exemple d'autres programmes d'API ou une base de données.
|
||||
|
||||
Un seul processus peut gérer l'adresse IP et le port spécifiques (le Proxy de terminaison TLS dans notre exemple), mais les autres applications/processus peuvent également s'exécuter sur le ou les serveurs, tant qu'ils n'essaient pas d'utiliser la même **combinaison d'adresse IP publique et de port**.
|
||||
|
||||
<img src="/img/deployment/https/https08.drawio.svg">
|
||||
|
||||
De cette façon, le Proxy de terminaison TLS pourrait gérer HTTPS et les certificats pour **plusieurs domaines**, pour plusieurs applications, puis transmettre les requêtes à la bonne application dans chaque cas.
|
||||
|
||||
### Renouvellement des certificats { #certificate-renewal }
|
||||
|
||||
À un moment donné dans le futur, chaque certificat **expirerait** (environ 3 mois après son acquisition).
|
||||
|
||||
Ensuite, il y aurait un autre programme (dans certains cas c'est un autre programme, dans d'autres cas cela pourrait être le même Proxy de terminaison TLS) qui communiquerait avec Let's Encrypt et renouvellerait le ou les certificats.
|
||||
|
||||
<img src="/img/deployment/https/https.drawio.svg">
|
||||
|
||||
Les **certificats TLS** sont **associés à un nom de domaine**, pas à une adresse IP.
|
||||
|
||||
Ainsi, pour renouveler les certificats, le programme de renouvellement doit **prouver** à l'autorité (Let's Encrypt) qu'il **« possède » et contrôle ce domaine**.
|
||||
|
||||
Pour ce faire, et pour s'adapter aux différents besoins des applications, il existe plusieurs façons de procéder. Parmi les plus courantes :
|
||||
|
||||
* **Modifier certains enregistrements DNS**.
|
||||
* Pour cela, le programme de renouvellement doit prendre en charge les API du fournisseur DNS ; ainsi, selon le fournisseur DNS que vous utilisez, cela peut être ou non une option.
|
||||
* **S'exécuter comme un serveur** (au moins pendant le processus d'acquisition du certificat) sur l'adresse IP publique associée au domaine.
|
||||
* Comme nous l'avons dit plus haut, un seul processus peut écouter sur une adresse IP et un port spécifiques.
|
||||
* C'est l'une des raisons pour lesquelles il est très utile que le même Proxy de terminaison TLS prenne également en charge le processus de renouvellement des certificats.
|
||||
* Sinon, vous pourriez avoir à arrêter le Proxy de terminaison TLS momentanément, démarrer le programme de renouvellement pour acquérir les certificats, puis les configurer avec le Proxy de terminaison TLS, et ensuite redémarrer le Proxy de terminaison TLS. Ce n'est pas idéal, car votre/vos application(s) ne seront pas disponibles pendant le temps où le Proxy de terminaison TLS est arrêté.
|
||||
|
||||
Tout ce processus de renouvellement, tout en continuant à servir l'application, est l'une des principales raisons pour lesquelles vous voudriez avoir un **système séparé pour gérer HTTPS** avec un Proxy de terminaison TLS, au lieu d'utiliser directement les certificats TLS avec le serveur d'application (par exemple Uvicorn).
|
||||
|
||||
## En-têtes Proxy Forwarded { #proxy-forwarded-headers }
|
||||
|
||||
Lorsque vous utilisez un proxy pour gérer HTTPS, votre **serveur d'application** (par exemple Uvicorn via FastAPI CLI) ne connaît rien du processus HTTPS, il communique en HTTP en clair avec le **Proxy de terminaison TLS**.
|
||||
|
||||
Ce **proxy** définirait normalement certains en-têtes HTTP à la volée avant de transmettre la requête au **serveur d'application**, pour informer le serveur d'application que la requête est **transmise** par le proxy.
|
||||
|
||||
/// 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>
|
||||
|
||||
///
|
||||
|
||||
Néanmoins, comme le **serveur d'application** ne sait pas qu'il se trouve derrière un **proxy** de confiance, par défaut, il ne ferait pas confiance à ces en-têtes.
|
||||
|
||||
Mais vous pouvez configurer le **serveur d'application** pour qu'il fasse confiance aux en-têtes transmis (*forwarded*) envoyés par le **proxy**. Si vous utilisez FastAPI CLI, vous pouvez utiliser l'*option CLI* `--forwarded-allow-ips` pour lui indiquer à partir de quelles IP il doit faire confiance à ces en-têtes transmis.
|
||||
|
||||
Par exemple, si le **serveur d'application** ne reçoit des communications que du **proxy** de confiance, vous pouvez définir `--forwarded-allow-ips="*"` pour lui faire faire confiance à toutes les IP entrantes, puisqu'il ne recevra des requêtes que depuis l'IP utilisée par le **proxy**.
|
||||
|
||||
De cette façon, l'application sera en mesure de savoir quelle est sa propre URL publique, si elle utilise HTTPS, le domaine, etc.
|
||||
|
||||
Cela serait utile, par exemple, pour gérer correctement les redirections.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous pouvez en savoir plus dans la documentation [Derrière un proxy - Activer les en-têtes transmis par le proxy](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank}
|
||||
|
||||
///
|
||||
|
||||
## Récapitulatif { #recap }
|
||||
|
||||
Disposer de **HTTPS** est très important, et assez **critique** dans la plupart des cas. La majeure partie de l'effort que vous, en tant que développeur, devez fournir autour de HTTPS consiste simplement à **comprendre ces concepts** et leur fonctionnement.
|
||||
|
||||
Mais une fois que vous connaissez les informations de base sur **HTTPS pour les développeurs**, vous pouvez facilement combiner et configurer différents outils pour vous aider à tout gérer simplement.
|
||||
|
||||
Dans certains des prochains chapitres, je vous montrerai plusieurs exemples concrets de configuration de **HTTPS** pour des applications **FastAPI**. 🔒
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Déploiement
|
||||
# Déploiement { #deployment }
|
||||
|
||||
Le déploiement d'une application **FastAPI** est relativement simple.
|
||||
|
||||
## Que signifie le déploiement
|
||||
## Que signifie le déploiement { #what-does-deployment-mean }
|
||||
|
||||
**Déployer** une application signifie effectuer les étapes nécessaires pour la rendre **disponible pour les
|
||||
utilisateurs**.
|
||||
@@ -14,7 +14,7 @@ l'application efficacement et sans interruption ni problème.
|
||||
Ceci contraste avec les étapes de **développement**, où vous êtes constamment en train de modifier le code, de le casser
|
||||
et de le réparer, d'arrêter et de redémarrer le serveur de développement, _etc._
|
||||
|
||||
## Stratégies de déploiement
|
||||
## Stratégies de déploiement { #deployment-strategies }
|
||||
|
||||
Il existe plusieurs façons de procéder, en fonction de votre cas d'utilisation spécifique et des outils que vous
|
||||
utilisez.
|
||||
@@ -22,6 +22,8 @@ utilisez.
|
||||
Vous pouvez **déployer un serveur** vous-même en utilisant une combinaison d'outils, vous pouvez utiliser un **service
|
||||
cloud** qui fait une partie du travail pour vous, ou encore d'autres options possibles.
|
||||
|
||||
Par exemple, nous, l'équipe derrière FastAPI, avons créé <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>, pour rendre le déploiement d'applications FastAPI dans le cloud aussi fluide que possible, avec la même expérience développeur que lorsque vous travaillez avec FastAPI.
|
||||
|
||||
Je vais vous montrer certains des principaux concepts que vous devriez probablement avoir à l'esprit lors du déploiement
|
||||
d'une application **FastAPI** (bien que la plupart de ces concepts s'appliquent à tout autre type d'application web).
|
||||
|
||||
|
||||
@@ -1,33 +1,82 @@
|
||||
# Exécuter un serveur manuellement - Uvicorn
|
||||
# Exécuter un serveur manuellement { #run-a-server-manually }
|
||||
|
||||
La principale chose dont vous avez besoin pour exécuter une application **FastAPI** sur une machine serveur distante est un programme serveur ASGI tel que **Uvicorn**.
|
||||
## Utiliser la commande `fastapi run` { #use-the-fastapi-run-command }
|
||||
|
||||
Il existe 3 principales alternatives :
|
||||
En bref, utilisez `fastapi run` pour servir votre application FastAPI :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```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 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> Started server process <b>[</b><font color="#34E2E2"><b>2306215</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.
|
||||
<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>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Cela fonctionnerait pour la plupart des cas. 😎
|
||||
|
||||
Vous pourriez utiliser cette commande par exemple pour démarrer votre application **FastAPI** dans un conteneur, sur un serveur, etc.
|
||||
|
||||
## Serveurs ASGI { #asgi-servers }
|
||||
|
||||
Allons un peu plus en détail.
|
||||
|
||||
FastAPI utilise un standard pour construire des frameworks web Python et des serveurs appelé <abbr title="Asynchronous Server Gateway Interface - Interface passerelle serveur asynchrone">ASGI</abbr>. FastAPI est un framework web ASGI.
|
||||
|
||||
La principale chose dont vous avez besoin pour exécuter une application **FastAPI** (ou toute autre application ASGI) sur une machine serveur distante est un programme serveur ASGI comme **Uvicorn**, c'est celui utilisé par défaut par la commande `fastapi`.
|
||||
|
||||
Il existe plusieurs alternatives, notamment :
|
||||
|
||||
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a> : un serveur ASGI haute performance.
|
||||
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a> : un serveur
|
||||
ASGI compatible avec HTTP/2 et Trio entre autres fonctionnalités.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a> : le serveur ASGI
|
||||
conçu pour Django Channels.
|
||||
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a> : un serveur ASGI compatible avec HTTP/2 et Trio entre autres fonctionnalités.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a> : le serveur ASGI conçu pour Django Channels.
|
||||
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a> : un serveur HTTP Rust pour les applications Python.
|
||||
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a> : NGINX Unit est un environnement d'exécution d'applications web léger et polyvalent.
|
||||
|
||||
## Machine serveur et programme serveur
|
||||
## Machine serveur et programme serveur { #server-machine-and-server-program }
|
||||
|
||||
Il y a un petit détail sur les noms à garder à l'esprit. 💡
|
||||
|
||||
Le mot "**serveur**" est couramment utilisé pour désigner à la fois l'ordinateur distant/cloud (la machine physique ou virtuelle) et également le programme qui s'exécute sur cette machine (par exemple, Uvicorn).
|
||||
Le mot « serveur » est couramment utilisé pour désigner à la fois l'ordinateur distant/cloud (la machine physique ou virtuelle) et également le programme qui s'exécute sur cette machine (par exemple, Uvicorn).
|
||||
|
||||
Gardez cela à l'esprit lorsque vous lisez "serveur" en général, cela pourrait faire référence à l'une de ces deux choses.
|
||||
Gardez cela à l'esprit lorsque vous lisez « serveur » en général, cela pourrait faire référence à l'une de ces deux choses.
|
||||
|
||||
Lorsqu'on se réfère à la machine distante, il est courant de l'appeler **serveur**, mais aussi **machine**, **VM** (machine virtuelle), **nœud**. Tout cela fait référence à un type de machine distante, exécutant Linux, en règle générale, sur laquelle vous exécutez des programmes.
|
||||
Lorsqu'on se réfère à la machine distante, il est courant de l'appeler **serveur**, mais aussi **machine**, **VM** (machine virtuelle), **nœud**. Tout cela fait référence à un type de machine distante, exécutant normalement Linux, sur laquelle vous exécutez des programmes.
|
||||
|
||||
## Installer le programme serveur { #install-the-server-program }
|
||||
|
||||
## Installer le programme serveur
|
||||
Lorsque vous installez FastAPI, il est fourni avec un serveur de production, Uvicorn, et vous pouvez le démarrer avec la commande `fastapi run`.
|
||||
|
||||
Vous pouvez installer un serveur compatible ASGI avec :
|
||||
Mais vous pouvez également installer un serveur ASGI manuellement.
|
||||
|
||||
//// tab | Uvicorn
|
||||
Vous devez créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, l'activer, puis vous pouvez installer l'application serveur.
|
||||
|
||||
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>, un serveur ASGI rapide comme l'éclair, basé sur uvloop et httptools.
|
||||
Par exemple, pour installer Uvicorn :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -39,39 +88,21 @@ $ pip install "uvicorn[standard]"
|
||||
|
||||
</div>
|
||||
|
||||
Un processus similaire s'appliquerait à tout autre programme de serveur ASGI.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
En ajoutant `standard`, Uvicorn va installer et utiliser quelques dépendances supplémentaires recommandées.
|
||||
|
||||
Cela inclut `uvloop`, le remplaçant performant de `asyncio`, qui fournit le gros gain de performance en matière de concurrence.
|
||||
Cela inclut `uvloop`, le remplaçant hautes performances de `asyncio`, qui fournit le gros gain de performance en matière de concurrence.
|
||||
|
||||
Lorsque vous installez FastAPI avec quelque chose comme `pip install "fastapi[standard]"`, vous obtenez déjà `uvicorn[standard]` aussi.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
## Exécuter le programme serveur { #run-the-server-program }
|
||||
|
||||
//// tab | Hypercorn
|
||||
|
||||
* <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, un serveur ASGI également compatible avec HTTP/2.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install hypercorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
...ou tout autre serveur ASGI.
|
||||
|
||||
////
|
||||
|
||||
## Exécutez le programme serveur
|
||||
|
||||
Vous pouvez ensuite exécuter votre application de la même manière que vous l'avez fait dans les tutoriels, mais sans l'option `--reload`, par exemple :
|
||||
|
||||
//// tab | Uvicorn
|
||||
Si vous avez installé un serveur ASGI manuellement, vous devrez normalement passer une chaîne d'import dans un format spécial pour qu'il importe votre application FastAPI :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -83,85 +114,44 @@ $ uvicorn main:app --host 0.0.0.0 --port 80
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
/// note | Remarque
|
||||
|
||||
//// tab | Hypercorn
|
||||
La commande `uvicorn main:app` fait référence à :
|
||||
|
||||
<div class="termy">
|
||||
* `main` : le fichier `main.py` (le « module » Python).
|
||||
* `app` : l'objet créé dans `main.py` avec la ligne `app = FastAPI()`.
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --bind 0.0.0.0:80
|
||||
C'est équivalent à :
|
||||
|
||||
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
|
||||
```Python
|
||||
from main import app
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
////
|
||||
|
||||
/// warning
|
||||
|
||||
N'oubliez pas de supprimer l'option `--reload` si vous l'utilisiez.
|
||||
|
||||
L'option `--reload` consomme beaucoup plus de ressources, est plus instable, etc.
|
||||
|
||||
Cela aide beaucoup pendant le **développement**, mais vous **ne devriez pas** l'utiliser en **production**.
|
||||
|
||||
///
|
||||
|
||||
## Hypercorn avec Trio
|
||||
Chaque programme de serveur ASGI alternatif aurait une commande similaire, vous pouvez en lire plus dans leur documentation respective.
|
||||
|
||||
Starlette et **FastAPI** sont basés sur
|
||||
<a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, qui les rend
|
||||
compatibles avec <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a>, de la bibliothèque standard Python et
|
||||
<a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
/// warning | Alertes
|
||||
|
||||
Néanmoins, Uvicorn n'est actuellement compatible qu'avec asyncio, et il utilise normalement <a href="https://github.
|
||||
com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a >, le remplaçant hautes performances de `asyncio`.
|
||||
Uvicorn et d'autres serveurs prennent en charge une option `--reload` utile pendant le développement.
|
||||
|
||||
Mais si vous souhaitez utiliser directement **Trio**, vous pouvez utiliser **Hypercorn** car il le prend en charge. ✨
|
||||
L'option `--reload` consomme beaucoup plus de ressources, est plus instable, etc.
|
||||
|
||||
### Installer Hypercorn avec Trio
|
||||
Cela aide beaucoup pendant le **développement**, mais vous **ne devriez pas** l'utiliser en **production**.
|
||||
|
||||
Vous devez d'abord installer Hypercorn avec le support Trio :
|
||||
///
|
||||
|
||||
<div class="termy">
|
||||
## Concepts de déploiement { #deployment-concepts }
|
||||
|
||||
```console
|
||||
$ pip install "hypercorn[trio]"
|
||||
---> 100%
|
||||
```
|
||||
Ces exemples exécutent le programme serveur (par exemple Uvicorn), en démarrant **un seul processus**, à l'écoute sur toutes les IP (`0.0.0.0`) sur un port prédéfini (par exemple `80`).
|
||||
|
||||
</div>
|
||||
C'est l'idée de base. Mais vous voudrez probablement vous occuper de certaines choses supplémentaires, comme :
|
||||
|
||||
### Exécuter avec Trio
|
||||
* 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écédant le démarrage
|
||||
|
||||
Ensuite, vous pouvez passer l'option de ligne de commande `--worker-class` avec la valeur `trio` :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --worker-class trio
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Et cela démarrera Hypercorn avec votre application en utilisant Trio comme backend.
|
||||
|
||||
Vous pouvez désormais utiliser Trio en interne dans votre application. Ou mieux encore, vous pouvez utiliser AnyIO pour que votre code reste compatible avec Trio et asyncio. 🎉
|
||||
|
||||
## Concepts de déploiement
|
||||
|
||||
Ces exemples lancent le programme serveur (e.g. Uvicorn), démarrant **un seul processus**, sur toutes les IPs (`0.0.
|
||||
0.0`) sur un port prédéfini (par example, `80`).
|
||||
|
||||
C'est l'idée de base. Mais vous vous préoccuperez probablement de certains concepts supplémentaires, tels que ... :
|
||||
|
||||
* la sécurité - HTTPS
|
||||
* l'exécution au démarrage
|
||||
* les redémarrages
|
||||
* la réplication (le nombre de processus en cours d'exécution)
|
||||
* la mémoire
|
||||
* les étapes précédant le démarrage
|
||||
|
||||
Je vous en dirai plus sur chacun de ces concepts, sur la façon de les aborder, et donnerai quelques exemples concrets avec des stratégies pour les traiter dans les prochains chapitres. 🚀
|
||||
Je vous en dirai plus sur chacun de ces concepts, sur la manière d'y réfléchir, et donnerai quelques exemples concrets avec des stratégies pour les gérer dans les prochains chapitres. 🚀
|
||||
|
||||
@@ -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. ✨
|
||||
@@ -1,101 +1,93 @@
|
||||
# À propos des versions de FastAPI
|
||||
# À propos des versions de FastAPI { #about-fastapi-versions }
|
||||
|
||||
**FastAPI** est déjà utilisé en production dans de nombreuses applications et systèmes. Et la couverture de test est maintenue à 100 %. Mais son développement est toujours aussi rapide.
|
||||
**FastAPI** est déjà utilisé en production dans de nombreuses applications et de nombreux systèmes. Et la couverture de tests est maintenue à 100 %. Mais son développement avance toujours rapidement.
|
||||
|
||||
De nouvelles fonctionnalités sont ajoutées fréquemment, des bogues sont corrigés régulièrement et le code est
|
||||
amélioré continuellement.
|
||||
De nouvelles fonctionnalités sont ajoutées fréquemment, des bogues sont corrigés régulièrement et le code s'améliore continuellement.
|
||||
|
||||
C'est pourquoi les versions actuelles sont toujours `0.x.x`, cela reflète que chaque version peut potentiellement
|
||||
recevoir des changements non rétrocompatibles. Cela suit les conventions de <a href="https://semver.org/" class="external-link"
|
||||
target="_blank">versionnage sémantique</a>.
|
||||
C'est pourquoi les versions actuelles sont toujours `0.x.x`, cela reflète que chaque version pourrait potentiellement comporter des changements non rétrocompatibles. Cela suit les conventions de <a href="https://semver.org/" class="external-link" target="_blank">versionnage sémantique</a>.
|
||||
|
||||
Vous pouvez créer des applications de production avec **FastAPI** dès maintenant (et vous le faites probablement depuis un certain temps), vous devez juste vous assurer que vous utilisez une version qui fonctionne correctement avec le reste de votre code.
|
||||
|
||||
## Épinglez votre version de `fastapi`
|
||||
## Épingler votre version de `fastapi` { #pin-your-fastapi-version }
|
||||
|
||||
Tout d'abord il faut "épingler" la version de **FastAPI** que vous utilisez à la dernière version dont vous savez
|
||||
qu'elle fonctionne correctement pour votre application.
|
||||
La première chose que vous devez faire est « épingler » la version de **FastAPI** que vous utilisez à la dernière version spécifique dont vous savez qu’elle fonctionne correctement pour votre application.
|
||||
|
||||
Par exemple, disons que vous utilisez la version `0.45.0` dans votre application.
|
||||
Par exemple, disons que vous utilisez la version `0.112.0` dans votre application.
|
||||
|
||||
Si vous utilisez un fichier `requirements.txt`, vous pouvez spécifier la version avec :
|
||||
Si vous utilisez un fichier `requirements.txt`, vous pouvez spécifier la version avec :
|
||||
|
||||
```txt
|
||||
fastapi==0.45.0
|
||||
fastapi[standard]==0.112.0
|
||||
```
|
||||
|
||||
ce qui signifierait que vous utiliseriez exactement la version `0.45.0`.
|
||||
ce qui signifierait que vous utiliseriez exactement la version `0.112.0`.
|
||||
|
||||
Ou vous pourriez aussi l'épingler avec :
|
||||
Ou vous pourriez aussi l'épingler avec :
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
fastapi[standard]>=0.112.0,<0.113.0
|
||||
```
|
||||
|
||||
cela signifierait que vous utiliseriez les versions `0.45.0` ou supérieures, mais inférieures à `0.46.0`, par exemple, une version `0.45.2` serait toujours acceptée.
|
||||
cela signifierait que vous utiliseriez les versions `0.112.0` ou supérieures, mais inférieures à `0.113.0`, par exemple, une version `0.112.2` serait toujours acceptée.
|
||||
|
||||
Si vous utilisez un autre outil pour gérer vos installations, comme Poetry, Pipenv, ou autres, ils ont tous un moyen que vous pouvez utiliser pour définir des versions spécifiques pour vos paquets.
|
||||
Si vous utilisez un autre outil pour gérer vos installations, comme `uv`, Poetry, Pipenv, ou autres, ils ont tous un moyen que vous pouvez utiliser pour définir des versions spécifiques pour vos paquets.
|
||||
|
||||
## Versions disponibles
|
||||
## Versions disponibles { #available-versions }
|
||||
|
||||
Vous pouvez consulter les versions disponibles (par exemple, pour vérifier quelle est la dernière version en date) dans les [Notes de version](../release-notes.md){.internal-link target=_blank}.
|
||||
|
||||
## À propos des versions
|
||||
## À propos des versions { #about-versions }
|
||||
|
||||
Suivant les conventions de versionnage sémantique, toute version inférieure à `1.0.0` peut potentiellement ajouter
|
||||
des changements non rétrocompatibles.
|
||||
Suivant les conventions de versionnage sémantique, toute version inférieure à `1.0.0` peut potentiellement ajouter des changements non rétrocompatibles.
|
||||
|
||||
FastAPI suit également la convention que tout changement de version "PATCH" est pour des corrections de bogues et
|
||||
des changements rétrocompatibles.
|
||||
FastAPI suit également la convention selon laquelle tout changement de version « PATCH » concerne des corrections de bogues et des changements rétrocompatibles.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Le "PATCH" est le dernier chiffre, par exemple, dans `0.2.3`, la version PATCH est `3`.
|
||||
Le « PATCH » est le dernier chiffre, par exemple, dans `0.2.3`, la version PATCH est `3`.
|
||||
|
||||
///
|
||||
|
||||
Donc, vous devriez être capable d'épingler une version comme suit :
|
||||
Donc, vous devriez être en mesure d'épingler une version comme suit :
|
||||
|
||||
```txt
|
||||
fastapi>=0.45.0,<0.46.0
|
||||
```
|
||||
|
||||
Les changements non rétrocompatibles et les nouvelles fonctionnalités sont ajoutés dans les versions "MINOR".
|
||||
Les changements non rétrocompatibles et les nouvelles fonctionnalités sont ajoutés dans les versions « MINOR ».
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Le "MINOR" est le numéro au milieu, par exemple, dans `0.2.3`, la version MINOR est `2`.
|
||||
Le « MINOR » est le numéro au milieu, par exemple, dans `0.2.3`, la version MINOR est `2`.
|
||||
|
||||
///
|
||||
|
||||
## Mise à jour des versions FastAPI
|
||||
## Mettre à niveau les versions de FastAPI { #upgrading-the-fastapi-versions }
|
||||
|
||||
Vous devriez tester votre application.
|
||||
Vous devez ajouter des tests pour votre application.
|
||||
|
||||
Avec **FastAPI** c'est très facile (merci à Starlette), consultez la documentation : [Testing](../tutorial/testing.md){.internal-link target=_blank}
|
||||
Avec **FastAPI** c'est très facile (merci à Starlette), consultez les documents : [Tests](../tutorial/testing.md){.internal-link target=_blank}
|
||||
|
||||
Après avoir effectué des tests, vous pouvez mettre à jour la version **FastAPI** vers une version plus récente, et vous assurer que tout votre code fonctionne correctement en exécutant vos tests.
|
||||
Après avoir des tests, vous pouvez mettre à niveau la version de **FastAPI** vers une version plus récente et vous assurer que tout votre code fonctionne correctement en exécutant vos tests.
|
||||
|
||||
Si tout fonctionne, ou après avoir fait les changements nécessaires, et que tous vos tests passent, vous pouvez
|
||||
épingler votre version de `fastapi` à cette nouvelle version récente.
|
||||
Si tout fonctionne, ou après avoir effectué les changements nécessaires, et que tous vos tests passent, vous pouvez alors épingler votre `fastapi` à cette nouvelle version récente.
|
||||
|
||||
## À propos de Starlette
|
||||
## À propos de Starlette { #about-starlette }
|
||||
|
||||
Vous ne devriez pas épingler la version de `starlette`.
|
||||
Vous ne devez pas épingler la version de `starlette`.
|
||||
|
||||
Différentes versions de **FastAPI** utiliseront une version spécifique plus récente de Starlette.
|
||||
|
||||
Ainsi, vous pouvez simplement laisser **FastAPI** utiliser la bonne version de Starlette.
|
||||
|
||||
## À propos de Pydantic
|
||||
## À propos de Pydantic { #about-pydantic }
|
||||
|
||||
Pydantic inclut des tests pour **FastAPI** avec ses propres tests, ainsi les nouvelles versions de Pydantic (au-dessus
|
||||
de `1.0.0`) sont toujours compatibles avec **FastAPI**.
|
||||
Pydantic inclut les tests pour **FastAPI** avec ses propres tests, ainsi les nouvelles versions de Pydantic (au-dessus de `1.0.0`) sont toujours compatibles avec FastAPI.
|
||||
|
||||
Vous pouvez épingler Pydantic à toute version supérieure à `1.0.0` qui fonctionne pour vous et inférieure à `2.0.0`.
|
||||
Vous pouvez épingler Pydantic à toute version supérieure à `1.0.0` qui fonctionne pour vous.
|
||||
|
||||
Par exemple :
|
||||
Par exemple :
|
||||
|
||||
```txt
|
||||
pydantic>=1.2.0,<2.0.0
|
||||
pydantic>=2.7.0,<3.0.0
|
||||
```
|
||||
|
||||
@@ -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).
|
||||
@@ -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">'/home/user/code/awesomeapp'</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 l’instance `FastAPI` (généralement nommée `app`), détermine la procédure d’importation 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 }
|
||||
|
||||
L’exé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 lorsqu’il est désactivé. Vous devez l’utiliser uniquement pour le développement. Il écoute aussi sur l’adresse IP `127.0.0.1`, qui est l’adresse IP permettant à votre machine de communiquer uniquement avec elle‑mê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 l’adresse 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. C’est ainsi que vous l’exécutez normalement en production, par exemple dans un conteneur.
|
||||
|
||||
Dans la plupart des cas, vous avez (et devez avoir) un « termination proxy » au‑dessus 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 vous‑même.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous pouvez en savoir plus à ce sujet dans la [documentation de déploiement](deployment/index.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
+99
-99
@@ -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.
|
||||
|
||||

|
||||
|
||||
* 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>.
|
||||
|
||||

|
||||
|
||||
### 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 "l’autocomplé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 l’une 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> :
|
||||
|
||||

|
||||
|
||||
* 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> :
|
||||
|
||||

|
||||
|
||||
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">l’un 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**
|
||||
L’inverse 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 %.
|
||||
|
||||
+205
-53
@@ -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 e‑mail) 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 **copier‑coller** 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** vous‑mê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 sous‑jacent 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, assurez‑vous 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 par‑dessus 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écutez‑le 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 }
|
||||
|
||||
* Aidez‑moi à 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 vous‑mê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>.
|
||||
Aidez‑moi à 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 ci‑dessus).
|
||||
* [Relire des Pull Requests](#review-pull-requests){.internal-link target=_blank} (voir la section ci‑dessus).
|
||||
|
||||
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, posez‑les 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 vous‑mê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. 🎁
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
///
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
@@ -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.
|
||||
@@ -0,0 +1,185 @@
|
||||
# Héberger en propre les ressources statiques de l’UI des docs personnalisées { #custom-docs-ui-static-assets-self-hosting }
|
||||
|
||||
Les documents de l’API 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 vous‑mê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` : l’URL où la page HTML des docs peut récupérer le schéma OpenAPI de votre API. Vous pouvez utiliser ici l’attribut `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` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **JavaScript**. C’est l’URL du CDN personnalisé.
|
||||
- `swagger_css_url` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **CSS**. C’est l’URL 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 l’API avec les identifiants acquis. Et interagir avec elle en utilisant la véritable authentification OAuth2.
|
||||
|
||||
Swagger UI s’en chargera en arrière‑plan 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 vous‑mê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 vous‑mê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 placez‑les 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 rendez‑vous 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 l’application 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 l’utilisation d’un 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` : l’URL où la page HTML des docs peut récupérer le schéma OpenAPI de votre API. Vous pouvez utiliser ici l’attribut `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` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **JavaScript**. **C’est celui que votre propre application sert désormais**.
|
||||
- `swagger_css_url` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **CSS**. **C’est 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 l’API avec les identifiants acquis. Et interagir avec elle en utilisant la véritable authentification OAuth2.
|
||||
|
||||
Swagger UI s’en chargera en arrière‑plan 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 l’UI avec des fichiers statiques { #test-static-files-ui }
|
||||
|
||||
Vous devriez maintenant pouvoir couper votre Wi‑Fi, 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.
|
||||
@@ -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] *}
|
||||
@@ -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">l’extension OpenAPI de ReDoc pour inclure un logo personnalisé</a>.
|
||||
|
||||
### **FastAPI** normal { #normal-fastapi }
|
||||
|
||||
Tout d’abord, écrivez votre application **FastAPI** comme d’habitude :
|
||||
|
||||
{* ../../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 l’extension 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 n’aura pas à générer le schéma à chaque fois qu’un utilisateur ouvre les documents de votre API.
|
||||
|
||||
Il ne sera généré qu’une 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">
|
||||
@@ -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}.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
///
|
||||
@@ -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. 🚶
|
||||
@@ -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 celui‑ci :
|
||||
|
||||
{* ../../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>
|
||||
@@ -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>. 😎
|
||||
+243
-156
@@ -1,11 +1,11 @@
|
||||
# FastAPI
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
.md-content .md-typeset h1 { display: none; }
|
||||
</style>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
<a href="https://fastapi.tiangolo.com/fr"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>Framework FastAPI, haute performance, facile à apprendre, rapide à coder, prêt pour la production</em>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
---
|
||||
|
||||
**Documentation** : <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
**Documentation** : <a href="https://fastapi.tiangolo.com/fr" target="_blank">https://fastapi.tiangolo.com/fr</a>
|
||||
|
||||
**Code Source** : <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
|
||||
|
||||
@@ -37,128 +37,130 @@ FastAPI est un framework web moderne et rapide (haute performance) pour la créa
|
||||
|
||||
Les principales fonctionnalités sont :
|
||||
|
||||
* **Rapidité** : De 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 IDE. <abbr title="également connu sous le nom d'auto-complétion, autocomplétion, IntelliSense">Complétion</abbr> complète. Moins de temps passé à déboguer.
|
||||
* **Facile** : Conçu pour être facile à utiliser et à apprendre. Moins de temps passé à lire la documentation.
|
||||
* **Concis** : Diminue la duplication de code. De nombreuses fonctionnalités liées à la déclaration de chaque paramètre. Moins de bugs.
|
||||
* **Robuste** : Obtenez un code prêt pour la production. Avec une documentation interactive automatique.
|
||||
* **Basé sur des normes** : Basé sur (et entièrement compatible avec) les standards ouverts pour les APIs : <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (précédemment connu sous le nom de Swagger) et <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
|
||||
* **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. <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.
|
||||
* **Basé sur des normes** : basé sur (et entièrement compatible avec) les standards ouverts pour les APIs : <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (précédemment connu sous le nom de Swagger) et <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
|
||||
|
||||
<small>* estimation basée sur des tests d'une équipe de développement interne, construisant des applications de production.</small>
|
||||
|
||||
## Sponsors
|
||||
## Sponsors { #sponsors }
|
||||
|
||||
<!-- sponsors -->
|
||||
|
||||
{% if sponsors %}
|
||||
### Sponsor clé de voûte { #keystone-sponsor }
|
||||
|
||||
{% for sponsor in sponsors.keystone -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor -%}
|
||||
|
||||
### Sponsors Or et Argent { #gold-and-silver-sponsors }
|
||||
|
||||
{% for sponsor in sponsors.gold -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor -%}
|
||||
{%- for sponsor in sponsors.silver -%}
|
||||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- /sponsors -->
|
||||
|
||||
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
|
||||
<a href="https://fastapi.tiangolo.com/fr/fastapi-people/#sponsors" class="external-link" target="_blank">Autres sponsors</a>
|
||||
|
||||
## Opinions
|
||||
## Opinions { #opinions }
|
||||
|
||||
"_[...] J'utilise beaucoup **FastAPI** ces derniers temps. [...] Je prévois de l'utiliser dans mon équipe pour tous les **services de ML chez Microsoft**. Certains d'entre eux seront intégrés dans le coeur de **Windows** et dans certains produits **Office**._"
|
||||
« _[...] J'utilise beaucoup **FastAPI** ces derniers temps. [...] Je prévois de l'utiliser dans mon équipe pour tous les **services de ML chez Microsoft**. Certains d'entre eux sont intégrés au cœur de **Windows** et à certains produits **Office**._ »
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/fastapi/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Nous avons adopté la bibliothèque **FastAPI** pour créer un serveur **REST** qui peut être interrogé pour obtenir des **prédictions**. [pour Ludwig]_"
|
||||
« _Nous avons adopté la bibliothèque **FastAPI** pour créer un serveur **REST** qui peut être interrogé pour obtenir des **prédictions**. [pour Ludwig]_ »
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin et Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, et Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_**Netflix** a le plaisir d'annoncer la sortie en open-source de notre framework d'orchestration de **gestion de crise** : **Dispatch** ! [construit avec **FastAPI**]_"
|
||||
« _**Netflix** est heureux d'annoncer la publication en open source de notre framework d'orchestration de **gestion de crise** : **Dispatch** ! [construit avec **FastAPI**]_ »
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Je suis très enthousiaste à propos de **FastAPI**. C'est un bonheur !_"
|
||||
« _Je suis plus qu'enthousiaste à propos de **FastAPI**. C'est tellement fun !_ »
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong>Auteur du podcast <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a></strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong>Animateur du podcast <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a></strong> <a href="https://x.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Honnêtement, ce que vous avez construit a l'air super solide et élégant. A bien des égards, c'est comme ça que je voulais que **Hug** soit - c'est vraiment inspirant de voir quelqu'un construire ça._"
|
||||
« _Honnêtement, ce que vous avez construit a l'air super solide et soigné. À bien des égards, c'est ce que je voulais que **Hug** soit — c'est vraiment inspirant de voir quelqu'un construire ça._ »
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong> Créateur de <a href="https://github.com/hugapi/hug" target="_blank">Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong>Créateur de <a href="https://github.com/hugapi/hug" target="_blank">Hug</a></strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Si vous cherchez à apprendre un **framework moderne** pour créer des APIs REST, regardez **FastAPI** [...] C'est rapide, facile à utiliser et à apprendre [...]_"
|
||||
« _Si vous cherchez à apprendre un **framework moderne** pour créer des APIs REST, regardez **FastAPI** [...] C'est rapide, facile à utiliser et facile à apprendre [...]_ »
|
||||
|
||||
"_Nous sommes passés à **FastAPI** pour nos **APIs** [...] Je pense que vous l'aimerez [...]_"
|
||||
« _Nous sommes passés à **FastAPI** pour nos **APIs** [...] Je pense que vous l'aimerez [...]_ »
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong>Fondateurs de <a href="https://explosion.ai" target="_blank">Explosion AI</a> - Créateurs de <a href="https://spacy.io" target="_blank">spaCy</a></strong> <a href="https://x.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://x.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"_Si quelqu'un cherche à construire une API Python de production, je recommande vivement **FastAPI**. Il est **bien conçu**, **simple à utiliser** et **très évolutif**. Il est devenu un **composant clé** dans notre stratégie de développement API first et il est à l'origine de nombreux automatismes et services tels que notre ingénieur virtuel TAC._"
|
||||
« _Si quelqu'un cherche à construire une API Python de production, je recommande vivement **FastAPI**. Il est **magnifiquement conçu**, **simple à utiliser** et **hautement scalable**. Il est devenu un **composant clé** de notre stratégie de développement API-first et alimente de nombreuses automatisations et services tels que notre ingénieur TAC virtuel._ »
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
## **Typer**, le FastAPI des <abbr title="Command Line Interface">CLI</abbr>
|
||||
## Mini documentaire FastAPI { #fastapi-mini-documentary }
|
||||
|
||||
Un <a href="https://www.youtube.com/watch?v=mpR8ngthqiE" class="external-link" target="_blank">mini documentaire FastAPI</a> est sorti fin 2025, vous pouvez le regarder en ligne :
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=mpR8ngthqiE" target="_blank"><img src="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg" alt="FastAPI Mini Documentary"></a>
|
||||
|
||||
## **Typer**, le FastAPI des CLIs { #typer-the-fastapi-of-clis }
|
||||
|
||||
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
|
||||
|
||||
Si vous souhaitez construire une application <abbr title="Command Line Interface">CLI</abbr> utilisable dans un terminal au lieu d'une API web, regardez <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
Si vous construisez une application <abbr title="Command Line Interface - Interface en ligne de commande">CLI</abbr> à utiliser dans un terminal au lieu d'une API web, regardez <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
|
||||
**Typer** est le petit frère de FastAPI. Et il est destiné à être le **FastAPI des <abbr title="Command Line Interface">CLI</abbr>**. ⌨️ 🚀
|
||||
**Typer** est le petit frère de FastAPI. Et il est destiné à être le **FastAPI des CLIs**. ⌨️ 🚀
|
||||
|
||||
## Prérequis
|
||||
## Prérequis { #requirements }
|
||||
|
||||
FastAPI repose sur les épaules de géants :
|
||||
|
||||
* <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> pour les parties web.
|
||||
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> pour les parties données.
|
||||
|
||||
## Installation
|
||||
## Installation { #installation }
|
||||
|
||||
Créez et activez un <a href="https://fastapi.tiangolo.com/fr/virtual-environments/" class="external-link" target="_blank">environnement virtuel</a> puis installez FastAPI :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install fastapi
|
||||
$ pip install "fastapi[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Vous aurez également besoin d'un serveur ASGI pour la production tel que <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a> ou <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
|
||||
**Remarque** : Vous devez vous assurer de mettre « fastapi[standard] » entre guillemets pour garantir que cela fonctionne dans tous les terminaux.
|
||||
|
||||
<div class="termy">
|
||||
## Exemple { #example }
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]"
|
||||
### Créer { #create-it }
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Exemple
|
||||
|
||||
### Créez
|
||||
|
||||
* Créez un fichier `main.py` avec :
|
||||
Créez un fichier `main.py` avec :
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -170,18 +172,16 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
<details markdown="1">
|
||||
<summary>Ou utilisez <code>async def</code> ...</summary>
|
||||
<summary>Ou utilisez <code>async def</code>...</summary>
|
||||
|
||||
Si votre code utilise `async` / `await`, utilisez `async def` :
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -193,28 +193,41 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
async def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
**Note**
|
||||
**Remarque** :
|
||||
|
||||
Si vous n'êtes pas familier avec cette notion, consultez la section _"Vous êtes pressés ?"_ à propos de <a href="https://fastapi.tiangolo.com/fr/async/#vous-etes-presses" target="_blank">`async` et `await` dans la documentation</a>.
|
||||
Si vous ne savez pas, consultez la section « Vous êtes pressés ? » à propos de <a href="https://fastapi.tiangolo.com/fr/async/#in-a-hurry" target="_blank">`async` et `await` dans la documentation</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Lancez
|
||||
### Lancer { #run-it }
|
||||
|
||||
Lancez le serveur avec :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
$ fastapi dev main.py
|
||||
|
||||
╭────────── FastAPI CLI - Development mode ───────────╮
|
||||
│ │
|
||||
│ Serving at: http://127.0.0.1:8000 │
|
||||
│ │
|
||||
│ API docs: http://127.0.0.1:8000/docs │
|
||||
│ │
|
||||
│ Running in development mode, for production use: │
|
||||
│ │
|
||||
│ fastapi run │
|
||||
│ │
|
||||
╰─────────────────────────────────────────────────────╯
|
||||
|
||||
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
INFO: Started reloader process [28720]
|
||||
INFO: Started server process [28722]
|
||||
INFO: Started reloader process [2248755] using WatchFiles
|
||||
INFO: Started server process [2248757]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: Application startup complete.
|
||||
```
|
||||
@@ -222,34 +235,34 @@ INFO: Application startup complete.
|
||||
</div>
|
||||
|
||||
<details markdown="1">
|
||||
<summary>À propos de la commande <code>uvicorn main:app --reload</code> ...</summary>
|
||||
<summary>À propos de la commande <code>fastapi dev main.py</code>...</summary>
|
||||
|
||||
La commande `uvicorn main:app` fait référence à :
|
||||
La commande `fastapi dev` lit votre fichier `main.py`, détecte l'application **FastAPI** qu'il contient et lance un serveur avec <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>.
|
||||
|
||||
* `main` : le fichier `main.py` (le "module" Python).
|
||||
* `app` : l'objet créé à l'intérieur de `main.py` avec la ligne `app = FastAPI()`.
|
||||
* `--reload` : fait redémarrer le serveur après des changements de code. À n'utiliser que pour le développement.
|
||||
Par défaut, `fastapi dev` démarre avec le rechargement automatique activé pour le développement local.
|
||||
|
||||
Vous pouvez en savoir plus dans la <a href="https://fastapi.tiangolo.com/fr/fastapi-cli/" target="_blank">documentation de la CLI FastAPI</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Vérifiez
|
||||
### Vérifier { #check-it }
|
||||
|
||||
Ouvrez votre navigateur à l'adresse <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
|
||||
|
||||
Vous obtenez alors cette réponse <abbr title="JavaScript Object Notation">JSON</abbr> :
|
||||
Vous verrez la réponse JSON :
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
Vous venez de créer une API qui :
|
||||
Vous avez déjà créé une API qui :
|
||||
|
||||
* Reçoit les requêtes HTTP pour les _chemins_ `/` et `/items/{item_id}`.
|
||||
* Les deux _chemins_ acceptent des <em>opérations</em> `GET` (également connu sous le nom de _méthodes_ HTTP).
|
||||
* Le _chemin_ `/items/{item_id}` a un _<abbr title="en anglais : path parameter">paramètre</abbr>_ `item_id` qui doit être un `int`.
|
||||
* Le _chemin_ `/items/{item_id}` a un _<abbr title="en anglais : query param">paramètre de requête</abbr>_ optionnel `q` de type `str`.
|
||||
* Reçoit des requêtes HTTP sur les _chemins_ `/` et `/items/{item_id}`.
|
||||
* Les deux _chemins_ acceptent des <em>opérations</em> `GET` (également connues sous le nom de _méthodes_ HTTP).
|
||||
* Le _chemin_ `/items/{item_id}` a un _paramètre de chemin_ `item_id` qui doit être un `int`.
|
||||
* Le _chemin_ `/items/{item_id}` a un _paramètre de requête_ optionnel `q` de type `str`.
|
||||
|
||||
### Documentation API interactive
|
||||
### Documentation API interactive { #interactive-api-docs }
|
||||
|
||||
Maintenant, rendez-vous sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
@@ -257,23 +270,21 @@ Vous verrez la documentation interactive automatique de l'API (fournie par <a hr
|
||||
|
||||

|
||||
|
||||
### Documentation API alternative
|
||||
### Documentation API alternative { #alternative-api-docs }
|
||||
|
||||
Et maintenant, rendez-vous 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 la documentation interactive automatique de l'API (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
|
||||
Vous verrez la documentation alternative automatique (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
|
||||
|
||||

|
||||
|
||||
## Exemple plus poussé
|
||||
## Mettre à niveau l'exemple { #example-upgrade }
|
||||
|
||||
Maintenant, modifiez le fichier `main.py` pour recevoir <abbr title="en anglais : body">le corps</abbr> d'une requête `PUT`.
|
||||
Modifiez maintenant le fichier `main.py` pour recevoir un corps depuis une requête `PUT`.
|
||||
|
||||
Déclarez ce corps en utilisant les types Python standards, grâce à Pydantic.
|
||||
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
Déclarez le corps en utilisant les types Python standard, grâce à Pydantic.
|
||||
|
||||
```Python hl_lines="2 7-10 23-25"
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -283,7 +294,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: Union[bool, None] = None
|
||||
is_offer: bool | None = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -292,7 +303,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@@ -301,35 +312,35 @@ def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
Le serveur se recharge normalement automatiquement (car vous avez pensé à `--reload` dans la commande `uvicorn` ci-dessus).
|
||||
Le serveur `fastapi dev` devrait se recharger automatiquement.
|
||||
|
||||
### Plus loin avec la documentation API interactive
|
||||
### Mettre à niveau la documentation API interactive { #interactive-api-docs-upgrade }
|
||||
|
||||
Maintenant, rendez-vous sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
* La documentation interactive de l'API sera automatiquement mise à jour, y compris le nouveau corps de la requête :
|
||||
* La documentation interactive de l'API sera automatiquement mise à jour, y compris le nouveau corps :
|
||||
|
||||

|
||||
|
||||
* Cliquez sur le bouton "Try it out", il vous permet de renseigner les paramètres et d'interagir directement avec l'API :
|
||||
* Cliquez sur le bouton « Try it out », il vous permet de renseigner les paramètres et d'interagir directement avec l'API :
|
||||
|
||||

|
||||
|
||||
* Cliquez ensuite sur le bouton "Execute", l'interface utilisateur communiquera avec votre API, enverra les paramètres, obtiendra les résultats et les affichera à l'écran :
|
||||
* Cliquez ensuite sur le bouton « Execute », l'interface utilisateur communiquera avec votre API, enverra les paramètres, obtiendra les résultats et les affichera à l'écran :
|
||||
|
||||

|
||||
|
||||
### Plus loin avec la documentation API alternative
|
||||
### Mettre à niveau la documentation API alternative { #alternative-api-docs-upgrade }
|
||||
|
||||
Et maintenant, rendez-vous sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
* La documentation alternative reflétera également le nouveau paramètre de requête et le nouveau corps :
|
||||
* La documentation alternative reflètera également le nouveau paramètre de requête et le nouveau corps :
|
||||
|
||||

|
||||
|
||||
### En résumé
|
||||
### En résumé { #recap }
|
||||
|
||||
En résumé, vous déclarez **une fois** les types de paramètres, <abbr title="en anglais : body">le corps</abbr> de la requête, etc. en tant que paramètres de fonction.
|
||||
En résumé, vous déclarez **une fois** les types de paramètres, le corps, etc. en tant que paramètres de fonction.
|
||||
|
||||
Vous faites cela avec les types Python standard modernes.
|
||||
|
||||
@@ -337,7 +348,7 @@ Vous n'avez pas à apprendre une nouvelle syntaxe, les méthodes ou les classes
|
||||
|
||||
Juste du **Python** standard.
|
||||
|
||||
Par exemple, pour un `int`:
|
||||
Par exemple, pour un `int` :
|
||||
|
||||
```Python
|
||||
item_id: int
|
||||
@@ -351,54 +362,54 @@ item: Item
|
||||
|
||||
... et avec cette déclaration unique, vous obtenez :
|
||||
|
||||
* Une assistance dans votre IDE, notamment :
|
||||
* la complétion.
|
||||
* Une assistance dans l'éditeur, notamment :
|
||||
* l'autocomplétion.
|
||||
* la vérification des types.
|
||||
* 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 <abbr title="JavaScript Object Notation">JSON</abbr> profondément imbriqués.
|
||||
* <abbr title="aussi connu sous le nom de : serialization, parsing, marshalling">Une conversion</abbr> des données d'entrée : venant du réseau et allant vers les données et types de Python, permettant de lire :
|
||||
* le <abbr title="JavaScript Object Notation">JSON</abbr>.
|
||||
* <abbr title="en anglais : path parameters">les paramètres du chemin</abbr>.
|
||||
* <abbr title="en anglais : query parameters">les paramètres de la requête</abbr>.
|
||||
* les cookies.
|
||||
* <abbr title="en anglais : headers">les en-têtes</abbr>.
|
||||
* <abbr title="en anglais : forms">les formulaires</abbr>.
|
||||
* <abbr title="en anglais : files">les fichiers</abbr>.
|
||||
* <abbr title="aussi connu sous le nom de : serialization, parsing, marshalling">La conversion</abbr> des données de sortie : conversion des données et types Python en données réseau (au format <abbr title="JavaScript Object Notation">JSON</abbr>), permettant de convertir :
|
||||
* les types Python (`str`, `int`, `float`, `bool`, `list`, etc).
|
||||
* les objets `datetime`.
|
||||
* les objets `UUID`.
|
||||
* les modèles de base de données.
|
||||
* ... et beaucoup plus.
|
||||
* La documentation API interactive automatique, avec 2 interfaces utilisateur au choix :
|
||||
* une validation même pour les objets JSON profondément imbriqués.
|
||||
* <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.
|
||||
* Cookies.
|
||||
* En-têtes.
|
||||
* Formulaires.
|
||||
* Fichiers.
|
||||
* <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`.
|
||||
* Modèles de base de données.
|
||||
* ... et bien plus.
|
||||
* Documentation API interactive automatique, avec 2 interfaces utilisateur au choix :
|
||||
* Swagger UI.
|
||||
* ReDoc.
|
||||
|
||||
---
|
||||
|
||||
Pour revenir à l'exemple de code précédent, **FastAPI** permet de :
|
||||
Pour revenir à l'exemple de code précédent, **FastAPI** va :
|
||||
|
||||
* Valider que `item_id` existe dans le chemin des requêtes `GET` et `PUT`.
|
||||
* Valider la présence d'un `item_id` dans le chemin pour les requêtes `GET` et `PUT`.
|
||||
* Valider que `item_id` est de type `int` pour les requêtes `GET` et `PUT`.
|
||||
* Si ce n'est pas le cas, le client voit une erreur utile et claire.
|
||||
* Vérifier qu'il existe un paramètre de requête facultatif nommé `q` (comme dans `http://127.0.0.1:8000/items/foo?q=somequery`) pour les requêtes `GET`.
|
||||
* Puisque le paramètre `q` est déclaré avec `= None`, il est facultatif.
|
||||
* Sans le `None`, il serait nécessaire (comme l'est <abbr title="en anglais : body">le corps</abbr> de la requête dans le cas du `PUT`).
|
||||
* Pour les requêtes `PUT` vers `/items/{item_id}`, de lire <abbr title="en anglais : body">le corps</abbr> en <abbr title="JavaScript Object Notation">JSON</abbr> :
|
||||
* Vérifier qu'il a un attribut obligatoire `name` qui devrait être un `str`.
|
||||
* Vérifier qu'il a un attribut obligatoire `prix` qui doit être un `float`.
|
||||
* Vérifier qu'il a un attribut facultatif `is_offer`, qui devrait être un `bool`, s'il est présent.
|
||||
* Tout cela fonctionnerait également pour les objets <abbr title="JavaScript Object Notation">JSON</abbr> profondément imbriqués.
|
||||
* Convertir de et vers <abbr title="JavaScript Object Notation">JSON</abbr> automatiquement.
|
||||
* Documenter tout avec OpenAPI, qui peut être utilisé par :
|
||||
* Les systèmes de documentation interactifs.
|
||||
* Les systèmes de génération automatique de code client, pour de nombreuses langues.
|
||||
* Si ce n'est pas le cas, le client verra une erreur utile et claire.
|
||||
* Vérifier s'il existe un paramètre de requête optionnel nommé `q` (comme dans `http://127.0.0.1:8000/items/foo?q=somequery`) pour les requêtes `GET`.
|
||||
* Comme le paramètre `q` est déclaré avec `= None`, il est optionnel.
|
||||
* Sans le `None`, il serait requis (comme l'est le corps dans le cas de `PUT`).
|
||||
* Pour les requêtes `PUT` vers `/items/{item_id}`, lire le corps au format JSON :
|
||||
* Vérifier qu'il a un attribut obligatoire `name` qui doit être un `str`.
|
||||
* Vérifier qu'il a un attribut obligatoire `price` qui doit être un `float`.
|
||||
* Vérifier qu'il a un attribut optionnel `is_offer`, qui doit être un `bool`, s'il est présent.
|
||||
* Tout cela fonctionne également pour les objets JSON profondément imbriqués.
|
||||
* Convertir automatiquement depuis et vers JSON.
|
||||
* Tout documenter avec OpenAPI, qui peut être utilisé par :
|
||||
* des systèmes de documentation interactive.
|
||||
* des systèmes de génération automatique de clients, pour de nombreux langages.
|
||||
* Fournir directement 2 interfaces web de documentation interactive.
|
||||
|
||||
---
|
||||
|
||||
Nous n'avons fait qu'effleurer la surface, mais vous avez déjà une idée de la façon dont tout cela fonctionne.
|
||||
Nous n'avons fait qu'effleurer la surface, mais vous avez déjà une idée de la façon dont tout fonctionne.
|
||||
|
||||
Essayez de changer la ligne contenant :
|
||||
|
||||
@@ -412,61 +423,137 @@ Essayez de changer la ligne contenant :
|
||||
... "item_name": item.name ...
|
||||
```
|
||||
|
||||
... vers :
|
||||
... à :
|
||||
|
||||
```Python
|
||||
... "item_price": item.price ...
|
||||
```
|
||||
|
||||
... et voyez comment votre éditeur complétera automatiquement les attributs et connaîtra leurs types :
|
||||
... et voyez comment votre éditeur complète automatiquement les attributs et connaît leurs types :
|
||||
|
||||

|
||||

|
||||
|
||||
Pour un exemple plus complet comprenant plus de fonctionnalités, voir le <a href="https://fastapi.tiangolo.com/fr/tutorial/">Tutoriel - Guide utilisateur</a>.
|
||||
|
||||
**Spoiler alert** : le tutoriel - guide utilisateur inclut :
|
||||
**Alerte spoiler** : le tutoriel - guide utilisateur inclut :
|
||||
|
||||
* Déclaration de **paramètres** provenant d'autres endroits différents comme : **<abbr title="en anglais : headers">en-têtes</abbr>.**, **cookies**, **champs de formulaire** et **fichiers**.
|
||||
* L'utilisation de **contraintes de validation** comme `maximum_length` ou `regex`.
|
||||
* Un **<abbr title="aussi connu sous le nom de composants, ressources, fournisseurs, services, injectables">système d'injection de dépendance </abbr>** très puissant et facile à utiliser .
|
||||
* Sécurité et authentification, y compris la prise en charge de **OAuth2** avec les **<abbr title="en anglais : JWT tokens">jetons <abbr title="JSON Web Tokens">JWT</abbr></abbr>** et l'authentification **HTTP Basic**.
|
||||
* Des techniques plus avancées (mais tout aussi faciles) pour déclarer les **modèles <abbr title="JavaScript Object Notation">JSON</abbr> profondément imbriqués** (grâce à Pydantic).
|
||||
* Intégration de **GraphQL** avec <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> et d'autres bibliothèques.
|
||||
* D'obtenir de nombreuses fonctionnalités supplémentaires (grâce à Starlette) comme :
|
||||
* 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 **<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.
|
||||
* De nombreuses fonctionnalités supplémentaires (grâce à Starlette) comme :
|
||||
* **WebSockets**
|
||||
* de tester le code très facilement avec `requests` et `pytest`
|
||||
* **<abbr title="Cross-Origin Resource Sharing">CORS</abbr>**
|
||||
* des tests extrêmement faciles basés sur HTTPX et `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ... et plus encore.
|
||||
|
||||
## Performance
|
||||
### Déployer votre application (optionnel) { #deploy-your-app-optional }
|
||||
|
||||
Les benchmarks TechEmpower indépendants montrent que les applications **FastAPI** s'exécutant sous Uvicorn sont <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank"> parmi les frameworks existants en Python les plus rapides </a>, juste derrière Starlette et Uvicorn (utilisés en interne par FastAPI). (*)
|
||||
Vous pouvez, si vous le souhaitez, déployer votre application FastAPI sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>, allez vous inscrire sur la liste d'attente si ce n'est pas déjà fait. 🚀
|
||||
|
||||
Si vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste d'attente 😉), vous pouvez déployer votre application avec une seule commande.
|
||||
|
||||
Avant de déployer, assurez-vous d'être connecté :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi login
|
||||
|
||||
You are logged in to FastAPI Cloud 🚀
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Puis déployez votre application :
|
||||
|
||||
<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>
|
||||
|
||||
C'est 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 construit par le même auteur et la même équipe derrière **FastAPI**.
|
||||
|
||||
Il simplifie le processus de **construction**, de **déploiement** et **d'accès** à une API avec un effort minimal.
|
||||
|
||||
Il apporte la même **expérience développeur** de la création d'applications avec FastAPI au **déploiement** dans le cloud. 🎉
|
||||
|
||||
FastAPI Cloud est le principal sponsor et financeur des projets open source *FastAPI and friends*. ✨
|
||||
|
||||
#### Déployer sur d'autres fournisseurs cloud { #deploy-to-other-cloud-providers }
|
||||
|
||||
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI sur n'importe quel fournisseur cloud de votre choix.
|
||||
|
||||
Suivez les guides de votre fournisseur cloud pour y déployer des applications FastAPI. 🤓
|
||||
|
||||
## Performance { #performance }
|
||||
|
||||
Les benchmarks TechEmpower indépendants montrent que les applications **FastAPI** s'exécutant sous Uvicorn sont <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">parmi les frameworks Python les plus rapides</a>, juste derrière Starlette et Uvicorn eux-mêmes (utilisés en interne par FastAPI). (*)
|
||||
|
||||
Pour en savoir plus, consultez la section <a href="https://fastapi.tiangolo.com/fr/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
|
||||
|
||||
## Dépendances facultatives
|
||||
## Dépendances { #dependencies }
|
||||
|
||||
Utilisées par Pydantic:
|
||||
FastAPI dépend de Pydantic et Starlette.
|
||||
|
||||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - pour la validation des adresses email.
|
||||
### Dépendances `standard` { #standard-dependencies }
|
||||
|
||||
Lorsque vous installez FastAPI avec `pip install "fastapi[standard]"`, il inclut le groupe `standard` de dépendances optionnelles :
|
||||
|
||||
Utilisées par Pydantic :
|
||||
|
||||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - pour la validation des adresses e-mail.
|
||||
|
||||
Utilisées par Starlette :
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Obligatoire si vous souhaitez utiliser `TestClient`.
|
||||
* <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 supporter le <abbr title="convertit la chaine de caractère d'une requête HTTP en donnée Python">"décodage"</abbr> de formulaire avec `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Obligatoire pour la prise en charge de `SessionMiddleware`.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Obligatoire pour le support `SchemaGenerator` de Starlette (vous n'en avez probablement pas besoin avec FastAPI).
|
||||
* <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 / Starlette :
|
||||
Utilisées par FastAPI :
|
||||
|
||||
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - Pour le serveur qui charge et sert votre application.
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Obligatoire si vous voulez utiliser `ORJSONResponse`.
|
||||
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - pour le serveur qui charge et sert votre application. Cela inclut `uvicorn[standard]`, qui comprend certaines dépendances (par ex. `uvloop`) nécessaires pour une haute performance.
|
||||
* `fastapi-cli[standard]` - pour fournir la commande `fastapi`.
|
||||
* Cela inclut `fastapi-cloud-cli`, qui vous permet de déployer votre application FastAPI sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
|
||||
|
||||
### Sans les dépendances `standard` { #without-standard-dependencies }
|
||||
|
||||
Si vous ne souhaitez pas inclure les dépendances optionnelles `standard`, vous pouvez installer avec `pip install fastapi` au lieu de `pip install "fastapi[standard]"`.
|
||||
|
||||
### Sans `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
|
||||
|
||||
Si vous souhaitez installer FastAPI avec les dépendances standard mais sans `fastapi-cloud-cli`, vous pouvez installer avec `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
|
||||
|
||||
### Dépendances optionnelles supplémentaires { #additional-optional-dependencies }
|
||||
|
||||
Il existe des dépendances supplémentaires que vous pourriez vouloir installer.
|
||||
|
||||
Dépendances optionnelles supplémentaires pour Pydantic :
|
||||
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - pour la gestion des paramètres.
|
||||
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - pour des types supplémentaires à utiliser avec Pydantic.
|
||||
|
||||
Dépendances optionnelles supplémentaires pour FastAPI :
|
||||
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Obligatoire si vous souhaitez utiliser `ORJSONResponse`.
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Obligatoire si vous souhaitez utiliser `UJSONResponse`.
|
||||
|
||||
Vous pouvez tout installer avec `pip install fastapi[all]`.
|
||||
|
||||
## Licence
|
||||
## Licence { #license }
|
||||
|
||||
Ce projet est soumis aux termes de la licence MIT.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Apprendre
|
||||
# Apprendre { #learn }
|
||||
|
||||
Voici les sections introductives et les tutoriels pour apprendre **FastAPI**.
|
||||
|
||||
Vous pouvez considérer ceci comme un **manuel**, un **cours**, la **méthode officielle** et recommandée pour appréhender FastAPI. 😎
|
||||
Vous pouvez considérer ceci comme un **livre**, un **cours**, la méthode **officielle** et recommandée pour apprendre FastAPI. 😎
|
||||
|
||||
@@ -1,84 +1,28 @@
|
||||
# Génération de projets - Modèle
|
||||
# Modèle Full Stack FastAPI { #full-stack-fastapi-template }
|
||||
|
||||
Vous pouvez utiliser un générateur de projet pour commencer, qui réalisera pour vous la mise en place de bases côté architecture globale, sécurité, base de données et premières routes d'API.
|
||||
Les modèles, bien qu'ils soient généralement livrés avec une configuration spécifique, sont conçus pour être flexibles et personnalisables. Cela vous permet de les modifier et de les adapter aux exigences de votre projet, ce qui en fait un excellent point de départ. 🏁
|
||||
|
||||
Un générateur de projet fera toujours une mise en place très subjective que vous devriez modifier et adapter suivant vos besoins, mais cela reste un bon point de départ pour vos projets.
|
||||
Vous pouvez utiliser ce modèle pour démarrer, car il inclut une grande partie de la configuration initiale, la sécurité, la base de données et quelques endpoints d'API déjà prêts pour vous.
|
||||
|
||||
## Full Stack FastAPI PostgreSQL
|
||||
Dépôt GitHub : <a href="https://github.com/tiangolo/full-stack-fastapi-template" class="external-link" target="_blank">Modèle Full Stack FastAPI</a>
|
||||
|
||||
GitHub : <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>
|
||||
## Modèle Full Stack FastAPI - Pile technologique et fonctionnalités { #full-stack-fastapi-template-technology-stack-and-features }
|
||||
|
||||
### Full Stack FastAPI PostgreSQL - Fonctionnalités
|
||||
|
||||
* Intégration **Docker** complète (basée sur Docker).
|
||||
* Déploiement Docker en mode <a href="https://docs.docker.com/engine/swarm/" class="external-link" target="_blank">Swarm</a>
|
||||
* Intégration **Docker Compose** et optimisation pour développement local.
|
||||
* Serveur web Python **prêt au déploiement** utilisant Uvicorn et Gunicorn.
|
||||
* Backend Python <a href="https://github.com/fastapi/fastapi" class="external-link" target="_blank">**FastAPI**</a> :
|
||||
* **Rapide** : Très hautes performances, comparables à **NodeJS** ou **Go** (grâce à Starlette et Pydantic).
|
||||
* **Intuitif** : Excellent support des éditeurs. <abbr title="aussi appelée auto-complétion, autocomplétion, IntelliSense...">Complétion</abbr> partout. Moins de temps passé à déboguer.
|
||||
* **Facile** : Fait pour être facile à utiliser et apprendre. Moins de temps passé à lire de la documentation.
|
||||
* **Concis** : Minimise la duplication de code. Plusieurs fonctionnalités à chaque déclaration de paramètre.
|
||||
* **Robuste** : Obtenez du code prêt pour être utilisé en production. Avec de la documentation automatique interactive.
|
||||
* **Basé sur des normes** : Basé sur (et totalement compatible avec) les normes ouvertes pour les APIs : <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> et <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
|
||||
* <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**Et bien d'autres fonctionnalités**</a> comme la validation automatique, la sérialisation, l'authentification avec OAuth2 JWT tokens, etc.
|
||||
* Hashage de **mots de passe sécurisé** par défaut.
|
||||
* Authentification par **jetons JWT**.
|
||||
* Modèles **SQLAlchemy** (indépendants des extensions Flask, afin qu'ils puissent être utilisés directement avec des *workers* Celery).
|
||||
* Modèle de démarrages basiques pour les utilisateurs (à modifier et supprimer au besoin).
|
||||
* Migrations **Alembic**.
|
||||
* **CORS** (partage des ressources entre origines multiples, ou *Cross Origin Resource Sharing*).
|
||||
* *Worker* **Celery** pouvant importer et utiliser les modèles et le code du reste du backend.
|
||||
* Tests du backend REST basés sur **Pytest**, intégrés dans Docker, pour que vous puissiez tester toutes les interactions de l'API indépendamment de la base de données. Étant exécutés dans Docker, les tests peuvent utiliser un nouvel entrepôt de données créé de zéro à chaque fois (vous pouvez donc utiliser ElasticSearch, MongoDB, CouchDB, etc. et juste tester que l'API fonctionne).
|
||||
* Intégration Python facile avec **Jupyter Kernels** pour le développement à distance ou intra-Docker avec des extensions comme Atom Hydrogen ou Visual Studio Code Jupyter.
|
||||
* Frontend **Vue** :
|
||||
* Généré avec Vue CLI.
|
||||
* Gestion de l'**Authentification JWT**.
|
||||
* Page de connexion.
|
||||
* Après la connexion, page de tableau de bord principal.
|
||||
* Tableau de bord principal avec création et modification d'utilisateurs.
|
||||
* Modification de ses propres caractéristiques utilisateur.
|
||||
* **Vuex**.
|
||||
* **Vue-router**.
|
||||
* **Vuetify** pour de magnifiques composants *material design*.
|
||||
* **TypeScript**.
|
||||
* Serveur Docker basé sur **Nginx** (configuré pour être facilement manipulé avec Vue-router).
|
||||
* Utilisation de *Docker multi-stage building*, pour ne pas avoir besoin de sauvegarder ou *commit* du code compilé.
|
||||
* Tests frontend exécutés à la compilation (pouvant être désactivés).
|
||||
* Fait aussi modulable que possible, pour pouvoir fonctionner comme tel, tout en pouvant être utilisé qu'en partie grâce à Vue CLI.
|
||||
* **PGAdmin** pour les bases de données PostgreSQL, facilement modifiable pour utiliser PHPMYAdmin ou MySQL.
|
||||
* **Flower** pour la surveillance de tâches Celery.
|
||||
* Équilibrage de charge entre le frontend et le backend avec **Traefik**, afin de pouvoir avoir les deux sur le même domaine, séparés par chemins, mais servis par différents conteneurs.
|
||||
* Intégration Traefik, comprenant la génération automatique de certificat **HTTPS** Let's Encrypt.
|
||||
* GitLab **CI** (intégration continue), comprenant des tests pour le frontend et le backend.
|
||||
|
||||
## Full Stack FastAPI Couchbase
|
||||
|
||||
GitHub : <a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a>
|
||||
|
||||
⚠️ **ATTENTION** ⚠️
|
||||
|
||||
Si vous démarrez un nouveau projet de zéro, allez voir les alternatives au début de cette page.
|
||||
|
||||
Par exemple, le générateur de projet <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">Full Stack FastAPI PostgreSQL</a> peut être une meilleure alternative, étant activement maintenu et utilisé et comprenant toutes les nouvelles fonctionnalités et améliorations.
|
||||
|
||||
Vous êtes toujours libre d'utiliser le générateur basé sur Couchbase si vous le voulez, cela devrait probablement fonctionner correctement, et si vous avez déjà un projet généré en utilisant ce dernier, cela devrait fonctionner aussi (et vous l'avez déjà probablement mis à jour suivant vos besoins).
|
||||
|
||||
Vous pouvez en apprendre plus dans la documentation du dépôt GithHub.
|
||||
|
||||
## Full Stack FastAPI MongoDB
|
||||
|
||||
...viendra surement plus tard, suivant le temps que j'ai. 😅 🎉
|
||||
|
||||
## Modèles d'apprentissage automatique avec spaCy et FastAPI
|
||||
|
||||
GitHub : <a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a>
|
||||
|
||||
## Modèles d'apprentissage automatique avec spaCy et FastAPI - Fonctionnalités
|
||||
|
||||
* Intégration d'un modèle NER **spaCy**.
|
||||
* Formatage de requête pour **Azure Cognitive Search**.
|
||||
* Serveur Python web **prêt à utiliser en production** utilisant Uvicorn et Gunicorn.
|
||||
* Déploiement CI/CD Kubernetes pour **Azure DevOps** (AKS).
|
||||
* **Multilangues**. Choisissez facilement l'une des langues intégrées à spaCy durant la mise en place du projet.
|
||||
* **Facilement généralisable** à d'autres bibliothèques similaires (Pytorch, Tensorflow), et non juste spaCy.
|
||||
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/fr) pour l'API backend Python.
|
||||
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) pour les interactions avec la base de données SQL en Python (ORM).
|
||||
- 🔍 [Pydantic](https://docs.pydantic.dev), utilisé par FastAPI, pour la validation des données et la gestion des paramètres.
|
||||
- 💾 [PostgreSQL](https://www.postgresql.org) comme base de données SQL.
|
||||
- 🚀 [React](https://react.dev) pour le frontend.
|
||||
- 💃 Utilisation de TypeScript, des hooks, de Vite et d'autres éléments d'un stack frontend moderne.
|
||||
- 🎨 [Tailwind CSS](https://tailwindcss.com) et [shadcn/ui](https://ui.shadcn.com) pour les composants frontend.
|
||||
- 🤖 Un client frontend généré automatiquement.
|
||||
- 🧪 [Playwright](https://playwright.dev) pour les tests de bout en bout.
|
||||
- 🦇 Prise en charge du mode sombre.
|
||||
- 🐋 [Docker Compose](https://www.docker.com) pour le développement et la production.
|
||||
- 🔒 Hachage sécurisé des mots de passe par défaut.
|
||||
- 🔑 Authentification JWT (JSON Web Token).
|
||||
- 📫 Récupération de mot de passe par e-mail.
|
||||
- ✅ Tests avec [Pytest](https://pytest.org).
|
||||
- 📞 [Traefik](https://traefik.io) comme proxy inverse / répartiteur de charge.
|
||||
- 🚢 Instructions de déploiement avec Docker Compose, y compris la configuration d'un proxy Traefik frontal pour gérer les certificats HTTPS automatiques.
|
||||
- 🏭 CI (intégration continue) et CD (déploiement continu) basés sur GitHub Actions.
|
||||
|
||||
+176
-127
@@ -1,70 +1,68 @@
|
||||
# Introduction aux Types Python
|
||||
# Introduction aux types Python { #python-types-intro }
|
||||
|
||||
Python supporte des annotations de type (ou *type hints*) optionnelles.
|
||||
Python prend en charge des « annotations de type » (aussi appelées « type hints ») facultatives.
|
||||
|
||||
Ces annotations de type constituent 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, cela permet aux différents outils comme les éditeurs de texte d'offrir un meilleur support.
|
||||
En déclarant les types de vos variables, les éditeurs et outils peuvent vous offrir un meilleur support.
|
||||
|
||||
Ce chapitre n'est qu'un **tutoriel rapide / rappel** sur les annotations de type Python.
|
||||
Seulement le minimum nécessaire pour les utiliser avec **FastAPI** sera couvert... ce qui est en réalité très peu.
|
||||
Ceci est un **tutoriel rapide / rappel** à propos des annotations de type Python. Il couvre uniquement le minimum nécessaire pour les utiliser avec **FastAPI** ... ce qui est en réalité très peu.
|
||||
|
||||
**FastAPI** est totalement basé sur ces annotations de type, qui lui donnent de nombreux avantages.
|
||||
**FastAPI** est totalement basé sur ces annotations de type, elles lui donnent de nombreux avantages et bénéfices.
|
||||
|
||||
Mais même si vous n'utilisez pas ou n'utiliserez jamais **FastAPI**, vous pourriez bénéficier d'apprendre quelques choses sur ces dernières.
|
||||
Mais même si vous n'utilisez jamais **FastAPI**, vous auriez intérêt à en apprendre un peu à leur sujet.
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
Si vous êtes un expert Python, et que vous savez déjà **tout** sur les annotations de type, passez au chapitre suivant.
|
||||
Si vous êtes un expert Python, et que vous savez déjà tout sur les annotations de type, passez au chapitre suivant.
|
||||
|
||||
///
|
||||
|
||||
## Motivations
|
||||
## Motivation { #motivation }
|
||||
|
||||
Prenons un exemple simple :
|
||||
Commençons par un exemple simple :
|
||||
|
||||
{*../../docs_src/python_types/tutorial001.py*}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py *}
|
||||
|
||||
Exécuter ce programe affiche :
|
||||
Exécuter ce programme affiche :
|
||||
|
||||
```
|
||||
John Doe
|
||||
```
|
||||
|
||||
La fonction :
|
||||
La fonction fait ce qui suit :
|
||||
|
||||
* Prend un `first_name` et un `last_name`.
|
||||
* Convertit la première lettre de chaque paramètre en majuscules grâce à `title()`.
|
||||
* Concatène les résultats avec un espace entre les deux.
|
||||
* Convertit la première lettre de chacun en majuscule avec `title()`.
|
||||
* <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.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
|
||||
|
||||
### Limitations
|
||||
### Modifier le code { #edit-it }
|
||||
|
||||
C'est un programme très simple.
|
||||
|
||||
Mais maintenant imaginez que vous l'écriviez de zéro.
|
||||
|
||||
À un certain point vous auriez commencé la définition de la fonction, vous aviez les paramètres prêts.
|
||||
À un certain moment, vous auriez commencé la définition de la fonction, vous aviez les paramètres prêts ...
|
||||
|
||||
Mais vous aviez besoin de "cette méthode qui convertit la première lettre en majuscule".
|
||||
Mais ensuite vous devez appeler « cette méthode qui convertit la première lettre en majuscule ».
|
||||
|
||||
Était-ce `upper` ? `uppercase` ? `first_uppercase` ? `capitalize` ?
|
||||
Était-ce `upper` ? Était-ce `uppercase` ? `first_uppercase` ? `capitalize` ?
|
||||
|
||||
Vous essayez donc d'utiliser le vieil ami du programmeur, l'auto-complétion de l'éditeur.
|
||||
Vous essayez alors avec l'ami de toujours des programmeurs, l'autocomplétion de l'éditeur.
|
||||
|
||||
Vous écrivez le premier paramètre, `first_name`, puis un point (`.`) et appuyez sur `Ctrl+Espace` pour déclencher l'auto-complétion.
|
||||
Vous tapez le premier paramètre de la fonction, `first_name`, puis un point (`.`) et appuyez sur `Ctrl+Espace` pour déclencher l'autocomplétion.
|
||||
|
||||
Mais malheureusement, rien d'utile n'en résulte :
|
||||
Mais, malheureusement, vous n'obtenez rien d'utile :
|
||||
|
||||
<img src="/img/python-types/image01.png">
|
||||
|
||||
### Ajouter des types
|
||||
### Ajouter des types { #add-types }
|
||||
|
||||
Modifions une seule ligne de la version précédente.
|
||||
|
||||
Nous allons changer seulement cet extrait, les paramètres de la fonction, de :
|
||||
|
||||
Nous allons changer exactement ce fragment, les paramètres de la fonction, de :
|
||||
|
||||
```Python
|
||||
first_name, last_name
|
||||
@@ -78,222 +76,273 @@ Nous allons changer seulement cet extrait, les paramètres de la fonction, de :
|
||||
|
||||
C'est tout.
|
||||
|
||||
Ce sont des annotations de types :
|
||||
Ce sont les « annotations de type » :
|
||||
|
||||
{*../../docs_src/python_types/tutorial002.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
|
||||
|
||||
À ne pas confondre avec la déclaration de valeurs par défaut comme ici :
|
||||
Ce n'est pas la même chose que de déclarer des valeurs par défaut, ce qui serait :
|
||||
|
||||
```Python
|
||||
first_name="john", last_name="doe"
|
||||
```
|
||||
|
||||
C'est une chose différente.
|
||||
C'est différent.
|
||||
|
||||
On utilise un deux-points (`:`), et pas un égal (`=`).
|
||||
Nous utilisons des deux-points (`:`), pas des signes égal (`=`).
|
||||
|
||||
Et ajouter des annotations de types ne crée normalement pas de différence avec le comportement qui aurait eu lieu si elles n'étaient pas là.
|
||||
Et ajouter des annotations de type ne change normalement pas ce qui se passe par rapport à ce qui se passerait sans elles.
|
||||
|
||||
Maintenant, imaginez que vous êtes en train de créer cette fonction, mais avec des annotations de type cette fois.
|
||||
Mais maintenant, imaginez que vous êtes à nouveau en train de créer cette fonction, mais avec des annotations de type.
|
||||
|
||||
Au même moment que durant l'exemple précédent, vous essayez de déclencher l'auto-complétion et vous voyez :
|
||||
Au même moment, vous essayez de déclencher l'autocomplétion avec `Ctrl+Espace` et vous voyez :
|
||||
|
||||
<img src="/img/python-types/image02.png">
|
||||
|
||||
Vous pouvez donc dérouler les options jusqu'à trouver la méthode à laquelle vous pensiez.
|
||||
Avec cela, vous pouvez faire défiler en voyant les options, jusqu'à trouver celle qui « vous dit quelque chose » :
|
||||
|
||||
<img src="/img/python-types/image03.png">
|
||||
|
||||
## Plus de motivations
|
||||
## Plus de motivation { #more-motivation }
|
||||
|
||||
Cette fonction possède déjà des annotations de type :
|
||||
Regardez cette fonction, elle a déjà des annotations de type :
|
||||
|
||||
{*../../docs_src/python_types/tutorial003.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
|
||||
|
||||
Comme l'éditeur connaît le type des variables, vous n'avez pas seulement l'auto-complétion, mais aussi de la détection d'erreurs :
|
||||
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 :
|
||||
|
||||
<img src="/img/python-types/image04.png">
|
||||
|
||||
Maintenant que vous avez connaissance du problème, convertissez `age` en <abbr title="string">chaîne de caractères</abbr> grâce à `str(age)` :
|
||||
Vous savez maintenant qu'il faut corriger, convertir `age` en chaîne avec `str(age)` :
|
||||
|
||||
{*../../docs_src/python_types/tutorial004.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
|
||||
|
||||
## Déclarer des types
|
||||
## Déclarer des types { #declaring-types }
|
||||
|
||||
Vous venez de voir là où les types sont généralement déclarés : dans les paramètres de fonctions.
|
||||
Vous venez de voir l'endroit principal pour déclarer des annotations de type : dans les paramètres des fonctions.
|
||||
|
||||
C'est aussi ici que vous les utiliseriez avec **FastAPI**.
|
||||
C'est aussi l'endroit principal où vous les utiliserez avec **FastAPI**.
|
||||
|
||||
### Types simples
|
||||
### Types simples { #simple-types }
|
||||
|
||||
Vous pouvez déclarer tous les types de Python, pas seulement `str`.
|
||||
Vous pouvez déclarer tous les types standards de Python, pas seulement `str`.
|
||||
|
||||
Comme par exemple :
|
||||
Vous pouvez utiliser, par exemple :
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{*../../docs_src/python_types/tutorial005.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
|
||||
|
||||
### Types génériques avec des paramètres de types
|
||||
### Module `typing` { #typing-module }
|
||||
|
||||
Il existe certaines structures de données qui contiennent d'autres valeurs, comme `dict`, `list`, `set` et `tuple`. Et les valeurs internes peuvent elles aussi avoir leurs propres types.
|
||||
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` :
|
||||
|
||||
Pour déclarer ces types et les types internes, on utilise le module standard de Python `typing`.
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
Il existe spécialement pour supporter ces annotations de types.
|
||||
|
||||
#### `List`
|
||||
def some_function(data: Any):
|
||||
print(data)
|
||||
```
|
||||
|
||||
Par exemple, définissons une variable comme `list` de `str`.
|
||||
### Types génériques { #generic-types }
|
||||
|
||||
Importez `List` (avec un `L` majuscule) depuis `typing`.
|
||||
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]`.
|
||||
|
||||
{*../../docs_src/python_types/tutorial006.py hl[1] *}
|
||||
Ces types qui peuvent prendre des paramètres de type sont appelés des **types génériques** ou **Generics**.
|
||||
|
||||
Déclarez la variable, en utilisant la syntaxe des deux-points (`:`).
|
||||
Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :
|
||||
|
||||
Et comme type, mettez `List`.
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
Les listes étant un type contenant des types internes, mettez ces derniers entre crochets (`[`, `]`) :
|
||||
#### Liste { #list }
|
||||
|
||||
{*../../docs_src/python_types/tutorial006.py hl[4] *}
|
||||
Par exemple, définissons une variable comme une `list` de `str`.
|
||||
|
||||
/// tip | Astuce
|
||||
Déclarez la variable, en utilisant la même syntaxe avec deux-points (`:`).
|
||||
|
||||
Ces types internes entre crochets sont appelés des "paramètres de type".
|
||||
Comme type, mettez `list`.
|
||||
|
||||
Ici, `str` est un paramètre de type passé à `List`.
|
||||
Comme la liste est un type qui contient des types internes, mettez-les entre crochets :
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
Ces types internes entre crochets sont appelés « paramètres de type ».
|
||||
|
||||
Dans ce cas, `str` est le paramètre de type passé à `list`.
|
||||
|
||||
///
|
||||
|
||||
Ce qui signifie : "la variable `items` est une `list`, et chacun de ses éléments a pour type `str`.
|
||||
Cela signifie : « la variable `items` est une `list`, et chacun des éléments de cette liste est un `str` ».
|
||||
|
||||
En faisant cela, votre éditeur pourra vous aider, même pendant que vous traitez des éléments de la liste.
|
||||
En faisant cela, votre éditeur peut vous fournir de l'aide même pendant le traitement des éléments de la liste :
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
||||
Sans types, c'est presque impossible à réaliser.
|
||||
|
||||
Vous remarquerez que la variable `item` n'est qu'un des éléments de la list `items`.
|
||||
Remarquez que la variable `item` est l'un des éléments de la liste `items`.
|
||||
|
||||
Et pourtant, l'éditeur sait qu'elle est de type `str` et pourra donc vous aider à l'utiliser.
|
||||
Et pourtant, l'éditeur sait que c'est un `str` et fournit le support approprié.
|
||||
|
||||
#### `Tuple` et `Set`
|
||||
#### Tuple et Set { #tuple-and-set }
|
||||
|
||||
C'est le même fonctionnement pour déclarer un `tuple` ou un `set` :
|
||||
Vous feriez la même chose pour déclarer des `tuple` et des `set` :
|
||||
|
||||
{*../../docs_src/python_types/tutorial007.py hl[1,4] *}
|
||||
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
|
||||
|
||||
Dans cet exemple :
|
||||
Cela signifie :
|
||||
|
||||
* La variable `items_t` est un `tuple` avec 3 éléments, un `int`, un deuxième `int`, et un `str`.
|
||||
* La variable `items_t` est un `tuple` avec 3 éléments, un `int`, un autre `int`, et un `str`.
|
||||
* La variable `items_s` est un `set`, et chacun de ses éléments est de type `bytes`.
|
||||
|
||||
#### `Dict`
|
||||
#### Dict { #dict }
|
||||
|
||||
Pour définir un `dict`, il faut lui passer 2 paramètres, séparés par une virgule (`,`).
|
||||
Pour définir un `dict`, vous passez 2 paramètres de type, séparés par des virgules.
|
||||
|
||||
Le premier paramètre de type est pour les clés et le second pour les valeurs du dictionnaire (`dict`).
|
||||
Le premier paramètre de type est pour les clés du `dict`.
|
||||
|
||||
{*../../docs_src/python_types/tutorial008.py hl[1,4] *}
|
||||
Le second paramètre de type est pour les valeurs du `dict` :
|
||||
|
||||
Dans cet exemple :
|
||||
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
|
||||
|
||||
* La variable `prices` est de type `dict` :
|
||||
* Les clés de ce dictionnaire sont de type `str`.
|
||||
* Les valeurs de ce dictionnaire sont de type `float`.
|
||||
Cela signifie :
|
||||
|
||||
#### `Optional`
|
||||
* La variable `prices` est un `dict` :
|
||||
* Les clés de ce `dict` sont de type `str` (disons, le nom de chaque article).
|
||||
* Les valeurs de ce `dict` sont de type `float` (disons, le prix de chaque article).
|
||||
|
||||
Vous pouvez aussi utiliser `Optional` pour déclarer qu'une variable a un type, comme `str` mais qu'il est "optionnel" signifiant qu'il pourrait aussi être `None`.
|
||||
#### Union { #union }
|
||||
|
||||
{*../../docs_src/python_types/tutorial009.py hl[1,4] *}
|
||||
Vous pouvez déclarer qu'une variable peut être **plusieurs types**, par exemple, un `int` ou un `str`.
|
||||
|
||||
Utiliser `Optional[str]` plutôt que `str` permettra à l'éditeur de vous aider à détecter les erreurs où vous supposeriez qu'une valeur est toujours de type `str`, alors qu'elle pourrait aussi être `None`.
|
||||
Pour le définir, vous utilisez la <dfn title='aussi appelé « opérateur OU bit à bit », mais ce sens n’est pas pertinent ici'>barre verticale (`|`)</dfn> pour séparer les deux types.
|
||||
|
||||
#### Types génériques
|
||||
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.
|
||||
|
||||
Les types qui peuvent contenir des paramètres de types entre crochets, comme :
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Optional`
|
||||
* ...et d'autres.
|
||||
Cela signifie que `item` peut être un `int` ou un `str`.
|
||||
|
||||
sont appelés des **types génériques** ou **Generics**.
|
||||
#### Possiblement `None` { #possibly-none }
|
||||
|
||||
### Classes en tant que types
|
||||
Vous pouvez déclarer qu'une valeur peut avoir un type, comme `str`, mais qu'elle peut aussi être `None`.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
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 }
|
||||
|
||||
Vous pouvez aussi déclarer une classe comme type d'une variable.
|
||||
|
||||
Disons que vous avez une classe `Person`, avec une variable `name` :
|
||||
|
||||
{*../../docs_src/python_types/tutorial010.py hl[1:3] *}
|
||||
Disons que vous avez une classe `Person`, avec un nom :
|
||||
|
||||
{* ../../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.py hl[6] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
|
||||
|
||||
Et vous aurez accès, encore une fois, au support complet offert par l'éditeur :
|
||||
Et là encore, vous obtenez tout le support de l'éditeur :
|
||||
|
||||
<img src="/img/python-types/image06.png">
|
||||
|
||||
## Les modèles Pydantic
|
||||
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` ».
|
||||
|
||||
## Modèles Pydantic { #pydantic-models }
|
||||
|
||||
<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> est une bibliothèque Python pour effectuer de la validation de données.
|
||||
|
||||
Vous déclarez la forme de la donnée avec des classes et des attributs.
|
||||
Vous déclarez la « forme » de la donnée sous forme de classes avec des attributs.
|
||||
|
||||
Chaque attribut possède un type.
|
||||
Et chaque attribut a un type.
|
||||
|
||||
Puis vous créez une instance de cette classe avec certaines valeurs et **Pydantic** validera les valeurs, les convertira dans le type adéquat (si c'est nécessaire et possible) et vous donnera un objet avec toute la donnée.
|
||||
Ensuite, vous créez une instance de cette classe avec certaines valeurs et elle validera les valeurs, les convertira dans le type approprié (le cas échéant) et vous donnera un objet avec toutes les données.
|
||||
|
||||
Ainsi, votre éditeur vous offrira un support adapté pour l'objet résultant.
|
||||
Et vous obtenez tout le support de l'éditeur avec cet objet résultant.
|
||||
|
||||
Extrait de la documentation officielle de **Pydantic** :
|
||||
Un exemple tiré de la documentation officielle de Pydantic :
|
||||
|
||||
{*../../docs_src/python_types/tutorial011.py*}
|
||||
{* ../../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, allez jeter un coup d'oeil à sa documentation</a>.
|
||||
Pour en savoir plus à propos de <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, consultez sa documentation</a>.
|
||||
|
||||
///
|
||||
|
||||
**FastAPI** est basé entièrement sur **Pydantic**.
|
||||
**FastAPI** est entièrement basé sur Pydantic.
|
||||
|
||||
Vous verrez bien plus d'exemples de son utilisation dans [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
|
||||
Vous verrez beaucoup plus de tout cela en pratique dans le [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Les annotations de type dans **FastAPI**
|
||||
## Annotations de type avec métadonnées { #type-hints-with-metadata-annotations }
|
||||
|
||||
**FastAPI** utilise ces annotations pour faire différentes choses.
|
||||
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`.
|
||||
|
||||
Avec **FastAPI**, vous déclarez des paramètres grâce aux annotations de types et vous obtenez :
|
||||
Vous pouvez importer `Annotated` depuis `typing`.
|
||||
|
||||
* **du support de l'éditeur**
|
||||
* **de la vérification de types**
|
||||
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
|
||||
|
||||
...et **FastAPI** utilise ces mêmes déclarations pour :
|
||||
Python lui-même ne fait rien avec ce `Annotated`. Et pour les éditeurs et autres outils, le type est toujours `str`.
|
||||
|
||||
* **Définir les prérequis** : depuis les paramètres de chemins des requêtes, les entêtes, les corps, les dépendances, etc.
|
||||
* **Convertir des données** : depuis la requête vers les types requis.
|
||||
* **Valider des données** : venant de chaque requête :
|
||||
* Générant automatiquement des **erreurs** renvoyées au client quand la donnée est invalide.
|
||||
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.
|
||||
|
||||
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**.
|
||||
|
||||
/// 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. ✨
|
||||
|
||||
Et aussi que votre code sera très compatible avec de nombreux autres outils et bibliothèques Python. 🚀
|
||||
|
||||
///
|
||||
|
||||
## Annotations de type dans **FastAPI** { #type-hints-in-fastapi }
|
||||
|
||||
**FastAPI** tire parti de ces annotations de type pour faire plusieurs choses.
|
||||
|
||||
Avec **FastAPI**, vous déclarez des paramètres avec des annotations de type et vous obtenez :
|
||||
|
||||
* **Du support de l'éditeur**.
|
||||
* **Des vérifications de types**.
|
||||
|
||||
... et **FastAPI** utilise les mêmes déclarations pour :
|
||||
|
||||
* **Définir des prérequis** : à partir des paramètres de chemin de la requête, des paramètres de requête, des en-têtes, des corps, des dépendances, etc.
|
||||
* **Convertir des données** : de la requête vers le type requis.
|
||||
* **Valider des données** : provenant de chaque requête :
|
||||
* En générant des **erreurs automatiques** renvoyées au client lorsque la donnée est invalide.
|
||||
* **Documenter** l'API avec OpenAPI :
|
||||
* ce qui ensuite utilisé par les interfaces utilisateur automatiques de documentation interactive.
|
||||
* ce qui est ensuite utilisé par les interfaces utilisateur de documentation interactive automatiques.
|
||||
|
||||
Tout cela peut paraître bien abstrait, mais ne vous inquiétez pas, vous verrez tout ça en pratique dans [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
|
||||
Tout cela peut sembler abstrait. Ne vous inquiétez pas. Vous verrez tout cela en action dans le [Tutoriel - Guide utilisateur](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
Ce qu'il faut retenir c'est qu'en utilisant les types standard de Python, à un seul endroit (plutôt que d'ajouter plus de classes, de décorateurs, etc.), **FastAPI** fera une grande partie du travail pour vous.
|
||||
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à lu le tutoriel et êtes revenus ici pour voir plus sur les types, une bonne ressource est la <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">"cheat sheet" de `mypy`</a>.
|
||||
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>.
|
||||
|
||||
///
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Ressources { #resources }
|
||||
|
||||
Ressources supplémentaires, liens externes et plus encore. ✈️
|
||||
@@ -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)
|
||||
|
||||
///
|
||||
@@ -1,4 +1,4 @@
|
||||
# Tâches d'arrière-plan
|
||||
# Tâches d'arrière-plan { #background-tasks }
|
||||
|
||||
Vous pouvez définir des tâches d'arrière-plan qui seront exécutées après avoir retourné une réponse.
|
||||
|
||||
@@ -7,20 +7,21 @@ Ceci est utile pour les opérations qui doivent avoir lieu après une requête,
|
||||
Cela comprend, par exemple :
|
||||
|
||||
* Les notifications par email envoyées après l'exécution d'une action :
|
||||
* Étant donné que se connecter à un serveur et envoyer un email a tendance à être «lent» (plusieurs secondes), vous pouvez retourner la réponse directement et envoyer la notification en arrière-plan.
|
||||
* Étant donné que se connecter à un serveur et envoyer un email a tendance à être « lent » (plusieurs secondes), vous pouvez retourner la réponse directement et envoyer la notification en arrière-plan.
|
||||
* Traiter des données :
|
||||
* Par exemple, si vous recevez un fichier qui doit passer par un traitement lent, vous pouvez retourner une réponse «Accepted» (HTTP 202) puis faire le traitement en arrière-plan.
|
||||
* Par exemple, si vous recevez un fichier qui doit passer par un traitement lent, vous pouvez retourner une réponse « Accepted » (HTTP 202) puis faire le traitement en arrière-plan.
|
||||
|
||||
## Utiliser `BackgroundTasks` { #using-backgroundtasks }
|
||||
|
||||
## Utiliser `BackgroundTasks`
|
||||
Pour commencer, importez `BackgroundTasks` et définissez un paramètre dans votre *fonction de chemin d'accès* avec `BackgroundTasks` comme type déclaré.
|
||||
|
||||
Pour commencer, importez `BackgroundTasks` et définissez un paramètre dans votre *fonction de chemin* avec `BackgroundTasks` comme type déclaré.
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001.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
|
||||
## 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.
|
||||
|
||||
@@ -30,14 +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.py hl[6:9] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
|
||||
|
||||
## Ajouter une tâche d'arrière-plan
|
||||
## Ajouter une tâche d'arrière-plan { #add-the-background-task }
|
||||
|
||||
Dans votre *fonction de chemin*, passez votre fonction de tâche à l'objet de type `BackgroundTasks` (`background_tasks` ici) grâce à la méthode `.add_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.py hl[14] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
|
||||
|
||||
`.add_task()` reçoit comme arguments :
|
||||
|
||||
@@ -45,40 +45,40 @@ Dans votre *fonction de chemin*, passez votre fonction de tâche à l'objet de t
|
||||
* Les arguments positionnels à passer à la fonction de tâche dans l'ordre (`email`).
|
||||
* Les arguments nommés à passer à la fonction de tâche (`message="some notification"`).
|
||||
|
||||
## Injection de dépendances
|
||||
## Injection de dépendances { #dependency-injection }
|
||||
|
||||
Utiliser `BackgroundTasks` fonctionne aussi avec le système d'injection de dépendances. Vous pouvez déclarer un paramètre de type `BackgroundTasks` à différents niveaux : dans une *fonction de chemin*, dans une dépendance, dans une sous-dépendance...
|
||||
Utiliser `BackgroundTasks` fonctionne aussi avec le système d'injection de dépendances. Vous pouvez déclarer un paramètre de type `BackgroundTasks` à différents niveaux : dans une *fonction de chemin d'accès*, dans une dépendance (dependable), dans une sous-dépendance, etc.
|
||||
|
||||
**FastAPI** sait quoi faire dans chaque cas et comment réutiliser le même objet, afin que tous les paramètres de type `BackgroundTasks` soient fusionnés et que les tâches soient exécutées en arrière-plan :
|
||||
**FastAPI** sait quoi faire dans chaque cas et comment réutiliser le même objet, afin que toutes les tâches d'arrière-plan soient fusionnées et que les tâches soient ensuite exécutées en arrière-plan :
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial002.py hl[13,15,22,25] *}
|
||||
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
|
||||
|
||||
Dans cet exemple, les messages seront écrits dans le fichier `log.txt` après que la réponse soit envoyée.
|
||||
|
||||
S'il y avait une `query` (paramètre nommé `q`) dans la requête, alors elle sera écrite dans `log.txt` via une tâche d'arrière-plan.
|
||||
S'il y avait un paramètre de requête dans la requête, alors il sera écrit dans le journal via une tâche d'arrière-plan.
|
||||
|
||||
Et ensuite une autre tâche d'arrière-plan (générée dans les paramètres de la *la fonction de chemin*) écrira un message dans `log.txt` comprenant le paramètre de chemin `email`.
|
||||
Et ensuite une autre tâche d'arrière-plan (générée dans la *fonction de chemin d'accès*) écrira un message comprenant le paramètre de chemin `email`.
|
||||
|
||||
## Détails techniques
|
||||
## Détails techniques { #technical-details }
|
||||
|
||||
La classe `BackgroundTasks` provient directement de <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">`starlette.background`</a>.
|
||||
|
||||
Elle est importée/incluse directement dans **FastAPI** pour que vous puissiez l'importer depuis `fastapi` et éviter d'importer accidentellement `BackgroundTask` (sans `s` à la fin) depuis `starlette.background`.
|
||||
|
||||
En utilisant seulement `BackgroundTasks` (et non `BackgroundTask`), il est possible de l'utiliser en tant que paramètre de *fonction de chemin* et de laisser **FastAPI** gérer le reste pour vous, comme en utilisant l'objet `Request` directement.
|
||||
En utilisant seulement `BackgroundTasks` (et non `BackgroundTask`), il est possible de l'utiliser en tant que paramètre de *fonction de chemin d'accès* et de laisser **FastAPI** gérer le reste pour vous, comme en utilisant l'objet `Request` directement.
|
||||
|
||||
Il est tout de même possible d'utiliser `BackgroundTask` seul dans **FastAPI**, mais dans ce cas il faut créer l'objet dans le code et renvoyer une `Response` Starlette l'incluant.
|
||||
|
||||
Plus de détails sont disponibles dans <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">la documentation officielle de Starlette sur les tâches d'arrière-plan</a> (via leurs classes `BackgroundTasks`et `BackgroundTask`).
|
||||
Plus de détails sont disponibles dans <a href="https://www.starlette.dev/background/" class="external-link" target="_blank">la documentation officielle de Starlette sur les tâches d'arrière-plan</a>.
|
||||
|
||||
## Avertissement
|
||||
## Avertissement { #caveat }
|
||||
|
||||
Si vous avez besoin de réaliser des traitements lourds en tâche d'arrière-plan et que vous n'avez pas besoin que ces traitements aient lieu dans le même process (par exemple, pas besoin de partager la mémoire, les variables, etc.), il peut s'avérer profitable d'utiliser des outils plus importants tels que <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
|
||||
|
||||
Ces outils nécessitent généralement des configurations plus complexes ainsi qu'un gestionnaire de queue de message, comme RabbitMQ ou Redis, mais ils permettent d'exécuter des tâches d'arrière-plan dans différents process, et potentiellement, sur plusieurs serveurs.
|
||||
Ces outils nécessitent généralement des configurations plus complexes ainsi qu'un gestionnaire de queue de message, comme RabbitMQ ou Redis, mais ils permettent d'exécuter des tâches d'arrière-plan dans différents process, et surtout, sur plusieurs serveurs.
|
||||
|
||||
Mais si vous avez besoin d'accéder aux variables et objets de la même application **FastAPI**, ou si vous avez besoin d'effectuer de petites tâches d'arrière-plan (comme envoyer des notifications par email), vous pouvez simplement vous contenter d'utiliser `BackgroundTasks`.
|
||||
|
||||
## Résumé
|
||||
## Résumé { #recap }
|
||||
|
||||
Importez et utilisez `BackgroundTasks` grâce aux paramètres de *fonction de chemin* et les dépendances pour ajouter des tâches d'arrière-plan.
|
||||
Importez et utilisez `BackgroundTasks` grâce aux paramètres de *fonction de chemin d'accès* et les dépendances pour ajouter des tâches d'arrière-plan.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -1,24 +1,24 @@
|
||||
# Body - Paramètres multiples
|
||||
# Body - Paramètres multiples { #body-multiple-parameters }
|
||||
|
||||
Maintenant que nous avons vu comment manipuler `Path` et `Query`, voyons comment faire pour le corps d'une requête, communément désigné par le terme anglais "body".
|
||||
Maintenant que nous avons vu comment utiliser `Path` et `Query`, voyons des usages plus avancés des déclarations de paramètres du corps de la requête.
|
||||
|
||||
## Mélanger les paramètres `Path`, `Query` et body
|
||||
## Mélanger les paramètres `Path`, `Query` et du corps de la requête { #mix-path-query-and-body-parameters }
|
||||
|
||||
Tout d'abord, sachez que vous pouvez mélanger les déclarations des paramètres `Path`, `Query` et body, **FastAPI** saura quoi faire.
|
||||
Tout d'abord, sachez que vous pouvez mélanger librement les déclarations des paramètres `Path`, `Query` et du corps de la requête, **FastAPI** saura quoi faire.
|
||||
|
||||
Vous pouvez également déclarer des paramètres body comme étant optionnels, en leur assignant une valeur par défaut à `None` :
|
||||
Et vous pouvez également déclarer des paramètres du corps de la requête comme étant optionnels, en leur assignant une valeur par défaut à `None` :
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
Notez que, dans ce cas, le paramètre `item` provenant du `Body` est optionnel (sa valeur par défaut est `None`).
|
||||
Notez que, dans ce cas, l'élément `item` récupéré depuis le corps de la requête est optionnel. Comme sa valeur par défaut est `None`.
|
||||
|
||||
///
|
||||
|
||||
## Paramètres multiples du body
|
||||
## Paramètres multiples du corps de la requête { #multiple-body-parameters }
|
||||
|
||||
Dans l'exemple précédent, les opérations de routage attendaient un body JSON avec les attributs d'un `Item`, par exemple :
|
||||
Dans l'exemple précédent, les chemins d'accès attendraient un corps de la requête JSON avec les attributs d'un `Item`, par exemple :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -29,13 +29,13 @@ Dans l'exemple précédent, les opérations de routage attendaient un body JSON
|
||||
}
|
||||
```
|
||||
|
||||
Mais vous pouvez également déclarer plusieurs paramètres provenant de body, par exemple `item` et `user` simultanément :
|
||||
Mais vous pouvez également déclarer plusieurs paramètres provenant du corps de la requête, par exemple `item` et `user` :
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
|
||||
|
||||
Dans ce cas, **FastAPI** détectera qu'il y a plus d'un paramètre dans le body (chacun correspondant à un modèle Pydantic).
|
||||
Dans ce cas, **FastAPI** détectera qu'il y a plus d'un paramètre du corps de la requête dans la fonction (il y a deux paramètres qui sont des modèles Pydantic).
|
||||
|
||||
Il utilisera alors les noms des paramètres comme clés, et s'attendra à recevoir quelque chose de semblable à :
|
||||
Il utilisera alors les noms des paramètres comme clés (noms de champs) dans le corps de la requête, et s'attendra à recevoir un corps de la requête semblable à :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -52,29 +52,29 @@ Il utilisera alors les noms des paramètres comme clés, et s'attendra à recevo
|
||||
}
|
||||
```
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
"Notez que, bien que nous ayons déclaré le paramètre `item` de la même manière que précédemment, il est maintenant associé à la clé `item` dans le corps de la requête."`.
|
||||
Notez que, bien que `item` ait été déclaré de la même manière qu'auparavant, il est désormais attendu à l'intérieur du corps de la requête sous la clé `item`.
|
||||
|
||||
///
|
||||
|
||||
**FastAPI** effectue la conversion de la requête de façon transparente, de sorte que les objets `item` et `user` se trouvent correctement définis.
|
||||
**FastAPI** effectuera la conversion automatique depuis la requête, de sorte que le paramètre `item` reçoive son contenu spécifique, et de même pour `user`.
|
||||
|
||||
Il effectue également la validation des données (même imbriquées les unes dans les autres), et permet de les documenter correctement (schéma OpenAPI et documentation auto-générée).
|
||||
Il effectuera la validation des données composées, et les documentera ainsi pour le schéma OpenAPI et la documentation automatique.
|
||||
|
||||
## Valeurs scalaires dans le body
|
||||
## Valeurs singulières dans le corps de la requête { #singular-values-in-body }
|
||||
|
||||
De la même façon qu'il existe `Query` et `Path` pour définir des données supplémentaires pour les paramètres query et path, **FastAPI** fournit un équivalent `Body`.
|
||||
De la même façon qu'il existe `Query` et `Path` pour définir des données supplémentaires pour les paramètres de requête et de chemin, **FastAPI** fournit un équivalent `Body`.
|
||||
|
||||
Par exemple, en étendant le modèle précédent, vous pouvez vouloir ajouter un paramètre `importance` dans le même body, en plus des paramètres `item` et `user`.
|
||||
Par exemple, en étendant le modèle précédent, vous pourriez décider d'avoir une autre clé `importance` dans le même corps de la requête, en plus de `item` et `user`.
|
||||
|
||||
Si vous le déclarez tel quel, comme c'est une valeur [scalaire](https://docs.github.com/fr/graphql/reference/scalars), **FastAPI** supposera qu'il s'agit d'un paramètre de requête (`Query`).
|
||||
Si vous le déclarez tel quel, comme c'est une valeur singulière, **FastAPI** supposera qu'il s'agit d'un paramètre de requête.
|
||||
|
||||
Mais vous pouvez indiquer à **FastAPI** de la traiter comme une variable de body en utilisant `Body` :
|
||||
Mais vous pouvez indiquer à **FastAPI** de la traiter comme une autre clé du corps de la requête en utilisant `Body` :
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
|
||||
|
||||
Dans ce cas, **FastAPI** s'attendra à un body semblable à :
|
||||
Dans ce cas, **FastAPI** s'attendra à un corps de la requête semblable à :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -92,19 +92,13 @@ Dans ce cas, **FastAPI** s'attendra à un body semblable à :
|
||||
}
|
||||
```
|
||||
|
||||
Encore une fois, cela convertira les types de données, les validera, permettra de générer la documentation, etc...
|
||||
Encore une fois, il convertira les types de données, validera, documentera, etc.
|
||||
|
||||
## Paramètres multiples body et query
|
||||
## Paramètres multiples du corps de la requête et paramètres de requête { #multiple-body-params-and-query }
|
||||
|
||||
Bien entendu, vous pouvez déclarer autant de paramètres que vous le souhaitez, en plus des paramètres body déjà déclarés.
|
||||
Bien entendu, vous pouvez également déclarer des paramètres de requête supplémentaires quand vous en avez besoin, en plus de tout paramètre du corps de la requête.
|
||||
|
||||
Par défaut, les valeurs [scalaires](https://docs.github.com/fr/graphql/reference/scalars) sont interprétées comme des paramètres query, donc inutile d'ajouter explicitement `Query`. Vous pouvez juste écrire :
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Ou bien, en Python 3.10 et supérieur :
|
||||
Comme, par défaut, les valeurs singulières sont interprétées comme des paramètres de requête, vous n'avez pas besoin d'ajouter explicitement `Query`, vous pouvez simplement écrire :
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
@@ -112,31 +106,31 @@ q: str | None = None
|
||||
|
||||
Par exemple :
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[27] *}
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
||||
/// info
|
||||
|
||||
`Body` possède les mêmes paramètres de validation additionnels et de gestion des métadonnées que `Query` et `Path`, ainsi que d'autres que nous verrons plus tard.
|
||||
`Body` possède également les mêmes paramètres supplémentaires de validation et de métadonnées que `Query`, `Path` et d'autres que vous verrez plus tard.
|
||||
|
||||
///
|
||||
|
||||
## Inclure un paramètre imbriqué dans le body
|
||||
## Intégrer un seul paramètre du corps de la requête { #embed-a-single-body-parameter }
|
||||
|
||||
Disons que vous avez seulement un paramètre `item` dans le body, correspondant à un modèle Pydantic `Item`.
|
||||
Supposons que vous n'ayez qu'un seul paramètre `item` dans le corps de la requête, provenant d'un modèle Pydantic `Item`.
|
||||
|
||||
Par défaut, **FastAPI** attendra sa déclaration directement dans le body.
|
||||
Par défaut, **FastAPI** attendra alors son contenu directement.
|
||||
|
||||
Cependant, si vous souhaitez qu'il interprête correctement un JSON avec une clé `item` associée au contenu du modèle, comme cela serait le cas si vous déclariez des paramètres body additionnels, vous pouvez utiliser le paramètre spécial `embed` de `Body` :
|
||||
Mais si vous voulez qu'il attende un JSON avec une clé `item` contenant le contenu du modèle, comme lorsqu'on déclare des paramètres supplémentaires du corps de la requête, vous pouvez utiliser le paramètre spécial `embed` de `Body` :
|
||||
|
||||
```Python
|
||||
item: Item = Body(embed=True)
|
||||
```
|
||||
|
||||
Voici un exemple complet :
|
||||
comme dans :
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
|
||||
|
||||
Dans ce cas **FastAPI** attendra un body semblable à :
|
||||
Dans ce cas **FastAPI** s'attendra à un corps de la requête semblable à :
|
||||
|
||||
```JSON hl_lines="2"
|
||||
{
|
||||
@@ -160,12 +154,12 @@ au lieu de :
|
||||
}
|
||||
```
|
||||
|
||||
## Pour résumer
|
||||
## Récapitulatif { #recap }
|
||||
|
||||
Vous pouvez ajouter plusieurs paramètres body dans votre fonction de routage, même si une requête ne peut avoir qu'un seul body.
|
||||
Vous pouvez ajouter plusieurs paramètres du corps de la requête à votre fonction de chemin d'accès, même si une requête ne peut avoir qu'un seul corps de la requête.
|
||||
|
||||
Cependant, **FastAPI** se chargera de faire opérer sa magie, afin de toujours fournir à votre fonction des données correctes, les validera et documentera le schéma associé.
|
||||
Mais **FastAPI** s'en chargera, vous fournira les bonnes données dans votre fonction, et validera et documentera le schéma correct dans le chemin d'accès.
|
||||
|
||||
Vous pouvez également déclarer des valeurs [scalaires](https://docs.github.com/fr/graphql/reference/scalars) à recevoir dans le body.
|
||||
Vous pouvez également déclarer des valeurs singulières à recevoir dans le corps de la requête.
|
||||
|
||||
Et vous pouvez indiquer à **FastAPI** d'inclure le body dans une autre variable, même lorsqu'un seul paramètre est déclaré.
|
||||
Et vous pouvez indiquer à **FastAPI** d'intégrer le corps de la requête sous une clé même lorsqu'un seul paramètre est déclaré.
|
||||
|
||||
@@ -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">l’aperç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 d’un certain type et des valeurs d’un 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
|
||||
@@ -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 l’opé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 d’entré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 n’inclut pas l’attribut déjà enregistré « tax »: 20.2, le modèle d’entré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 l’opé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 n’envoyer 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 n’utilisent que `PUT`, même pour les mises à jour partielles.
|
||||
|
||||
Vous êtes libre de les utiliser comme vous le souhaitez, **FastAPI** n’impose 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 d’utiliser 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 l’utiliser 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 d’entrée (en utilisant `exclude_unset`).
|
||||
* De cette façon, vous mettez à jour uniquement les valeurs effectivement définies par l’utilisateur, 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 à l’utilisation à 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 l’exemple ici utilise `PATCH` car il a été créé pour ces cas d’usage.
|
||||
|
||||
///
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Remarquez que le modèle d’entrée est toujours validé.
|
||||
|
||||
Ainsi, si vous souhaitez recevoir des mises à jour partielles pouvant omettre tous les attributs, vous devez disposer d’un 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}.
|
||||
|
||||
///
|
||||
@@ -1,16 +1,16 @@
|
||||
# Corps de la requête
|
||||
# Corps de la requête { #request-body }
|
||||
|
||||
Quand vous avez besoin d'envoyer de la donnée depuis un client (comme un navigateur) vers votre API, vous l'envoyez en tant que **corps de requête**.
|
||||
|
||||
Le corps d'une **requête** est de la donnée envoyée par le client à votre API. Le corps d'une **réponse** est la donnée envoyée par votre API au client.
|
||||
|
||||
Votre API aura presque toujours à envoyer un corps de **réponse**. Mais un client n'a pas toujours à envoyer un corps de **requête**.
|
||||
Votre API aura presque toujours à envoyer un corps de **réponse**. Mais un client n'a pas toujours à envoyer un **corps de requête** : parfois il demande seulement un chemin, peut-être avec quelques paramètres de requête, mais n'envoie pas de corps.
|
||||
|
||||
Pour déclarer un corps de **requête**, on utilise les modèles de <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> en profitant de tous leurs avantages et fonctionnalités.
|
||||
|
||||
/// 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.
|
||||
|
||||
@@ -18,23 +18,23 @@ Ceci étant découragé, la documentation interactive générée par Swagger UI
|
||||
|
||||
///
|
||||
|
||||
## Importez le `BaseModel` de Pydantic
|
||||
## Importer le `BaseModel` de Pydantic { #import-pydantics-basemodel }
|
||||
|
||||
Commencez par importer la classe `BaseModel` du module `pydantic` :
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[4] *}
|
||||
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
|
||||
|
||||
## Créez votre modèle de données
|
||||
## Créer votre modèle de données { #create-your-data-model }
|
||||
|
||||
Déclarez ensuite votre modèle de données en tant que classe qui hérite de `BaseModel`.
|
||||
|
||||
Utilisez les types Python standard pour tous les attributs :
|
||||
|
||||
{* ../../docs_src/body/tutorial001.py hl[7:11] *}
|
||||
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
|
||||
|
||||
Tout comme pour la déclaration de paramètres de requête, quand un attribut de modèle a une valeur par défaut, il n'est pas nécessaire. Sinon, cet attribut doit être renseigné dans le corps de la requête. Pour rendre ce champ optionnel simplement, utilisez `None` comme valeur par défaut.
|
||||
Tout comme pour la déclaration de paramètres de requête, quand un attribut de modèle a une valeur par défaut, il n'est pas nécessaire. Sinon, cet attribut doit être renseigné dans le corps de la requête. Utilisez `None` pour le rendre simplement optionnel.
|
||||
|
||||
Par exemple, le modèle ci-dessus déclare un "objet" JSON (ou `dict` Python) tel que :
|
||||
Par exemple, le modèle ci-dessus déclare un JSON « `object` » (ou `dict` Python) tel que :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -45,7 +45,7 @@ Par exemple, le modèle ci-dessus déclare un "objet" JSON (ou `dict` Python) te
|
||||
}
|
||||
```
|
||||
|
||||
...`description` et `tax` étant des attributs optionnels (avec `None` comme valeur par défaut), cet "objet" JSON serait aussi valide :
|
||||
... `description` et `tax` étant des attributs optionnels (avec `None` comme valeur par défaut), ce JSON « `object` » serait aussi valide :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -54,94 +54,94 @@ Par exemple, le modèle ci-dessus déclare un "objet" JSON (ou `dict` Python) te
|
||||
}
|
||||
```
|
||||
|
||||
## Déclarez-le comme paramètre
|
||||
## 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.py hl[18] *}
|
||||
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
|
||||
|
||||
...et déclarez que son type est le modèle que vous avez créé : `Item`.
|
||||
... et déclarez que son type est le modèle que vous avez créé : `Item`.
|
||||
|
||||
## Résultats
|
||||
## Résultats { #results }
|
||||
|
||||
En utilisant uniquement les déclarations de type Python, **FastAPI** réussit à :
|
||||
|
||||
* Lire le contenu de la requête en tant que JSON.
|
||||
* Convertir les types correspondants (si nécessaire).
|
||||
* Valider la donnée.
|
||||
* Si la donnée est invalide, une erreur propre et claire sera renvoyée, indiquant exactement où était la donnée incorrecte.
|
||||
* Si la donnée est invalide, une erreur propre et claire sera renvoyée, indiquant exactement où et quelle était la donnée incorrecte.
|
||||
* Passer la donnée reçue dans le paramètre `item`.
|
||||
* Ce paramètre ayant été déclaré dans la fonction comme étant de type `Item`, vous aurez aussi tout le support offert par l'éditeur (auto-complétion, etc.) pour tous les attributs de ce paramètre et les types de ces attributs.
|
||||
* Générer des définitions <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> pour votre modèle, qui peuvent être utilisées où vous en avez besoin dans votre projet ensuite.
|
||||
* Ces schémas participeront à la constitution du schéma généré OpenAPI, et seront donc utilisés par les documentations automatiquement générées.
|
||||
* Ce paramètre ayant été déclaré dans la fonction comme étant de type `Item`, vous aurez aussi tout le support offert par l'éditeur (autocomplétion, etc.) pour tous les attributs de ce paramètre et les types de ces attributs.
|
||||
* Générer des définitions <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> pour votre modèle ; vous pouvez également les utiliser partout ailleurs si cela a du sens pour votre projet.
|
||||
* Ces schémas participeront à la constitution du schéma généré OpenAPI, et seront utilisés par les documentations automatiques <abbr title="User Interfaces - Interfaces utilisateur">UIs</abbr>.
|
||||
|
||||
## Documentation automatique
|
||||
## Documentation automatique { #automatic-docs }
|
||||
|
||||
Les schémas JSON de vos modèles seront intégrés au schéma OpenAPI global de votre application, et seront donc affichés dans la documentation interactive de l'API :
|
||||
|
||||
<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">
|
||||
|
||||
## Support de l'éditeur
|
||||
## Support de l'éditeur { #editor-support }
|
||||
|
||||
Dans votre éditeur, vous aurez des annotations de types et de l'auto-complétion partout dans votre fonction (ce qui n'aurait pas été le cas si vous aviez utilisé un classique `dict` plutôt qu'un modèle Pydantic) :
|
||||
Dans votre éditeur, vous aurez des annotations de type et de l'autocomplétion partout dans votre fonction (ce qui n'aurait pas été le cas si vous aviez reçu un `dict` plutôt qu'un modèle Pydantic) :
|
||||
|
||||
<img src="/img/tutorial/body/image03.png">
|
||||
|
||||
Et vous obtenez aussi de la vérification d'erreur pour les opérations incorrectes de types :
|
||||
Et vous obtenez aussi des vérifications d'erreurs pour les opérations de types incorrectes :
|
||||
|
||||
<img src="/img/tutorial/body/image04.png">
|
||||
|
||||
Ce n'est pas un hasard, ce framework entier a été bâti avec ce design comme objectif.
|
||||
|
||||
Et cela a été rigoureusement testé durant la phase de design, avant toute implémentation, pour s'assurer que cela fonctionnerait avec tous les éditeurs.
|
||||
Et cela a été rigoureusement testé durant la phase de design, avant toute implémentation, pour vous assurer que cela fonctionnerait avec tous les éditeurs.
|
||||
|
||||
Des changements sur Pydantic ont même été faits pour supporter cela.
|
||||
|
||||
Les captures d'écrans précédentes ont été prises sur <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
|
||||
Les captures d'écran précédentes ont été prises sur <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
|
||||
|
||||
Mais vous auriez le même support de l'éditeur avec <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> et la majorité des autres éditeurs de code Python.
|
||||
Mais vous auriez le même support de l'éditeur avec <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> et la majorité des autres éditeurs de code Python :
|
||||
|
||||
<img src="/img/tutorial/body/image05.png">
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous utilisez <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> comme éditeur, vous pouvez utiliser le Plugin <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
|
||||
Si vous utilisez <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> comme éditeur, vous pouvez utiliser le plug-in <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
|
||||
|
||||
Ce qui améliore le support pour les modèles Pydantic avec :
|
||||
|
||||
* de l'auto-complétion
|
||||
* de l'autocomplétion
|
||||
* des vérifications de type
|
||||
* du "refactoring" (ou remaniement de code)
|
||||
* du « refactoring »
|
||||
* de la recherche
|
||||
* de l'inspection
|
||||
* des inspections
|
||||
|
||||
///
|
||||
|
||||
## Utilisez le modèle
|
||||
## Utiliser le modèle { #use-the-model }
|
||||
|
||||
Dans la fonction, vous pouvez accéder à tous les attributs de l'objet du modèle directement :
|
||||
|
||||
{* ../../docs_src/body/tutorial002.py hl[21] *}
|
||||
{* ../../docs_src/body/tutorial002_py310.py *}
|
||||
|
||||
## Corps de la requête + paramètres de chemin
|
||||
## 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**.
|
||||
|
||||
{* ../../docs_src/body/tutorial003.py hl[17:18] *}
|
||||
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
|
||||
|
||||
## Corps de la requête + paramètres de chemin et de requête
|
||||
## 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.
|
||||
|
||||
{* ../../docs_src/body/tutorial004.py hl[18] *}
|
||||
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
|
||||
|
||||
Les paramètres de la fonction seront reconnus comme tel :
|
||||
|
||||
@@ -149,14 +149,16 @@ Les paramètres de la fonction seront reconnus comme tel :
|
||||
* Si le paramètre est d'un **type singulier** (comme `int`, `float`, `str`, `bool`, etc.), il sera interprété comme un paramètre de **requête**.
|
||||
* Si le paramètre est déclaré comme ayant pour type un **modèle Pydantic**, il sera interprété comme faisant partie du **corps** de la requête.
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `=None`.
|
||||
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`.
|
||||
|
||||
Le type `Optional` dans `Optional[str]` n'est pas utilisé par **FastAPI**, mais sera utile à votre éditeur pour améliorer le support offert par ce dernier et détecter plus facilement des erreurs de type.
|
||||
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.
|
||||
|
||||
///
|
||||
|
||||
## Sans Pydantic
|
||||
## Sans Pydantic { #without-pydantic }
|
||||
|
||||
Si vous ne voulez pas utiliser des modèles Pydantic, vous pouvez aussi utiliser des paramètres de **Corps**. Pour cela, allez voir la partie de la documentation sur [Corps de la requête - Paramètres multiples](body-multiple-params.md){.internal-link target=_blank}.
|
||||
Si vous ne voulez pas utiliser des modèles Pydantic, vous pouvez aussi utiliser des paramètres de **Body**. Pour cela, allez voir la documentation sur [Corps de la requête - Paramètres multiples : Valeurs singulières dans le corps](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
|
||||
@@ -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**. 😎
|
||||
@@ -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`.
|
||||
@@ -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 d’envoyer sa requête au backend `:80`.
|
||||
|
||||
Pour y parvenir, le backend `:80` doit disposer d’une liste « d’origines 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 n’autorisera que certains types de communication, en excluant tout ce qui implique des informations d’identification : 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 d’indiquer explicitement les origines autorisées.
|
||||
|
||||
## Utiliser `CORSMiddleware` { #use-corsmiddleware }
|
||||
|
||||
Vous pouvez le configurer dans votre application **FastAPI** à l’aide de `CORSMiddleware`.
|
||||
|
||||
* Importer `CORSMiddleware`.
|
||||
* Créer une liste d’origines autorisées (sous forme de chaînes).
|
||||
* L’ajouter comme « middleware » à votre application **FastAPI**.
|
||||
|
||||
Vous pouvez également spécifier si votre backend autorise :
|
||||
|
||||
* Les informations d’identification (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 l’implé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 inter‑domaine.
|
||||
|
||||
Les arguments suivants sont pris en charge :
|
||||
|
||||
* `allow_origins` - Une liste d’origines autorisées à effectuer des requêtes cross-origin. Par ex. `['https://example.org', 'https://www.example.org']`. Vous pouvez utiliser `['*']` pour autoriser n’importe 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 d’en-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 s’agit 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 d’informations 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.
|
||||
|
||||
///
|
||||
@@ -1,14 +1,14 @@
|
||||
# <abbr title="En anglais: Debugging">Débogage</abbr>
|
||||
# <abbr title="En anglais: Debugging">Débogage</abbr> { #debugging }
|
||||
|
||||
Vous pouvez connecter le <abbr title="En anglais: debugger">débogueur</abbr> dans votre éditeur, par exemple avec Visual Studio Code ou PyCharm.
|
||||
|
||||
## Faites appel à `uvicorn`
|
||||
## Appeler `uvicorn` { #call-uvicorn }
|
||||
|
||||
Dans votre application FastAPI, importez et exécutez directement `uvicorn` :
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
|
||||
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
|
||||
|
||||
### À propos de `__name__ == "__main__"`
|
||||
### À propos de `__name__ == "__main__"` { #about-name-main }
|
||||
|
||||
Le but principal de `__name__ == "__main__"` est d'avoir du code qui est exécuté lorsque votre fichier est appelé avec :
|
||||
|
||||
@@ -26,7 +26,7 @@ mais qui n'est pas appelé lorsqu'un autre fichier l'importe, comme dans :
|
||||
from myapp import app
|
||||
```
|
||||
|
||||
#### Pour davantage de détails
|
||||
#### Pour davantage de détails { #more-details }
|
||||
|
||||
Imaginons que votre fichier s'appelle `myapp.py`.
|
||||
|
||||
@@ -78,7 +78,7 @@ Pour plus d'informations, consultez <a href="https://docs.python.org/3/library/_
|
||||
|
||||
///
|
||||
|
||||
## Exécutez votre code avec votre <abbr title="En anglais: debugger">débogueur</abbr>
|
||||
## Exécuter votre code avec votre <abbr title="En anglais: debugger">débogueur</abbr> { #run-your-code-with-your-debugger }
|
||||
|
||||
Parce que vous exécutez le serveur Uvicorn directement depuis votre code, vous pouvez appeler votre programme Python (votre application FastAPI) directement depuis le <abbr title="En anglais: debugger">débogueur</abbr>.
|
||||
|
||||
@@ -86,10 +86,10 @@ Parce que vous exécutez le serveur Uvicorn directement depuis votre code, vous
|
||||
|
||||
Par exemple, dans Visual Studio Code, vous pouvez :
|
||||
|
||||
- Cliquer sur l'onglet "Debug" de la barre d'activités de Visual Studio Code.
|
||||
- "Add configuration...".
|
||||
- Sélectionnez "Python".
|
||||
- Lancez le <abbr title="En anglais: debugger">débogueur</abbr> avec l'option "`Python: Current File (Integrated Terminal)`".
|
||||
- Allez dans le panneau « Debug ».
|
||||
- « Add configuration ... ».
|
||||
- Sélectionnez « Python ».
|
||||
- Lancez le <abbr title="En anglais: debugger">débogueur</abbr> avec l'option « Python: Current File (Integrated Terminal) ».
|
||||
|
||||
Il démarrera alors le serveur avec votre code **FastAPI**, s'arrêtera à vos points d'arrêt, etc.
|
||||
|
||||
@@ -101,8 +101,8 @@ Voici à quoi cela pourrait ressembler :
|
||||
|
||||
Si vous utilisez Pycharm, vous pouvez :
|
||||
|
||||
- Ouvrir le menu "Run".
|
||||
- Sélectionnez l'option "Debug...".
|
||||
- Ouvrez le menu « Run ».
|
||||
- Sélectionnez l'option « Debug ... ».
|
||||
- Un menu contextuel s'affiche alors.
|
||||
- Sélectionnez le fichier à déboguer (dans ce cas, `main.py`).
|
||||
|
||||
|
||||
@@ -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 sous‑dé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` ci‑dessus 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 ci‑dessus :
|
||||
|
||||
//// 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 celui‑ci 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 elle‑mê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, ignorez‑le, vous n'en avez pas besoin.
|
||||
|
||||
Ce n'est qu'un raccourci. Parce que **FastAPI** tient à vous aider à minimiser la duplication de code.
|
||||
|
||||
///
|
||||
@@ -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*.
|
||||
@@ -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.
|
||||
|
||||
///
|
||||
@@ -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*.
|
||||
@@ -0,0 +1,250 @@
|
||||
# Dépendances { #dependencies }
|
||||
|
||||
**FastAPI** dispose d’un 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 l’intégration d’autres composants à **FastAPI** pour n’importe quel développeur.
|
||||
|
||||
## Qu’est-ce que « l’injection de dépendances » { #what-is-dependency-injection }
|
||||
|
||||
L’**« injection de dépendances »** signifie, en programmation, qu’il existe un moyen pour votre code (dans ce cas, vos fonctions de chemins d’accè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).
|
||||
|
||||
C’est 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é, l’authentification, des exigences de rôles, etc.
|
||||
* Et bien d’autres 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 qu’il n’est pas très utile, pour l’instant.
|
||||
|
||||
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 d’abord sur la dépendance.
|
||||
|
||||
C’est simplement une fonction qui peut prendre tous les mêmes paramètres qu’une fonction de chemin d’accès peut prendre :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
|
||||
|
||||
C’est tout.
|
||||
|
||||
**2 lignes**.
|
||||
|
||||
Et elle a la même forme et structure que toutes vos fonctions de chemins d’accès.
|
||||
|
||||
Vous pouvez la considérer comme une fonction de chemin d’accè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 d’utiliser `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 d’utiliser `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 d’accè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` qu’un seul paramètre.
|
||||
|
||||
Ce paramètre doit être quelque chose comme une fonction.
|
||||
|
||||
Vous ne l’appelez pas directement (n’ajoutez 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 d’accè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 qu’une 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 d’accè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 l’appeler pour vos chemins d’accès.
|
||||
|
||||
/// check | Vérifications
|
||||
|
||||
Notez que vous n’avez 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 qu’il 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 l’annotation 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 l’utiliser à plusieurs endroits :
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
C’est simplement du Python standard, cela s’appelle un « alias de type », ce n’est 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 **l’information de type sera conservée**, ce qui signifie que votre éditeur pourra continuer à vous fournir **l’autocomplétion**, **des erreurs en ligne**, etc. Idem pour d’autres outils comme `mypy`.
|
||||
|
||||
Cela sera particulièrement utile lorsque vous l’utiliserez dans une **grande base de code** où vous utilisez **les mêmes dépendances** encore et encore dans **de nombreux chemins d’accè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 d’accès), les mêmes règles s’appliquent 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` à l’intérieur de fonctions de chemins d’accès `def` normales, ou des dépendances `def` à l’intérieur de fonctions de chemins d’accè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 d’accès sont déclarées pour être utilisées chaque fois qu’un « chemin » et une « opération » correspondent, puis **FastAPI** se charge d’appeler 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 n’appelez jamais ces fonctions directement. Elles sont appelées par votre framework (dans ce cas, **FastAPI**).
|
||||
|
||||
Avec le système d’injection de dépendances, vous pouvez aussi indiquer à **FastAPI** que votre fonction de chemin d’accès « dépend » également d’autre chose qui doit être exécuté avant votre fonction de chemin d’accès, et **FastAPI** se chargera de l’exécuter et d’« injecter » les résultats.
|
||||
|
||||
D’autres termes courants pour cette même idée « d’injection 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 n’y a **pas besoin de créer des « plug-ins »**, car en utilisant des dépendances il est possible de déclarer un nombre infini d’intégrations et d’interactions qui deviennent disponibles pour vos fonctions de chemins d’accès.
|
||||
|
||||
Et les dépendances peuvent être créées de manière très simple et intuitive, ce qui vous permet d’importer juste les packages Python dont vous avez besoin, et de les intégrer à vos fonctions d’API 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 d’injection 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 d’authentification et d’autorisation
|
||||
* les systèmes de supervision d’usage d’API
|
||||
* les systèmes d’injection de données de réponse
|
||||
* etc.
|
||||
|
||||
## Simple et puissant { #simple-and-powerful }
|
||||
|
||||
Bien que le système hiérarchique d’injection 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 d’API (chemins d’accès) :
|
||||
|
||||
* `/items/public/`
|
||||
* `/items/private/`
|
||||
* `/users/{user_id}/activate`
|
||||
* `/items/pro/`
|
||||
|
||||
alors vous pourriez ajouter différentes exigences d’autorisations pour chacun d’eux 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 d’accès.
|
||||
|
||||
**FastAPI** se chargera d’ajouter le tout au schéma OpenAPI, afin que cela apparaisse dans les systèmes de documentation interactive.
|
||||
@@ -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 elle‑mê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.
|
||||
|
||||
///
|
||||
@@ -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.
|
||||
|
||||
///
|
||||
@@ -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] *}
|
||||
@@ -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.
|
||||
@@ -1,107 +1,122 @@
|
||||
# Démarrage
|
||||
# Démarrer { #first-steps }
|
||||
|
||||
Le fichier **FastAPI** le plus simple possible pourrait ressembler à cela :
|
||||
Le fichier **FastAPI** le plus simple possible pourrait ressembler à ceci :
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py *}
|
||||
|
||||
Copiez ce code dans un fichier nommé `main.py`.
|
||||
Copiez cela dans un fichier `main.py`.
|
||||
|
||||
Démarrez le serveur :
|
||||
Démarrez le serveur en direct :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
<span style="color: green;">INFO</span>: Started reloader process [28720]
|
||||
<span style="color: green;">INFO</span>: Started server process [28722]
|
||||
<span style="color: green;">INFO</span>: Waiting for application startup.
|
||||
<span style="color: green;">INFO</span>: Application startup complete.
|
||||
<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">'/home/user/code/awesomeapp'</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>
|
||||
|
||||
/// note
|
||||
|
||||
La commande `uvicorn main:app` fait référence à :
|
||||
|
||||
* `main` : le fichier `main.py` (le module Python).
|
||||
* `app` : l'objet créé dans `main.py` via la ligne `app = FastAPI()`.
|
||||
* `--reload` : l'option disant à uvicorn de redémarrer le serveur à chaque changement du code. À ne pas utiliser en production !
|
||||
|
||||
///
|
||||
|
||||
Vous devriez voir dans la console, une ligne semblable à la suivante :
|
||||
Dans la sortie, il y a une ligne semblable à :
|
||||
|
||||
```hl_lines="4"
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
Cette ligne montre l'URL par laquelle l'app est actuellement accessible, sur votre machine locale.
|
||||
Cette ligne montre l’URL où votre application est servie, sur votre machine locale.
|
||||
|
||||
### Allez voir le résultat
|
||||
### Vérifier { #check-it }
|
||||
|
||||
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>.
|
||||
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 obtiendrez cette réponse JSON :
|
||||
Vous verrez la réponse JSON suivante :
|
||||
|
||||
```JSON
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
### Documentation interactive de l'API
|
||||
### Documentation interactive de l’API { #interactive-api-docs }
|
||||
|
||||
Rendez-vous sur <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
Allez maintenant 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 la documentation interactive de l'API générée automatiquement (via <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
|
||||
Vous verrez la documentation interactive de l’API générée automatiquement (fournie par <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>) :
|
||||
|
||||

|
||||
|
||||
### Documentation alternative
|
||||
### Documentation alternative de l’API { #alternative-api-docs }
|
||||
|
||||
Ensuite, rendez-vous sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
Et maintenant, 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 y verrez la documentation alternative (via <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
|
||||
Vous verrez la documentation automatique alternative (fournie par <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>) :
|
||||
|
||||

|
||||
|
||||
### OpenAPI
|
||||
### OpenAPI { #openapi }
|
||||
|
||||
**FastAPI** génère un "schéma" contenant toute votre API dans le standard de définition d'API **OpenAPI**.
|
||||
**FastAPI** génère un « schéma » contenant toute votre API en utilisant le standard **OpenAPI** pour définir des API.
|
||||
|
||||
#### "Schéma"
|
||||
#### « Schéma » { #schema }
|
||||
|
||||
Un "schéma" est une définition ou une description de quelque chose. Pas le code qui l'implémente, uniquement une description abstraite.
|
||||
Un « schéma » est une définition ou une description de quelque chose. Pas le code qui l’implémente, mais uniquement une description abstraite.
|
||||
|
||||
#### "Schéma" d'API
|
||||
#### « Schéma » d’API { #api-schema }
|
||||
|
||||
Ici, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> est une spécification qui dicte comment définir le schéma de votre API.
|
||||
|
||||
Le schéma inclut les chemins de votre API, les paramètres potentiels de chaque chemin, etc.
|
||||
Cette définition de schéma inclut les chemins de votre API, les paramètres possibles qu’ils prennent, etc.
|
||||
|
||||
#### "Schéma" de données
|
||||
#### « Schéma » de données { #data-schema }
|
||||
|
||||
Le terme "schéma" peut aussi faire référence à la forme de la donnée, comme un contenu JSON.
|
||||
Le terme « schéma » peut également faire référence à la forme d’une donnée, comme un contenu JSON.
|
||||
|
||||
Dans ce cas, cela signifierait les attributs JSON, ainsi que les types de ces attributs, etc.
|
||||
Dans ce cas, cela désignerait les attributs JSON, ainsi que leurs types, etc.
|
||||
|
||||
#### OpenAPI et JSON Schema
|
||||
#### OpenAPI et JSON Schema { #openapi-and-json-schema }
|
||||
|
||||
**OpenAPI** définit un schéma d'API pour votre API. Il inclut des définitions (ou "schémas") de la donnée envoyée et reçue par votre API en utilisant **JSON Schema**, le standard des schémas de données JSON.
|
||||
OpenAPI définit un schéma d’API pour votre API. Et ce schéma inclut des définitions (ou « schémas ») des données envoyées et reçues par votre API en utilisant **JSON Schema**, le standard pour les schémas de données JSON.
|
||||
|
||||
#### Allez voir `openapi.json`
|
||||
#### Voir le `openapi.json` { #check-the-openapi-json }
|
||||
|
||||
Si vous êtes curieux d'à quoi ressemble le schéma brut **OpenAPI**, **FastAPI** génère automatiquement un (schéma) JSON avec les descriptions de toute votre API.
|
||||
Si vous êtes curieux de voir à quoi ressemble le schéma OpenAPI brut, FastAPI génère automatiquement un JSON (schéma) avec les descriptions de toute votre API.
|
||||
|
||||
Vous pouvez le voir directement à cette adresse : <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
|
||||
|
||||
Le schéma devrait ressembler à ceci :
|
||||
Vous pouvez le voir directement à l’adresse : <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
|
||||
|
||||
Il affichera un JSON commençant par quelque chose comme :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
@@ -120,79 +135,87 @@ Le schéma devrait ressembler à ceci :
|
||||
...
|
||||
```
|
||||
|
||||
#### À quoi sert OpenAPI
|
||||
#### À quoi sert OpenAPI { #what-is-openapi-for }
|
||||
|
||||
Le schéma **OpenAPI** est ce qui alimente les deux systèmes de documentation interactive.
|
||||
Le schéma OpenAPI est ce qui alimente les deux systèmes de documentation interactive inclus.
|
||||
|
||||
Et il existe des dizaines d'alternatives, toutes basées sur **OpenAPI**. Vous pourriez facilement ajouter n'importe laquelle de ces alternatives à votre application **FastAPI**.
|
||||
Et il existe des dizaines d’alternatives, toutes basées sur OpenAPI. Vous pourriez facilement ajouter n’importe laquelle de ces alternatives à votre application construite avec **FastAPI**.
|
||||
|
||||
Vous pourriez aussi l'utiliser pour générer du code automatiquement, pour les clients qui communiquent avec votre API. Comme par exemple, des applications frontend, mobiles ou IOT.
|
||||
Vous pourriez également l’utiliser pour générer du code automatiquement, pour les clients qui communiquent avec votre API. Par exemple, des applications frontend, mobiles ou IoT.
|
||||
|
||||
## Récapitulatif, étape par étape
|
||||
### Déployer votre application (optionnel) { #deploy-your-app-optional }
|
||||
|
||||
### Étape 1 : import `FastAPI`
|
||||
Vous pouvez, si vous le souhaitez, déployer votre application FastAPI sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>, allez rejoindre la liste d’attente si ce n’est pas déjà fait. 🚀
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
|
||||
Si vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste d’attente 😉), vous pouvez déployer votre application avec une seule commande.
|
||||
|
||||
`FastAPI` est une classe Python qui fournit toutes les fonctionnalités nécessaires au lancement de votre API.
|
||||
Avant de déployer, vous devez vous assurer que vous êtes connecté :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi login
|
||||
|
||||
You are logged in to FastAPI Cloud 🚀
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Puis déployez votre application :
|
||||
|
||||
<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>
|
||||
|
||||
C’est tout ! Vous pouvez maintenant accéder à votre application à cette URL. ✨
|
||||
|
||||
## Récapitulatif, étape par étape { #recap-step-by-step }
|
||||
|
||||
### Étape 1 : importer `FastAPI` { #step-1-import-fastapi }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
|
||||
|
||||
`FastAPI` est une classe Python qui fournit toutes les fonctionnalités nécessaires à votre API.
|
||||
|
||||
/// note | Détails techniques
|
||||
|
||||
`FastAPI` est une classe héritant directement de `Starlette`.
|
||||
`FastAPI` est une classe qui hérite directement de `Starlette`.
|
||||
|
||||
Vous pouvez donc aussi utiliser toutes les fonctionnalités de <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> depuis `FastAPI`.
|
||||
Vous pouvez donc aussi utiliser toutes les fonctionnalités de <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> avec `FastAPI`.
|
||||
|
||||
///
|
||||
|
||||
### Étape 2 : créer une "instance" `FastAPI`
|
||||
### Étape 2 : créer une « instance » `FastAPI` { #step-2-create-a-fastapi-instance }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
|
||||
|
||||
Ici la variable `app` sera une "instance" de la classe `FastAPI`.
|
||||
Ici, la variable `app` sera une « instance » de la classe `FastAPI`.
|
||||
|
||||
Ce sera le point principal d'interaction pour créer toute votre API.
|
||||
Ce sera le point principal d’interaction pour créer toute votre API.
|
||||
|
||||
Cette `app` est la même que celle à laquelle fait référence `uvicorn` dans la commande :
|
||||
### Étape 3 : créer un « chemin d’accès » { #step-3-create-a-path-operation }
|
||||
|
||||
<div class="termy">
|
||||
#### Chemin { #path }
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
« Chemin » fait ici référence à la dernière partie de l’URL à partir du premier `/`.
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Si vous créez votre app avec :
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial002.py hl[3] *}
|
||||
|
||||
Et la mettez dans un fichier `main.py`, alors vous appelleriez `uvicorn` avec :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:my_awesome_api --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Étape 3: créer une *opération de chemin*
|
||||
|
||||
#### Chemin
|
||||
|
||||
Chemin, ou "path" fait référence ici à la dernière partie de l'URL démarrant au premier `/`.
|
||||
|
||||
Donc, dans un URL tel que :
|
||||
Donc, dans une URL telle que :
|
||||
|
||||
```
|
||||
https://example.com/items/foo
|
||||
```
|
||||
|
||||
...le "path" serait :
|
||||
... le chemin serait :
|
||||
|
||||
```
|
||||
/items/foo
|
||||
@@ -200,66 +223,67 @@ https://example.com/items/foo
|
||||
|
||||
/// info
|
||||
|
||||
Un chemin, ou "path" est aussi souvent appelé route ou "endpoint".
|
||||
Un « chemin » est aussi couramment appelé « endpoint » ou « route ».
|
||||
|
||||
///
|
||||
|
||||
#### Opération
|
||||
Lors de la création d’une API, le « chemin » est la manière principale de séparer les « préoccupations » et les « ressources ».
|
||||
|
||||
"Opération" fait référence à une des "méthodes" HTTP.
|
||||
#### Opération { #operation }
|
||||
|
||||
Une de :
|
||||
« Opération » fait ici référence à l’une des « méthodes » HTTP.
|
||||
|
||||
L’une de :
|
||||
|
||||
* `POST`
|
||||
* `GET`
|
||||
* `PUT`
|
||||
* `DELETE`
|
||||
|
||||
...ou une des plus exotiques :
|
||||
... et les plus exotiques :
|
||||
|
||||
* `OPTIONS`
|
||||
* `HEAD`
|
||||
* `PATCH`
|
||||
* `TRACE`
|
||||
|
||||
Dans le protocol HTTP, vous pouvez communiquer avec chaque chemin en utilisant une (ou plus) de ces "méthodes".
|
||||
Dans le protocole HTTP, vous pouvez communiquer avec chaque chemin en utilisant une (ou plusieurs) de ces « méthodes ».
|
||||
|
||||
---
|
||||
|
||||
En construisant des APIs, vous utilisez généralement ces méthodes HTTP spécifiques pour effectuer une action précise.
|
||||
En construisant des APIs, vous utilisez normalement ces méthodes HTTP spécifiques pour effectuer une action précise.
|
||||
|
||||
Généralement vous utilisez :
|
||||
En général, vous utilisez :
|
||||
|
||||
* `POST` : pour créer de la donnée.
|
||||
* `GET` : pour lire de la donnée.
|
||||
* `PUT` : pour mettre à jour de la donnée.
|
||||
* `DELETE` : pour supprimer de la donnée.
|
||||
* `POST` : pour créer des données.
|
||||
* `GET` : pour lire des données.
|
||||
* `PUT` : pour mettre à jour des données.
|
||||
* `DELETE` : pour supprimer des données.
|
||||
|
||||
Donc, dans **OpenAPI**, chaque méthode HTTP est appelée une "opération".
|
||||
Donc, dans OpenAPI, chacune des méthodes HTTP est appelée une « opération ».
|
||||
|
||||
Nous allons donc aussi appeler ces dernières des "**opérations**".
|
||||
Nous allons donc aussi les appeler « opérations ».
|
||||
|
||||
#### Définir un « décorateur de chemin d’accès » { #define-a-path-operation-decorator }
|
||||
|
||||
#### Définir un *décorateur d'opération de chemin*
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
|
||||
|
||||
Le `@app.get("/")` dit à **FastAPI** que la fonction en dessous est chargée de gérer les requêtes qui vont sur :
|
||||
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 GET HTTP">opération <code>get</code></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".
|
||||
Cette syntaxe `@something` en Python est appelée un « décorateur ».
|
||||
|
||||
Vous la mettez au dessus d'une fonction. Comme un joli chapeau décoratif (j'imagine que ce terme vient de là 🤷🏻♂).
|
||||
Vous la mettez au-dessus d’une fonction. Comme un joli chapeau décoratif (j’imagine que c’est de là que vient le terme 🤷🏻♂).
|
||||
|
||||
Un "décorateur" prend la fonction en dessous et en fait quelque chose.
|
||||
Un « décorateur » prend la fonction en dessous et fait quelque chose avec.
|
||||
|
||||
Dans notre cas, ce décorateur dit à **FastAPI** que la fonction en dessous correspond au **chemin** `/` avec l'**opération** `get`.
|
||||
Dans notre cas, ce décorateur indique à **FastAPI** que la fonction en dessous correspond au **chemin** `/` avec une **opération** `get`.
|
||||
|
||||
C'est le "**décorateur d'opération de chemin**".
|
||||
C’est le « décorateur de chemin d’accès ».
|
||||
|
||||
///
|
||||
|
||||
@@ -269,7 +293,7 @@ Vous pouvez aussi utiliser les autres opérations :
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
|
||||
Tout comme celles les plus exotiques :
|
||||
Ainsi que les plus exotiques :
|
||||
|
||||
* `@app.options()`
|
||||
* `@app.head()`
|
||||
@@ -278,58 +302,79 @@ Tout comme celles les plus exotiques :
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous êtes libres d'utiliser chaque opération (méthode HTTP) comme vous le désirez.
|
||||
Vous êtes libre d’utiliser chaque opération (méthode HTTP) comme vous le souhaitez.
|
||||
|
||||
**FastAPI** n'impose pas de sens spécifique à chacune d'elle.
|
||||
**FastAPI** n’impose aucune signification spécifique.
|
||||
|
||||
Les informations qui sont présentées ici forment une directive générale, pas des obligations.
|
||||
Les informations ici sont présentées comme des lignes directrices, pas comme une obligation.
|
||||
|
||||
Par exemple, quand l'on utilise **GraphQL**, toutes les actions sont effectuées en utilisant uniquement des opérations `POST`.
|
||||
Par exemple, lorsque vous utilisez GraphQL, vous effectuez normalement toutes les actions en utilisant uniquement des opérations `POST`.
|
||||
|
||||
///
|
||||
|
||||
### Étape 4 : définir la **fonction de chemin**.
|
||||
### Étape 4 : définir la **fonction de chemin d’accès** { #step-4-define-the-path-operation-function }
|
||||
|
||||
Voici notre "**fonction de chemin**" (ou fonction d'opération de chemin) :
|
||||
Voici notre « fonction de chemin d’accès » :
|
||||
|
||||
* **chemin** : `/`.
|
||||
* **opération** : `get`.
|
||||
* **fonction** : la fonction sous le "décorateur" (sous `@app.get("/")`).
|
||||
* **fonction** : la fonction sous le « décorateur » (sous `@app.get("/")`).
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
|
||||
|
||||
C'est une fonction Python.
|
||||
C’est une fonction Python.
|
||||
|
||||
Elle sera appelée par **FastAPI** quand une requête sur l'URL `/` sera reçue via une opération `GET`.
|
||||
Elle sera appelée par **FastAPI** chaque fois qu’il recevra une requête vers l’URL « / » en utilisant une opération `GET`.
|
||||
|
||||
Ici, c'est une fonction asynchrone (définie avec `async def`).
|
||||
Dans ce cas, c’est une fonction `async`.
|
||||
|
||||
---
|
||||
|
||||
Vous pourriez aussi la définir comme une fonction classique plutôt qu'avec `async def` :
|
||||
Vous pouvez aussi la définir comme une fonction normale au lieu de `async def` :
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
Si vous ne connaissez pas la différence, allez voir la section [Concurrence : *"Vous êtes pressés ?"*](../async.md#vous-etes-presses){.internal-link target=_blank}.
|
||||
Si vous ne connaissez pas la différence, consultez [Asynchrone : « Pressé ? »](../async.md#in-a-hurry){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Étape 5 : retourner le contenu
|
||||
### Étape 5 : retourner le contenu { #step-5-return-the-content }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Vous pouvez retourner un dictionnaire (`dict`), une liste (`list`), des valeurs seules comme des chaines de caractères (`str`) et des entiers (`int`), etc.
|
||||
Vous pouvez retourner un `dict`, une `list`, des valeurs uniques comme `str`, `int`, etc.
|
||||
|
||||
Vous pouvez aussi retourner des models **Pydantic** (qui seront détaillés plus tard).
|
||||
Vous pouvez également retourner des modèles Pydantic (vous en verrez plus à ce sujet plus tard).
|
||||
|
||||
Il y a de nombreux autres objets et modèles qui seront automatiquement convertis en JSON. Essayez d'utiliser vos favoris, il est fort probable qu'ils soient déjà supportés.
|
||||
Il existe de nombreux autres objets et modèles qui seront automatiquement convertis en JSON (y compris des ORM, etc.). Essayez d’utiliser vos favoris, il est fort probable qu’ils soient déjà pris en charge.
|
||||
|
||||
## Récapitulatif
|
||||
### Étape 6 : le déployer { #step-6-deploy-it }
|
||||
|
||||
Déployez votre application sur **<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** avec une seule commande : `fastapi deploy`. 🎉
|
||||
|
||||
#### À propos de FastAPI Cloud { #about-fastapi-cloud }
|
||||
|
||||
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** est construit par le même auteur et l’équipe derrière **FastAPI**.
|
||||
|
||||
Il simplifie le processus de **construction**, de **déploiement** et d’**accès** à une API avec un minimum d’effort.
|
||||
|
||||
Il apporte la même **expérience développeur** de création d’applications avec FastAPI au **déploiement** dans le cloud. 🎉
|
||||
|
||||
FastAPI Cloud est le sponsor principal et le financeur des projets open source *FastAPI and friends*. ✨
|
||||
|
||||
#### Déployer sur d’autres fournisseurs cloud { #deploy-to-other-cloud-providers }
|
||||
|
||||
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI chez n’importe quel fournisseur cloud de votre choix.
|
||||
|
||||
Suivez les guides de votre fournisseur cloud pour y déployer des applications FastAPI. 🤓
|
||||
|
||||
## Récapitulatif { #recap }
|
||||
|
||||
* Importez `FastAPI`.
|
||||
* Créez une instance d'`app`.
|
||||
* Ajoutez une **décorateur d'opération de chemin** (tel que `@app.get("/")`).
|
||||
* Ajoutez une **fonction de chemin** (telle que `def root(): ...` comme ci-dessus).
|
||||
* Lancez le serveur de développement (avec `uvicorn main:app --reload`).
|
||||
* Créez une instance `app`.
|
||||
* Écrivez un **décorateur de chemin d’accès** avec des décorateurs comme `@app.get("/")`.
|
||||
* Définissez une **fonction de chemin d’accès** ; par exemple, `def root(): ...`.
|
||||
* Exécutez le serveur de développement avec la commande `fastapi dev`.
|
||||
* Déployez éventuellement votre application avec `fastapi deploy`.
|
||||
|
||||
@@ -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.
|
||||
@@ -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**. 😎
|
||||
@@ -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.
|
||||
@@ -1,29 +1,53 @@
|
||||
# Tutoriel - Guide utilisateur - Introduction
|
||||
# Tutoriel - Guide utilisateur { #tutorial-user-guide }
|
||||
|
||||
Ce tutoriel vous montre comment utiliser **FastAPI** avec la plupart de ses fonctionnalités, étape par étape.
|
||||
|
||||
Chaque section s'appuie progressivement sur les précédentes, mais elle est structurée de manière à séparer les sujets, afin que vous puissiez aller directement à l'un d'entre eux pour résoudre vos besoins spécifiques en matière d'API.
|
||||
Chaque section s'appuie progressivement sur les précédentes, mais elle est structurée de manière à séparer les sujets, afin que vous puissiez aller directement à l'un d'entre eux pour répondre à vos besoins spécifiques d'API.
|
||||
|
||||
Il est également conçu pour fonctionner comme une référence future.
|
||||
Il est également conçu pour servir de référence ultérieure, afin que vous puissiez revenir voir exactement ce dont vous avez besoin.
|
||||
|
||||
Vous pouvez donc revenir et voir exactement ce dont vous avez besoin.
|
||||
|
||||
## Exécuter le code
|
||||
## Exécuter le code { #run-the-code }
|
||||
|
||||
Tous les blocs de code peuvent être copiés et utilisés directement (il s'agit en fait de fichiers Python testés).
|
||||
|
||||
Pour exécuter l'un de ces exemples, copiez le code dans un fichier `main.py`, et commencez `uvicorn` avec :
|
||||
Pour exécuter l'un de ces exemples, copiez le code dans un fichier `main.py`, et démarrez `fastapi dev` avec :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
$ <font color="#4E9A06">fastapi</font> dev <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
<span style="color: green;">INFO</span>: Started reloader process [28720]
|
||||
<span style="color: green;">INFO</span>: Started server process [28722]
|
||||
<span style="color: green;">INFO</span>: Waiting for application startup.
|
||||
<span style="color: green;">INFO</span>: Application startup complete.
|
||||
<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">'/home/user/code/awesomeapp'</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>
|
||||
@@ -34,45 +58,33 @@ L'utiliser dans votre éditeur est ce qui vous montre vraiment les avantages de
|
||||
|
||||
---
|
||||
|
||||
## Installer FastAPI
|
||||
## Installer FastAPI { #install-fastapi }
|
||||
|
||||
La première étape consiste à installer FastAPI.
|
||||
|
||||
Pour le tutoriel, vous voudrez peut-être l'installer avec toutes les dépendances et fonctionnalités optionnelles :
|
||||
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, de l'activer, puis **d'installer FastAPI** :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install fastapi[all]
|
||||
$ pip install "fastapi[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
... qui comprend également `uvicorn`, que vous pouvez utiliser comme serveur pour exécuter votre code.
|
||||
/// note | Remarque
|
||||
|
||||
/// note
|
||||
Lorsque vous installez avec `pip install "fastapi[standard]"` cela inclut des dépendances standard optionnelles par défaut, y compris `fastapi-cloud-cli`, qui vous permet de déployer sur <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
|
||||
|
||||
Vous pouvez également l'installer pièce par pièce.
|
||||
Si vous ne souhaitez pas avoir ces dépendances optionnelles, vous pouvez à la place installer `pip install fastapi`.
|
||||
|
||||
C'est ce que vous feriez probablement une fois que vous voudrez déployer votre application en production :
|
||||
|
||||
```
|
||||
pip install fastapi
|
||||
```
|
||||
|
||||
Installez également `uvicorn` pour qu'il fonctionne comme serveur :
|
||||
|
||||
```
|
||||
pip install uvicorn
|
||||
```
|
||||
|
||||
Et la même chose pour chacune des dépendances facultatives que vous voulez utiliser.
|
||||
Si vous souhaitez installer les dépendances standard mais sans `fastapi-cloud-cli`, vous pouvez installer avec `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
|
||||
|
||||
///
|
||||
|
||||
## Guide utilisateur avancé
|
||||
## Guide d'utilisation avancé { #advanced-user-guide }
|
||||
|
||||
Il existe également un **Guide d'utilisation avancé** que vous pouvez lire plus tard après ce **Tutoriel - Guide d'utilisation**.
|
||||
|
||||
|
||||
@@ -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 l’API :
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
|------------|------|-------------|
|
||||
| `title` | `str` | Le titre de l’API. |
|
||||
| `summary` | `str` | Un court résumé de l’API. <small>Disponible depuis OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
|
||||
| `description` | `str` | Une brève description de l’API. Elle peut utiliser Markdown. |
|
||||
| `version` | `string` | La version de l’API. C’est la version de votre propre application, pas d’OpenAPI. Par exemple `2.5.0`. |
|
||||
| `terms_of_service` | `str` | Une URL vers les Conditions d’utilisation de l’API. Le cas échéant, il doit s’agir d’une URL. |
|
||||
| `contact` | `dict` | Les informations de contact pour l’API 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>L’URL pointant vers les informations de contact. DOIT être au format d’une URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>L’adresse e-mail de la personne/organisation de contact. DOIT être au format d’une adresse e-mail.</td></tr></tbody></table></details> |
|
||||
| `license_info` | `dict` | Les informations de licence pour l’API 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 l’API.</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 l’API. 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 l’API. DOIT être au format d’une 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 d’une `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 l’interface 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 l’URL 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 à l’intérieur des descriptions, par exemple « login » sera affiché en gras (**login**) et « fancy » sera affiché en italique (_fancy_).
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous n’avez 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 l’ordre des tags { #order-of-tags }
|
||||
|
||||
L’ordre de chaque dictionnaire de métadonnées de tag définit également l’ordre affiché dans l’interface 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 qu’il 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 l’utilisent.
|
||||
|
||||
## 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] *}
|
||||
@@ -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 qu’elle 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 qu’elle soit traitée par le reste de l’application (par un *chemin d'accès*).
|
||||
* Puis il récupère la **réponse** générée par l’application (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 s’exécutera après le middleware.
|
||||
|
||||
S’il y avait des tâches d’arrière-plan (présentées dans la section [Tâches d’arrière-plan](background-tasks.md){.internal-link target=_blank}, que vous verrez plus tard), elles s’exé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 d’une 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 à 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 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 d’usage. 🤓
|
||||
|
||||
///
|
||||
|
||||
## Ordre d’exé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 l’application, 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 s’exécute en premier.
|
||||
|
||||
Sur le chemin de la réponse, il s’exécute en dernier.
|
||||
|
||||
Par exemple :
|
||||
|
||||
```Python
|
||||
app.add_middleware(MiddlewareA)
|
||||
app.add_middleware(MiddlewareB)
|
||||
```
|
||||
|
||||
Cela aboutit à l’ordre d’exécution suivant :
|
||||
|
||||
* **Requête** : MiddlewareB → MiddlewareA → route
|
||||
|
||||
* **Réponse** : route → MiddlewareA → MiddlewareB
|
||||
|
||||
Ce comportement d’empilement garantit que les middlewares s’exécutent dans un ordre prévisible et contrôlable.
|
||||
|
||||
## Autres middlewares { #other-middlewares }
|
||||
|
||||
Vous pouvez en lire davantage sur d’autres middlewares dans le [Guide de l’utilisateur 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.
|
||||
@@ -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*.
|
||||
@@ -1,8 +1,8 @@
|
||||
# Paramètres de chemin et validations numériques
|
||||
# Paramètres de chemin et validations numériques { #path-parameters-and-numeric-validations }
|
||||
|
||||
De la même façon que vous pouvez déclarer plus de validations et de métadonnées pour les paramètres de requête avec `Query`, vous pouvez déclarer le même type de validations et de métadonnées pour les paramètres de chemin avec `Path`.
|
||||
|
||||
## Importer Path
|
||||
## Importer `Path` { #import-path }
|
||||
|
||||
Tout d'abord, importez `Path` de `fastapi`, et importez `Annotated` :
|
||||
|
||||
@@ -14,11 +14,11 @@ FastAPI a ajouté le support pour `Annotated` (et a commencé à le recommander)
|
||||
|
||||
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d'utiliser `Annotated`.
|
||||
|
||||
Assurez-vous de [Mettre à jour la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} à la version 0.95.1 à minima avant d'utiliser `Annotated`.
|
||||
Assurez-vous de [Mettre à niveau la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} à la version 0.95.1 à minima avant d'utiliser `Annotated`.
|
||||
|
||||
///
|
||||
|
||||
## Déclarer des métadonnées
|
||||
## Déclarer des métadonnées { #declare-metadata }
|
||||
|
||||
Vous pouvez déclarer les mêmes paramètres que pour `Query`.
|
||||
|
||||
@@ -26,15 +26,15 @@ Par exemple, pour déclarer une valeur de métadonnée `title` pour le paramètr
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
Un paramètre de chemin est toujours requis car il doit faire partie du chemin. Même si vous l'avez déclaré avec `None` ou défini une valeur par défaut, cela ne changerait rien, il serait toujours requis.
|
||||
|
||||
///
|
||||
|
||||
## Ordonnez les paramètres comme vous le souhaitez
|
||||
## Ordonner les paramètres comme vous le souhaitez { #order-the-parameters-as-you-need }
|
||||
|
||||
/// tip
|
||||
/// tip | Astuce
|
||||
|
||||
Ce n'est probablement pas aussi important ou nécessaire si vous utilisez `Annotated`.
|
||||
|
||||
@@ -46,7 +46,7 @@ Et vous n'avez pas besoin de déclarer autre chose pour ce paramètre, donc vous
|
||||
|
||||
Mais vous avez toujours besoin d'utiliser `Path` pour le paramètre de chemin `item_id`. Et vous ne voulez pas utiliser `Annotated` pour une raison quelconque.
|
||||
|
||||
Python se plaindra si vous mettez une valeur avec une "défaut" avant une valeur qui n'a pas de "défaut".
|
||||
Python se plaindra si vous mettez une valeur avec une « valeur par défaut » avant une valeur qui n'a pas de « valeur par défaut ».
|
||||
|
||||
Mais vous pouvez les réorganiser, et avoir la valeur sans défaut (le paramètre de requête `q`) en premier.
|
||||
|
||||
@@ -54,15 +54,15 @@ 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.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 hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
|
||||
|
||||
## Ordonnez les paramètres comme vous le souhaitez (astuces)
|
||||
## Ordonner les paramètres comme vous le souhaitez, astuces { #order-the-parameters-as-you-need-tricks }
|
||||
|
||||
/// tip
|
||||
/// tip | Astuce
|
||||
|
||||
Ce n'est probablement pas aussi important ou nécessaire si vous utilisez `Annotated`.
|
||||
|
||||
@@ -77,47 +77,38 @@ Si vous voulez :
|
||||
* les avoir dans un ordre différent
|
||||
* ne pas utiliser `Annotated`
|
||||
|
||||
...Python a une petite syntaxe spéciale pour cela.
|
||||
... Python a une petite syntaxe spéciale pour cela.
|
||||
|
||||
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.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
|
||||
|
||||
# Avec `Annotated`
|
||||
### 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
|
||||
## Validations numériques : supérieur ou égal { #number-validations-greater-than-or-equal }
|
||||
|
||||
Avec `Query` et `Path` (et d'autres que vous verrez plus tard) vous pouvez déclarer des contraintes numériques.
|
||||
|
||||
Ici, avec `ge=1`, `item_id` devra être un nombre entier "`g`reater than or `e`qual" à `1`.
|
||||
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 ou égal et inférieur ou égal
|
||||
## Validations numériques : supérieur et inférieur ou égal { #number-validations-greater-than-and-less-than-or-equal }
|
||||
|
||||
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/tutorial004_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
|
||||
|
||||
## Validations numériques : supérieur et inférieur ou égal
|
||||
|
||||
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] *}
|
||||
|
||||
## Validations numériques : flottants, supérieur et inférieur
|
||||
## Validations numériques : flottants, supérieur et inférieur { #number-validations-floats-greater-than-and-less-than }
|
||||
|
||||
Les validations numériques fonctionnent également pour les valeurs `float`.
|
||||
|
||||
@@ -127,9 +118,9 @@ 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
|
||||
## Pour résumer { #recap }
|
||||
|
||||
Avec `Query`, `Path` (et d'autres que vous verrez plus tard) vous pouvez déclarer des métadonnées et des validations de chaînes de la même manière qu'avec les [Paramètres de requête et validations de chaînes](query-params-str-validations.md){.internal-link target=_blank}.
|
||||
|
||||
|
||||
@@ -1,205 +1,196 @@
|
||||
# Paramètres de chemin
|
||||
# Paramètres de chemin { #path-parameters }
|
||||
|
||||
Vous pouvez déclarer des "paramètres" ou "variables" de chemin avec la même syntaxe que celle utilisée par le
|
||||
<a href="https://docs.python.org/fr/3/library/string.html#format-string-syntax" class="external-link" target="_blank">formatage de chaîne Python</a> :
|
||||
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_py310.py hl[6:7] *}
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
|
||||
La valeur du paramètre de chemin `item_id` sera transmise à votre fonction dans l'argument `item_id`.
|
||||
|
||||
La valeur du paramètre `item_id` sera transmise à la fonction dans l'argument `item_id`.
|
||||
|
||||
Donc, si vous exécutez cet exemple et allez sur <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>,
|
||||
vous verrez comme réponse :
|
||||
Donc, si vous exécutez cet exemple et allez sur <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, vous verrez comme réponse :
|
||||
|
||||
```JSON
|
||||
{"item_id":"foo"}
|
||||
```
|
||||
|
||||
## Paramètres de chemin typés
|
||||
## Paramètres de chemin typés { #path-parameters-with-types }
|
||||
|
||||
Vous pouvez déclarer le type d'un paramètre de chemin dans la fonction, en utilisant les annotations de type Python :
|
||||
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.py hl[7] *}
|
||||
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Ici, `item_id` est déclaré comme `int`.
|
||||
|
||||
/// check | vérifier
|
||||
/// check | Vérifications
|
||||
|
||||
Ceci vous permettra d'obtenir des fonctionnalités de l'éditeur dans votre fonction, telles
|
||||
que des vérifications d'erreur, de l'auto-complétion, etc.
|
||||
Cela vous apporte la prise en charge par l'éditeur dans votre fonction, avec vérifications d'erreurs, autocomplétion, etc.
|
||||
|
||||
///
|
||||
|
||||
## <abbr title="aussi appelé sérialisation, ou parfois parsing ou marshalling en anglais">Conversion</abbr> de données
|
||||
## <dfn title="également appelé : sérialisation, parsing, marshalling">Conversion</dfn> de données { #data-conversion }
|
||||
|
||||
Si vous exécutez cet exemple et allez 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 aurez comme réponse :
|
||||
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 :
|
||||
|
||||
```JSON
|
||||
{"item_id":3}
|
||||
```
|
||||
|
||||
/// check | vérifier
|
||||
/// check | Vérifications
|
||||
|
||||
Comme vous l'avez remarqué, la valeur reçue par la fonction (et renvoyée ensuite) est `3`,
|
||||
en tant qu'entier (`int`) Python, pas la chaîne de caractères (`string`) `"3"`.
|
||||
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 ».
|
||||
|
||||
Grâce aux déclarations de types, **FastAPI** fournit du
|
||||
<abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">"parsing"</abbr> automatique.
|
||||
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.
|
||||
|
||||
///
|
||||
|
||||
## Validation de données
|
||||
## Validation de données { #data-validation }
|
||||
|
||||
Si vous allez sur <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, vous aurez une belle erreur HTTP :
|
||||
Mais si vous allez dans le navigateur sur <a href="http://127.0.0.1:8000/items/foo" class="external-link" target="_blank">http://127.0.0.1:8000/items/foo</a>, vous verrez une belle erreur HTTP :
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"path",
|
||||
"item_id"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer"
|
||||
}
|
||||
]
|
||||
"detail": [
|
||||
{
|
||||
"type": "int_parsing",
|
||||
"loc": [
|
||||
"path",
|
||||
"item_id"
|
||||
],
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer",
|
||||
"input": "foo"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
car le paramètre de chemin `item_id` possède comme valeur `"foo"`, qui ne peut pas être convertie en entier (`int`).
|
||||
car le paramètre de chemin `item_id` a pour valeur « foo », qui n'est pas un `int`.
|
||||
|
||||
La même erreur se produira si vous passez un nombre flottant (`float`) et non un entier, comme ici
|
||||
<a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>.
|
||||
La même erreur apparaîtrait si vous fournissiez un `float` au lieu d'un `int`, comme ici : <a href="http://127.0.0.1:8000/items/4.2" class="external-link" target="_blank">http://127.0.0.1:8000/items/4.2</a>
|
||||
|
||||
/// check | Vérifications
|
||||
|
||||
/// check | vérifier
|
||||
Ainsi, avec la même déclaration de type Python, **FastAPI** vous fournit la validation de données.
|
||||
|
||||
Donc, avec ces mêmes déclarations de type Python, **FastAPI** vous fournit de la validation de données.
|
||||
Remarquez que l'erreur indique clairement l'endroit exact où la validation n'a pas réussi.
|
||||
|
||||
Notez que l'erreur mentionne le point exact où la validation n'a pas réussi.
|
||||
|
||||
Ce qui est incroyablement utile au moment de développer et débugger du code qui interagit avec votre API.
|
||||
C'est incroyablement utile lors du développement et du débogage du code qui interagit avec votre API.
|
||||
|
||||
///
|
||||
|
||||
## Documentation
|
||||
## Documentation { #documentation }
|
||||
|
||||
Et quand vous vous rendez 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 la
|
||||
documentation générée automatiquement et interactive :
|
||||
Et lorsque vous ouvrez votre navigateur 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 une documentation d'API automatique et interactive comme :
|
||||
|
||||
<img src="/img/tutorial/path-params/image01.png">
|
||||
|
||||
/// info
|
||||
/// check | Vérifications
|
||||
|
||||
À nouveau, en utilisant uniquement les déclarations de type Python, **FastAPI** vous fournit automatiquement une documentation interactive (via Swagger UI).
|
||||
À nouveau, simplement avec cette même déclaration de type Python, **FastAPI** vous fournit une documentation interactive automatique (intégrant Swagger UI).
|
||||
|
||||
On voit bien dans la documentation que `item_id` est déclaré comme entier.
|
||||
Remarquez que le paramètre de chemin est déclaré comme entier.
|
||||
|
||||
///
|
||||
|
||||
## Les avantages d'avoir une documentation basée sur une norme, et la documentation alternative.
|
||||
## Les avantages d'une norme, documentation alternative { #standards-based-benefits-alternative-documentation }
|
||||
|
||||
Le schéma généré suivant la norme <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a>,
|
||||
il existe de nombreux outils compatibles.
|
||||
Et comme le schéma généré suit la norme <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" class="external-link" target="_blank">OpenAPI</a>, il existe de nombreux outils compatibles.
|
||||
|
||||
Grâce à cela, **FastAPI** lui-même fournit une documentation alternative (utilisant ReDoc), qui peut être lue
|
||||
sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> :
|
||||
Grâce à cela, **FastAPI** fournit lui-même une documentation d'API alternative (utilisant ReDoc), accessible sur <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> :
|
||||
|
||||
<img src="/img/tutorial/path-params/image02.png">
|
||||
|
||||
De la même façon, il existe bien d'autres outils compatibles, y compris des outils de génération de code
|
||||
pour de nombreux langages.
|
||||
De la même façon, il existe de nombreux outils compatibles, y compris des outils de génération de code pour de nombreux langages.
|
||||
|
||||
## Pydantic
|
||||
## Pydantic { #pydantic }
|
||||
|
||||
Toute la validation de données est effectué en arrière-plan avec <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>,
|
||||
dont vous bénéficierez de tous les avantages. Vous savez donc que vous êtes entre de bonnes mains.
|
||||
Toute la validation de données est effectuée sous le capot par <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a>, vous en bénéficiez donc pleinement. Vous savez ainsi que vous êtes entre de bonnes mains.
|
||||
|
||||
## L'ordre importe
|
||||
Vous pouvez utiliser les mêmes déclarations de type avec `str`, `float`, `bool` et de nombreux autres types de données complexes.
|
||||
|
||||
Quand vous créez des *fonctions de chemins*, vous pouvez vous retrouver dans une situation où vous avez un chemin fixe.
|
||||
Plusieurs d'entre eux sont explorés dans les prochains chapitres du tutoriel.
|
||||
|
||||
Tel que `/users/me`, disons pour récupérer les données sur l'utilisateur actuel.
|
||||
## L'ordre importe { #order-matters }
|
||||
|
||||
Et vous avez un second chemin : `/users/{user_id}` pour récupérer de la donnée sur un utilisateur spécifique grâce à son identifiant d'utilisateur
|
||||
Quand vous créez des *chemins d'accès*, vous pouvez vous retrouver dans une situation avec un chemin fixe.
|
||||
|
||||
Les *fonctions de chemin* étant évaluées dans l'ordre, il faut s'assurer que la fonction correspondant à `/users/me` est déclarée avant celle de `/users/{user_id}` :
|
||||
Par exemple `/users/me`, disons pour récupérer les données de l'utilisateur actuel.
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
|
||||
Et vous pouvez aussi avoir un chemin `/users/{user_id}` pour récupérer des données sur un utilisateur spécifique grâce à un identifiant d'utilisateur.
|
||||
|
||||
Sinon, le chemin `/users/{user_id}` correspondrait aussi à `/users/me`, la fonction "croyant" qu'elle a reçu un paramètre `user_id` avec pour valeur `"me"`.
|
||||
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}` :
|
||||
|
||||
## Valeurs prédéfinies
|
||||
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
|
||||
|
||||
Si vous avez une *fonction de chemin* qui reçoit un *paramètre de chemin*, mais que vous voulez que les valeurs possibles des paramètres soient prédéfinies, vous pouvez utiliser les <abbr title="Enumeration">`Enum`</abbr> de Python.
|
||||
Sinon, le chemin `/users/{user_id}` correspondrait aussi à `/users/me`, « pensant » qu'il reçoit un paramètre `user_id` avec la valeur « me ».
|
||||
|
||||
### Création d'un `Enum`
|
||||
De même, vous ne pouvez pas redéfinir un chemin d'accès :
|
||||
|
||||
Importez `Enum` et créez une sous-classe qui hérite de `str` et `Enum`.
|
||||
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
|
||||
|
||||
En héritant de `str` la documentation sera capable de savoir que les valeurs doivent être de type `string` et pourra donc afficher cette `Enum` correctement.
|
||||
Le premier sera toujours utilisé puisque le chemin correspond en premier.
|
||||
|
||||
Créez ensuite des attributs de classe avec des valeurs fixes, qui seront les valeurs autorisées pour cette énumération.
|
||||
## Valeurs prédéfinies { #predefined-values }
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
|
||||
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.
|
||||
|
||||
/// info
|
||||
### Créer une classe `Enum` { #create-an-enum-class }
|
||||
|
||||
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Les énumérations (ou enums) sont disponibles en Python</a> depuis la version 3.4.
|
||||
Importez `Enum` et créez une sous-classe qui hérite de `str` et de `Enum`.
|
||||
|
||||
///
|
||||
En héritant de `str`, la documentation de l'API saura que les valeurs doivent être de type `string` et pourra donc s'afficher correctement.
|
||||
|
||||
Créez ensuite des attributs de classe avec des valeurs fixes, qui seront les valeurs valides disponibles :
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Pour ceux qui se demandent, "AlexNet", "ResNet", et "LeNet" sont juste des noms de <abbr title="Techniquement, des architectures de modèles">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.
|
||||
|
||||
///
|
||||
|
||||
### Déclarer un paramètre de chemin
|
||||
### Déclarer un paramètre de chemin { #declare-a-path-parameter }
|
||||
|
||||
Créez ensuite un *paramètre de chemin* avec une annotation de type désignant l'énumération créée précédemment (`ModelName`) :
|
||||
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.py hl[16] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
|
||||
|
||||
### Documentation
|
||||
### Consulter la documentation { #check-the-docs }
|
||||
|
||||
Les valeurs disponibles pour le *paramètre de chemin* sont bien prédéfinies, la documentation les affiche correctement :
|
||||
Comme les valeurs disponibles pour le *paramètre de chemin* sont prédéfinies, la documentation interactive peut les afficher clairement :
|
||||
|
||||
<img src="/img/tutorial/path-params/image03.png">
|
||||
|
||||
### Manipuler les *énumérations* Python
|
||||
### Travailler avec les *énumérations* Python { #working-with-python-enumerations }
|
||||
|
||||
La valeur du *paramètre de chemin* sera un des "membres" de l'énumération.
|
||||
La valeur du *paramètre de chemin* sera un *membre d'énumération*.
|
||||
|
||||
#### Comparer les *membres d'énumération*
|
||||
#### Comparer des *membres d'énumération* { #compare-enumeration-members }
|
||||
|
||||
Vous pouvez comparer ce paramètre avec les membres de votre énumération `ModelName` :
|
||||
Vous pouvez le comparer avec le *membre d'énumération* dans votre enum `ModelName` :
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005.py hl[17] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
|
||||
|
||||
#### Récupérer la *valeur de l'énumération*
|
||||
#### Obtenir la *valeur de l'énumération* { #get-the-enumeration-value }
|
||||
|
||||
Vous pouvez obtenir la valeur réel d'un membre (une chaîne de caractères ici), avec `model_name.value`, ou en général, `votre_membre_d'enum.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.py hl[20] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous pouvez aussi accéder la valeur `"lenet"` avec `ModelName.lenet.value`.
|
||||
Vous pouvez aussi accéder à la valeur « lenet » avec `ModelName.lenet.value`.
|
||||
|
||||
///
|
||||
|
||||
#### Retourner des *membres d'énumération*
|
||||
#### Retourner des *membres d'énumération* { #return-enumeration-members }
|
||||
|
||||
Vous pouvez retourner des *membres d'énumération* dans vos *fonctions de chemin*, même imbriquée dans un JSON (e.g. un `dict`).
|
||||
Vous pouvez retourner des *membres d'énumération* depuis votre *chemin d'accès*, même imbriqués dans un corps JSON (par ex. un `dict`).
|
||||
|
||||
Ils seront convertis vers leurs valeurs correspondantes (chaînes de caractères ici) avant d'être transmis au client :
|
||||
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.py hl[18,21,23] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
|
||||
|
||||
Le client recevra une réponse JSON comme celle-ci :
|
||||
Dans votre client, vous recevrez une réponse JSON comme :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -208,53 +199,53 @@ Le client recevra une réponse JSON comme celle-ci :
|
||||
}
|
||||
```
|
||||
|
||||
## Paramètres de chemin contenant des chemins
|
||||
## Paramètres de chemin contenant des chemins { #path-parameters-containing-paths }
|
||||
|
||||
Disons que vous avez une *fonction de chemin* liée au chemin `/files/{file_path}`.
|
||||
Disons que vous avez un *chemin d'accès* avec un chemin `/files/{file_path}`.
|
||||
|
||||
Mais que `file_path` lui-même doit contenir un *chemin*, comme `home/johndoe/myfile.txt` par exemple.
|
||||
Mais vous avez besoin que `file_path` lui-même contienne un *chemin*, comme `home/johndoe/myfile.txt`.
|
||||
|
||||
Donc, l'URL pour ce fichier pourrait être : `/files/home/johndoe/myfile.txt`.
|
||||
Ainsi, l'URL pour ce fichier serait : `/files/home/johndoe/myfile.txt`.
|
||||
|
||||
### Support d'OpenAPI
|
||||
### Support d'OpenAPI { #openapi-support }
|
||||
|
||||
OpenAPI ne supporte pas de manière de déclarer un paramètre de chemin contenant un *chemin*, cela pouvant causer des scénarios difficiles à tester et définir.
|
||||
OpenAPI ne prend pas en charge une manière de déclarer un *paramètre de chemin* contenant un *chemin* à l'intérieur, car cela peut conduire à des scénarios difficiles à tester et à définir.
|
||||
|
||||
Néanmoins, cela reste faisable dans **FastAPI**, via les outils internes de Starlette.
|
||||
Néanmoins, vous pouvez toujours le faire dans **FastAPI**, en utilisant l'un des outils internes de Starlette.
|
||||
|
||||
Et la documentation fonctionne quand même, bien qu'aucune section ne soit ajoutée pour dire que la paramètre devrait contenir un *chemin*.
|
||||
Et la documentation fonctionnera quand même, même si aucune indication supplémentaire ne sera ajoutée pour dire que le paramètre doit contenir un chemin.
|
||||
|
||||
### Convertisseur de *chemin*
|
||||
### Convertisseur de chemin { #path-convertor }
|
||||
|
||||
En utilisant une option de Starlette directement, vous pouvez déclarer un *paramètre de chemin* contenant un *chemin* avec une URL comme :
|
||||
En utilisant une option directement depuis Starlette, vous pouvez déclarer un *paramètre de chemin* contenant un *chemin* avec une URL comme :
|
||||
|
||||
```
|
||||
/files/{file_path:path}
|
||||
```
|
||||
|
||||
Dans ce cas, le nom du paramètre est `file_path`, et la dernière partie, `:path`, indique à Starlette que le paramètre devrait correspondre à un *chemin*.
|
||||
Dans ce cas, le nom du paramètre est `file_path`, et la dernière partie, `:path`, indique que le paramètre doit correspondre à n'importe quel *chemin*.
|
||||
|
||||
Vous pouvez donc l'utilisez comme tel :
|
||||
Vous pouvez donc l'utiliser ainsi :
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004.py hl[6] *}
|
||||
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Vous pourriez avoir besoin que le paramètre contienne `/home/johndoe/myfile.txt`, avec un slash au début (`/`).
|
||||
Vous pourriez avoir besoin que le paramètre contienne `/home/johndoe/myfile.txt`, avec un slash initial (`/`).
|
||||
|
||||
Dans ce cas, l'URL serait : `/files//home/johndoe/myfile.txt`, avec un double slash (`//`) entre `files` et `home`.
|
||||
|
||||
///
|
||||
|
||||
## Récapitulatif
|
||||
## Récapitulatif { #recap }
|
||||
|
||||
Avec **FastAPI**, en utilisant les déclarations de type rapides, intuitives et standards de Python, vous bénéficiez de :
|
||||
Avec **FastAPI**, en utilisant des déclarations de type Python courtes, intuitives et standard, vous obtenez :
|
||||
|
||||
* Support de l'éditeur : vérification d'erreurs, auto-complétion, etc.
|
||||
* <abbr title="conversion de la chaîne de caractères venant de la requête HTTP en données Python">"Parsing"</abbr> de données.
|
||||
* Validation de données.
|
||||
* Annotations d'API et documentation automatique.
|
||||
* Support de l'éditeur : vérifications d'erreurs, autocomplétion, etc.
|
||||
* 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
|
||||
|
||||
Et vous n'avez besoin de le déclarer qu'une fois.
|
||||
Et vous n'avez besoin de les déclarer qu'une seule fois.
|
||||
|
||||
C'est probablement l'avantage visible principal de **FastAPI** comparé aux autres *frameworks* (outre les performances pures).
|
||||
C'est probablement l'avantage visible principal de **FastAPI** comparé aux autres frameworks (outre les performances pures).
|
||||
|
||||
@@ -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. 🤫
|
||||
|
||||
///
|
||||
@@ -1,166 +1,249 @@
|
||||
# Paramètres de requête et validations de chaînes de caractères
|
||||
# Paramètres de requête et validations de chaînes de caractères { #query-parameters-and-string-validations }
|
||||
|
||||
**FastAPI** vous permet de déclarer des informations et des validateurs additionnels pour vos paramètres de requêtes.
|
||||
**FastAPI** vous permet de déclarer des informations et des validations supplémentaires pour vos paramètres.
|
||||
|
||||
Commençons avec cette application pour exemple :
|
||||
Prenons cette application comme exemple :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial001.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
|
||||
|
||||
Le paramètre de requête `q` a pour type `Union[str, None]` (ou `str | None` en Python 3.10), signifiant qu'il est de type `str` mais pourrait aussi être égal à `None`, et bien sûr, la valeur par défaut est `None`, donc **FastAPI** saura qu'il n'est pas requis.
|
||||
Le paramètre de requête `q` est de type `str | None`, cela signifie qu’il est de type `str` mais peut aussi être `None`, et en effet, la valeur par défaut est `None`, donc FastAPI saura qu’il n’est pas requis.
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`.
|
||||
FastAPI saura que la valeur de `q` n’est pas requise grâce à la valeur par défaut `= None`.
|
||||
|
||||
Le `Union` dans `Union[str, None]` permettra à votre éditeur de vous offrir un meilleur support et de détecter les erreurs.
|
||||
Avoir `str | None` permettra à votre éditeur de vous offrir un meilleur support et de détecter les erreurs.
|
||||
|
||||
///
|
||||
|
||||
## Validation additionnelle
|
||||
## Validation additionnelle { #additional-validation }
|
||||
|
||||
Nous allons imposer que bien que `q` soit un paramètre optionnel, dès qu'il est fourni, **sa longueur n'excède pas 50 caractères**.
|
||||
Nous allons imposer que, même si `q` est optionnel, dès qu’il est fourni, **sa longueur n’excède pas 50 caractères**.
|
||||
|
||||
## Importer `Query`
|
||||
### Importer `Query` et `Annotated` { #import-query-and-annotated }
|
||||
|
||||
Pour cela, importez d'abord `Query` depuis `fastapi` :
|
||||
Pour ce faire, importez d’abord :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[3] *}
|
||||
- `Query` depuis `fastapi`
|
||||
- `Annotated` depuis `typing`
|
||||
|
||||
## Utiliser `Query` comme valeur par défaut
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
|
||||
|
||||
Construisez ensuite la valeur par défaut de votre paramètre avec `Query`, en choisissant 50 comme `max_length` :
|
||||
/// info
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002.py hl[9] *}
|
||||
FastAPI a ajouté la prise en charge de `Annotated` (et a commencé à le recommander) dans la version 0.95.0.
|
||||
|
||||
Comme nous devons remplacer la valeur par défaut `None` dans la fonction par `Query()`, nous pouvons maintenant définir la valeur par défaut avec le paramètre `Query(default=None)`, il sert le même objectif qui est de définir cette valeur par défaut.
|
||||
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d’utiliser `Annotated`.
|
||||
|
||||
Assurez-vous de [mettre à niveau la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} vers au moins 0.95.1 avant d’utiliser `Annotated`.
|
||||
|
||||
///
|
||||
|
||||
## Utiliser `Annotated` dans le type pour le paramètre `q` { #use-annotated-in-the-type-for-the-q-parameter }
|
||||
|
||||
Vous vous souvenez que je vous ai dit plus tôt que `Annotated` peut être utilisé pour ajouter des métadonnées à vos paramètres dans l’[Introduction aux types Python](../python-types.md#type-hints-with-metadata-annotations){.internal-link target=_blank} ?
|
||||
|
||||
C’est le moment de l’utiliser avec FastAPI. 🚀
|
||||
|
||||
Nous avions cette annotation de type :
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Ce que nous allons faire, c’est l’englober avec `Annotated`, de sorte que cela devienne :
|
||||
|
||||
```Python
|
||||
q: Annotated[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, c’est `None`.
|
||||
|
||||
Passons maintenant aux choses amusantes. 🎉
|
||||
|
||||
## Ajouter `Query` à `Annotated` dans le paramètre `q` { #add-query-to-annotated-in-the-q-parameter }
|
||||
|
||||
Maintenant que nous avons cet `Annotated` dans lequel nous pouvons mettre plus d’informations (dans ce cas une validation supplémentaire), ajoutez `Query` à l’intérieur de `Annotated`, et définissez le paramètre `max_length` à `50` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
|
||||
|
||||
Remarquez que la valeur par défaut est toujours `None`, donc le paramètre est toujours optionnel.
|
||||
|
||||
Mais maintenant, avec `Query(max_length=50)` à l’intérieur de `Annotated`, nous indiquons à FastAPI que nous voulons **une validation supplémentaire** pour cette valeur, nous voulons qu’elle ait au maximum 50 caractères. 😎
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Ici nous utilisons `Query()` parce qu’il s’agit d’un **paramètre de requête**. Plus tard nous verrons d’autres comme `Path()`, `Body()`, `Header()` et `Cookie()`, qui acceptent également les mêmes arguments que `Query()`.
|
||||
|
||||
///
|
||||
|
||||
FastAPI va maintenant :
|
||||
|
||||
- **Valider** les données en s’assurant que la longueur maximale est de 50 caractères
|
||||
- Afficher une **erreur claire** au client quand les données ne sont pas valides
|
||||
- **Documenter** le paramètre dans la *chemin d'accès* du schéma OpenAPI (il apparaîtra donc dans l’**interface de documentation automatique**)
|
||||
|
||||
## Alternative (ancienne) : `Query` comme valeur par défaut { #alternative-old-query-as-the-default-value }
|
||||
|
||||
Les versions précédentes de FastAPI (avant <dfn title="avant 2023-03">0.95.0</dfn>) exigeaient d’utiliser `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 l’utilise encore, je vais donc vous l’expliquer.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Pour du nouveau code et dès que possible, utilisez `Annotated` comme expliqué ci-dessus. Il y a de multiples avantages (expliqués ci-dessous) et aucun inconvénient. 🍰
|
||||
|
||||
///
|
||||
|
||||
Voici comment vous utiliseriez `Query()` comme valeur par défaut du paramètre de votre fonction, en définissant le paramètre `max_length` à 50 :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Comme, dans ce cas (sans utiliser `Annotated`), nous devons remplacer la valeur par défaut `None` dans la fonction par `Query()`, nous devons maintenant définir la valeur par défaut avec le paramètre `Query(default=None)`, cela sert le même objectif de définir cette valeur par défaut (au moins pour FastAPI).
|
||||
|
||||
Donc :
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None)
|
||||
q: str | None = Query(default=None)
|
||||
```
|
||||
|
||||
... rend le paramètre optionnel, et est donc équivalent à :
|
||||
... rend le paramètre optionnel, avec une valeur par défaut de `None`, comme :
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Mais déclare explicitement `q` comme étant un paramètre de requête.
|
||||
Mais la version avec `Query` le déclare explicitement comme étant un paramètre de requête.
|
||||
|
||||
/// info
|
||||
|
||||
Gardez à l'esprit que la partie la plus importante pour rendre un paramètre optionnel est :
|
||||
Ensuite, nous pouvons passer plus de paramètres à `Query`. Dans ce cas, le paramètre `max_length` qui s’applique aux chaînes de caractères :
|
||||
|
||||
```Python
|
||||
= None
|
||||
q: str | None = Query(default=None, max_length=50)
|
||||
```
|
||||
|
||||
ou :
|
||||
Cela validera les données, affichera une erreur claire lorsque les données ne sont pas valides et documentera le paramètre dans la *chemin d'accès* du schéma OpenAPI.
|
||||
|
||||
### `Query` comme valeur par défaut ou dans `Annotated` { #query-as-the-default-value-or-in-annotated }
|
||||
|
||||
Gardez à l’esprit qu’en utilisant `Query` à l’intérieur de `Annotated`, vous ne pouvez pas utiliser le paramètre `default` de `Query`.
|
||||
|
||||
Utilisez à la place la valeur par défaut réelle du paramètre de fonction. Sinon, ce serait incohérent.
|
||||
|
||||
Par exemple, ceci n’est pas autorisé :
|
||||
|
||||
```Python
|
||||
= Query(None)
|
||||
q: Annotated[str, Query(default="rick")] = "morty"
|
||||
```
|
||||
|
||||
et utilisera ce `None` pour détecter que ce paramètre de requête **n'est pas requis**.
|
||||
... parce qu’il n’est pas clair si la valeur par défaut doit être « rick » ou « morty ».
|
||||
|
||||
Le `Union[str, None]` est uniquement là pour permettre à votre éditeur un meilleur support.
|
||||
Donc, vous utiliseriez (de préférence) :
|
||||
|
||||
```Python
|
||||
q: Annotated[str, Query()] = "rick"
|
||||
```
|
||||
|
||||
... ou dans des bases de code plus anciennes, vous trouverez :
|
||||
|
||||
```Python
|
||||
q: str = Query(default="rick")
|
||||
```
|
||||
|
||||
### Avantages de `Annotated` { #advantages-of-annotated }
|
||||
|
||||
**L’utilisation de `Annotated` est recommandée** plutôt que la valeur par défaut dans les paramètres de fonction, c’est **mieux** pour plusieurs raisons. 🤓
|
||||
|
||||
La valeur **par défaut** du **paramètre de fonction** est la **vraie valeur par défaut**, c’est plus intuitif en Python en général. 😌
|
||||
|
||||
Vous pouvez **appeler** cette même fonction dans **d’autres endroits** sans FastAPI, et elle **fonctionnera comme prévu**. S’il y a un paramètre **requis** (sans valeur par défaut), votre **éditeur** vous le signalera avec une erreur, **Python** se plaindra aussi si vous l’exécutez sans passer le paramètre requis.
|
||||
|
||||
Quand vous n’utilisez pas `Annotated` et utilisez à la place l’**ancienne** méthode avec la **valeur par défaut**, si vous appelez cette fonction sans FastAPI dans **d’autres endroits**, vous devez **penser** à passer les arguments à la fonction pour qu’elle fonctionne correctement, sinon les valeurs seront différentes de ce que vous attendez (par ex. `QueryInfo` ou quelque chose de similaire au lieu d’une `str`). Et votre éditeur ne se plaindra pas, et Python ne se plaindra pas en exécutant cette fonction, seulement quand les opérations internes échoueront.
|
||||
|
||||
Comme `Annotated` peut avoir plus d’une annotation de métadonnées, vous pouvez maintenant même utiliser la même fonction avec d’autres outils, comme <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">Typer</a>. 🚀
|
||||
|
||||
## Ajouter plus de validations { #add-more-validations }
|
||||
|
||||
Vous pouvez également ajouter un paramètre `min_length` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## Ajouter des expressions régulières { #add-regular-expressions }
|
||||
|
||||
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] *}
|
||||
|
||||
Ce pattern d’expression régulière spécifique vérifie que la valeur reçue pour le paramètre :
|
||||
|
||||
- `^` : commence avec les caractères qui suivent, n’a pas de caractères avant.
|
||||
- `fixedquery` : a exactement la valeur `fixedquery`.
|
||||
- `$` : se termine là, n’a pas d’autres caractères après `fixedquery`.
|
||||
|
||||
Si vous vous sentez perdu avec toutes ces idées d’**« expression régulière »**, pas d’inquiétude. C’est un sujet difficile pour beaucoup. Vous pouvez déjà faire beaucoup de choses sans avoir besoin d’expressions régulières.
|
||||
|
||||
Désormais, vous savez que, lorsque vous en aurez besoin, vous pourrez les utiliser dans **FastAPI**.
|
||||
|
||||
## Valeurs par défaut { #default-values }
|
||||
|
||||
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_py310.py hl[9] *}
|
||||
|
||||
/// note | Remarque
|
||||
|
||||
Avoir une valeur par défaut de n’importe quel type, y compris `None`, rend le paramètre optionnel (non requis).
|
||||
|
||||
///
|
||||
|
||||
Ensuite, nous pouvons passer d'autres paramètres à `Query`. Dans cet exemple, le paramètre `max_length` qui s'applique aux chaînes de caractères :
|
||||
## Paramètres requis { #required-parameters }
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, max_length=50)
|
||||
```
|
||||
|
||||
Cela va valider les données, montrer une erreur claire si ces dernières ne sont pas valides, et documenter le paramètre dans le schéma `OpenAPI` de cette *path operation*.
|
||||
|
||||
## Rajouter plus de validation
|
||||
|
||||
Vous pouvez aussi rajouter un second paramètre `min_length` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial003.py hl[9] *}
|
||||
|
||||
## Ajouter des validations par expressions régulières
|
||||
|
||||
On peut définir une <abbr title="Une expression régulière, regex ou regexp est une suite de caractères qui définit un pattern de correspondance pour les chaînes de caractères.">expression régulière</abbr> à laquelle le paramètre doit correspondre :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004.py hl[10] *}
|
||||
|
||||
Cette expression régulière vérifie que la valeur passée comme paramètre :
|
||||
|
||||
* `^` : commence avec les caractères qui suivent, avec aucun caractère avant ceux-là.
|
||||
* `fixedquery` : a pour valeur exacte `fixedquery`.
|
||||
* `$` : se termine directement ensuite, n'a pas d'autres caractères après `fixedquery`.
|
||||
|
||||
Si vous vous sentez perdu avec le concept d'**expression régulière**, pas d'inquiétudes. Il s'agit d'une notion difficile pour beaucoup, et l'on peut déjà réussir à faire beaucoup sans jamais avoir à les manipuler.
|
||||
|
||||
Mais si vous décidez d'apprendre à les utiliser, sachez qu'ensuite vous pouvez les utiliser directement dans **FastAPI**.
|
||||
|
||||
## Valeurs par défaut
|
||||
|
||||
De la même façon que vous pouvez passer `None` comme premier argument pour l'utiliser comme valeur par défaut, vous pouvez passer d'autres valeurs.
|
||||
|
||||
Disons que vous déclarez le paramètre `q` comme ayant une longueur minimale de `3`, et une valeur par défaut étant `"fixedquery"` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005.py hl[7] *}
|
||||
|
||||
/// note | Rappel
|
||||
|
||||
Avoir une valeur par défaut rend le paramètre optionnel.
|
||||
|
||||
///
|
||||
|
||||
## Rendre ce paramètre requis
|
||||
|
||||
Quand on ne déclare ni validation, ni métadonnée, on peut rendre le paramètre `q` requis en ne lui déclarant juste aucune valeur par défaut :
|
||||
Quand nous n’avons pas besoin de déclarer plus de validations ou de métadonnées, nous pouvons rendre le paramètre de requête `q` requis en n’indiquant simplement pas de valeur par défaut, comme :
|
||||
|
||||
```Python
|
||||
q: str
|
||||
```
|
||||
|
||||
à la place de :
|
||||
au lieu de :
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Mais maintenant, on déclare `q` avec `Query`, comme ceci :
|
||||
Mais maintenant nous le déclarons avec `Query`, par exemple ainsi :
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = Query(default=None, min_length=3)
|
||||
q: Annotated[str | None, Query(min_length=3)] = None
|
||||
```
|
||||
|
||||
Donc pour déclarer une valeur comme requise tout en utilisant `Query`, il faut utiliser `...` comme premier argument :
|
||||
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.py hl[7] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
|
||||
|
||||
/// info
|
||||
### Requis, peut valoir `None` { #required-can-be-none }
|
||||
|
||||
Si vous n'avez jamais vu ce `...` auparavant : c'est une des constantes natives de Python <a href="https://docs.python.org/fr/3/library/constants.html#Ellipsis" class="external-link" target="_blank">appelée "Ellipsis"</a>.
|
||||
Vous pouvez déclarer qu’un paramètre accepte `None`, mais qu’il est tout de même requis. Cela obligerait les clients à envoyer une valeur, même si la valeur est `None`.
|
||||
|
||||
///
|
||||
Pour ce faire, vous pouvez déclarer que `None` est un type valide tout en ne déclarant pas de valeur par défaut :
|
||||
|
||||
Cela indiquera à **FastAPI** que la présence de ce paramètre est obligatoire.
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
|
||||
|
||||
## Liste de paramètres / valeurs multiples via Query
|
||||
## Liste de paramètres de requête / valeurs multiples { #query-parameter-list-multiple-values }
|
||||
|
||||
Quand on définit un paramètre de requête explicitement avec `Query` on peut aussi déclarer qu'il reçoit une liste de valeur, ou des "valeurs multiples".
|
||||
Quand vous définissez un paramètre de requête explicitement avec `Query`, vous pouvez aussi déclarer qu’il reçoit une liste de valeurs, autrement dit, qu’il reçoit des valeurs multiples.
|
||||
|
||||
Par exemple, pour déclarer un paramètre de requête `q` qui peut apparaître plusieurs fois dans une URL, on écrit :
|
||||
Par exemple, pour déclarer un paramètre de requête `q` qui peut apparaître plusieurs fois dans l’URL, vous pouvez écrire :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial011.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
Ce qui fait qu'avec une URL comme :
|
||||
Ensuite, avec une URL comme :
|
||||
|
||||
```
|
||||
http://localhost:8000/items/?q=foo&q=bar
|
||||
```
|
||||
|
||||
vous recevriez les valeurs des multiples paramètres de requête `q` (`foo` et `bar`) dans une `list` Python au sein de votre fonction de **path operation**, dans le paramètre de fonction `q`.
|
||||
vous recevriez les valeurs des multiples paramètres de requête `q` (`foo` et `bar`) dans une `list` Python à l’intérieur de votre fonction de *chemin d'accès*, dans le *paramètre de fonction* `q`.
|
||||
|
||||
Donc la réponse de cette URL serait :
|
||||
Donc, la réponse pour cette URL serait :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -173,19 +256,19 @@ Donc la réponse de cette URL serait :
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Pour déclarer un paramètre de requête de type `list`, comme dans l'exemple ci-dessus, il faut explicitement utiliser `Query`, sinon cela sera interprété comme faisant partie du corps de la requête.
|
||||
Pour déclarer un paramètre de requête avec un type `list`, comme dans l’exemple ci-dessus, vous devez explicitement utiliser `Query`, sinon il serait interprété comme faisant partie du corps de la requête.
|
||||
|
||||
///
|
||||
|
||||
La documentation sera donc mise à jour automatiquement pour autoriser plusieurs valeurs :
|
||||
L’interface de documentation interactive de l’API sera mise à jour en conséquence, pour autoriser plusieurs valeurs :
|
||||
|
||||
<img src="/img/tutorial/query-params-str-validations/image02.png">
|
||||
|
||||
### Combiner liste de paramètres et valeurs par défaut
|
||||
### Liste de paramètres de requête / valeurs multiples avec valeurs par défaut { #query-parameter-list-multiple-values-with-defaults }
|
||||
|
||||
Et l'on peut aussi définir une liste de valeurs par défaut si aucune n'est fournie :
|
||||
Vous pouvez également définir une `list` de valeurs par défaut si aucune n’est fournie :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
|
||||
|
||||
Si vous allez à :
|
||||
|
||||
@@ -193,9 +276,7 @@ Si vous allez à :
|
||||
http://localhost:8000/items/
|
||||
```
|
||||
|
||||
la valeur par défaut de `q` sera : `["foo", "bar"]`
|
||||
|
||||
et la réponse sera :
|
||||
la valeur par défaut de `q` sera : `["foo", "bar"]` et votre réponse sera :
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -206,93 +287,163 @@ et la réponse sera :
|
||||
}
|
||||
```
|
||||
|
||||
#### Utiliser `list`
|
||||
#### Utiliser simplement `list` { #using-just-list }
|
||||
|
||||
Il est aussi possible d'utiliser directement `list` plutôt que `List[str]` :
|
||||
Vous pouvez aussi utiliser `list` directement au lieu de `list[str]` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013.py hl[7] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
Dans ce cas-là, **FastAPI** ne vérifiera pas le contenu de la liste.
|
||||
Gardez à l’esprit que dans ce cas, FastAPI ne vérifiera pas le contenu de la liste.
|
||||
|
||||
Par exemple, `List[int]` vérifiera (et documentera) que la liste est bien entièrement composée d'entiers. Alors qu'un simple `list` ne ferait pas cette vérification.
|
||||
Par exemple, `list[int]` vérifierait (et documenterait) que le contenu de la liste est composé d’entiers. Mais un simple `list` ne le ferait pas.
|
||||
|
||||
///
|
||||
|
||||
## Déclarer des métadonnées supplémentaires
|
||||
## Déclarer plus de métadonnées { #declare-more-metadata }
|
||||
|
||||
On peut aussi ajouter plus d'informations sur le paramètre.
|
||||
Vous pouvez ajouter plus d’informations à propos du paramètre.
|
||||
|
||||
Ces informations seront incluses dans le schéma `OpenAPI` généré et utilisées par la documentation interactive ou les outils externes utilisés.
|
||||
Ces informations seront incluses dans l’OpenAPI généré et utilisées par les interfaces de documentation et les outils externes.
|
||||
|
||||
/// note
|
||||
/// note | Remarque
|
||||
|
||||
Gardez en tête que les outils externes utilisés ne supportent pas forcément tous parfaitement OpenAPI.
|
||||
Gardez à l’esprit que différents outils peuvent avoir des niveaux de prise en charge d’OpenAPI différents.
|
||||
|
||||
Il se peut donc que certains d'entre eux n'utilisent pas toutes les métadonnées que vous avez déclarées pour le moment, bien que dans la plupart des cas, les fonctionnalités manquantes ont prévu d'être implémentées.
|
||||
Certains d’entre eux pourraient ne pas encore afficher toutes les informations supplémentaires déclarées, bien que, dans la plupart des cas, la fonctionnalité manquante soit déjà prévue au développement.
|
||||
|
||||
///
|
||||
|
||||
Vous pouvez ajouter un `title` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial007.py hl[10] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
|
||||
|
||||
Et une `description` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial008.py hl[13] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
|
||||
|
||||
## Alias de paramètres
|
||||
## Paramètres avec alias { #alias-parameters }
|
||||
|
||||
Imaginez que vous vouliez que votre paramètre se nomme `item-query`.
|
||||
Imaginez que vous vouliez que le paramètre soit `item-query`.
|
||||
|
||||
Comme dans la requête :
|
||||
Comme dans :
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
```
|
||||
|
||||
Mais `item-query` n'est pas un nom de variable valide en Python.
|
||||
Mais `item-query` n’est pas un nom de variable Python valide.
|
||||
|
||||
Le nom le plus proche serait `item_query`.
|
||||
Le plus proche serait `item_query`.
|
||||
|
||||
Mais vous avez vraiment envie que ce soit exactement `item-query`...
|
||||
Mais vous avez quand même besoin que ce soit exactement `item-query` ...
|
||||
|
||||
Pour cela vous pouvez déclarer un `alias`, et cet alias est ce qui sera utilisé pour trouver la valeur du paramètre :
|
||||
Vous pouvez alors déclarer un `alias`, et cet alias sera utilisé pour trouver la valeur du paramètre :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial009.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
|
||||
|
||||
## Déprécier des paramètres
|
||||
## Déprécier des paramètres { #deprecating-parameters }
|
||||
|
||||
Disons que vous ne vouliez plus utiliser ce paramètre désormais.
|
||||
Disons que vous n’aimez plus ce paramètre.
|
||||
|
||||
Il faut qu'il continue à exister pendant un certain temps car vos clients l'utilisent, mais vous voulez que la documentation mentionne clairement que ce paramètre est <abbr title="obsolète, recommandé de ne pas l'utiliser">déprécié</abbr>.
|
||||
Vous devez le laisser là quelque temps car des clients l’utilisent, mais vous voulez que les documents l’affichent clairement comme <dfn title="obsolète, il est recommandé de ne pas l’utiliser">déprécié</dfn>.
|
||||
|
||||
On utilise alors l'argument `deprecated=True` de `Query` :
|
||||
Passez alors le paramètre `deprecated=True` à `Query` :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial010.py hl[18] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
|
||||
|
||||
La documentation le présentera comme il suit :
|
||||
Les documents l’afficheront ainsi :
|
||||
|
||||
<img src="/img/tutorial/query-params-str-validations/image01.png">
|
||||
|
||||
## Pour résumer
|
||||
## Exclure des paramètres d’OpenAPI { #exclude-parameters-from-openapi }
|
||||
|
||||
Il est possible d'ajouter des validateurs et métadonnées pour vos paramètres.
|
||||
Pour exclure un paramètre de requête du schéma OpenAPI généré (et donc, des systèmes de documentation automatiques), définissez le paramètre `include_in_schema` de `Query` à `False` :
|
||||
|
||||
Validateurs et métadonnées génériques:
|
||||
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
|
||||
|
||||
* `alias`
|
||||
* `title`
|
||||
* `description`
|
||||
* `deprecated`
|
||||
## Validation personnalisée { #custom-validation }
|
||||
|
||||
Validateurs spécifiques aux chaînes de caractères :
|
||||
Il peut y avoir des cas où vous devez faire une **validation personnalisée** qui ne peut pas être réalisée avec les paramètres montrés ci-dessus.
|
||||
|
||||
* `min_length`
|
||||
* `max_length`
|
||||
* `regex`
|
||||
Dans ces cas, vous pouvez utiliser une **fonction de validation personnalisée** qui est appliquée après la validation normale (par ex. après avoir validé que la valeur est une `str`).
|
||||
|
||||
Parmi ces exemples, vous avez pu voir comment déclarer des validateurs pour les chaînes de caractères.
|
||||
Vous pouvez y parvenir en utilisant <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator" class="external-link" target="_blank">`AfterValidator` de Pydantic</a> à l’intérieur de `Annotated`.
|
||||
|
||||
Dans les prochains chapitres, vous verrez comment déclarer des validateurs pour d'autres types, comme les nombres.
|
||||
/// tip | Astuce
|
||||
|
||||
Pydantic a aussi <a href="https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator" class="external-link" target="_blank">`BeforeValidator`</a> et d’autres. 🤓
|
||||
|
||||
///
|
||||
|
||||
Par exemple, ce validateur personnalisé vérifie que l’ID d’item 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 d’URL 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] *}
|
||||
|
||||
/// info
|
||||
|
||||
C’est disponible avec Pydantic version 2 ou supérieure. 😎
|
||||
|
||||
///
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
Si vous devez faire un type de validation qui nécessite de communiquer avec un **composant externe**, comme une base de données ou une autre API, vous devez plutôt utiliser les **Dépendances de FastAPI**, vous en apprendrez davantage plus tard.
|
||||
|
||||
Ces validateurs personnalisés sont destinés aux éléments qui peuvent être vérifiés **uniquement** avec les **mêmes données** fournies dans la requête.
|
||||
|
||||
///
|
||||
|
||||
### Comprendre ce code { #understand-that-code }
|
||||
|
||||
Le point important est simplement d’utiliser **`AfterValidator` avec une fonction à l’intérieur de `Annotated`**. N’hésitez pas à passer cette partie. 🤸
|
||||
|
||||
---
|
||||
|
||||
Mais si vous êtes curieux de cet exemple de code spécifique et que vous êtes toujours partant, voici quelques détails supplémentaires.
|
||||
|
||||
#### Chaîne avec `value.startswith()` { #string-with-value-startswith }
|
||||
|
||||
Avez-vous remarqué ? Une chaîne utilisant `value.startswith()` peut prendre un tuple, et elle vérifiera chaque valeur du tuple :
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
|
||||
|
||||
#### Un élément aléatoire { #a-random-item }
|
||||
|
||||
Avec `data.items()` nous obtenons un <dfn title="Quelque chose que l’on 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())`.
|
||||
|
||||
Ensuite, avec `random.choice()` nous pouvons obtenir une **valeur aléatoire** depuis la liste, nous obtenons donc un tuple `(id, name)`. Ce sera quelque chose comme `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
|
||||
|
||||
Puis nous **affectons ces deux valeurs** du tuple aux variables `id` et `name`.
|
||||
|
||||
Ainsi, si l’utilisateur n’a pas fourni d’ID d’item, il recevra quand même une suggestion aléatoire.
|
||||
|
||||
... nous faisons tout cela en **une seule ligne simple**. 🤯 Vous n’adorez pas Python ? 🐍
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
|
||||
|
||||
## Récapitulatif { #recap }
|
||||
|
||||
Vous pouvez déclarer des validations et des métadonnées supplémentaires pour vos paramètres.
|
||||
|
||||
Validations et métadonnées génériques :
|
||||
|
||||
- `alias`
|
||||
- `title`
|
||||
- `description`
|
||||
- `deprecated`
|
||||
|
||||
Validations spécifiques aux chaînes :
|
||||
|
||||
- `min_length`
|
||||
- `max_length`
|
||||
- `pattern`
|
||||
|
||||
Validations personnalisées avec `AfterValidator`.
|
||||
|
||||
Dans ces exemples, vous avez vu comment déclarer des validations pour des valeurs `str`.
|
||||
|
||||
Voyez les prochains chapitres pour apprendre à déclarer des validations pour d’autres types, comme les nombres.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user