committed by
GitHub
2 changed files with 255 additions and 0 deletions
@ -0,0 +1,254 @@ |
|||||
|
# Paramètres de chemin |
||||
|
|
||||
|
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> : |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="6-7" |
||||
|
{!../../../docs_src/path_params/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
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 : |
||||
|
|
||||
|
```JSON |
||||
|
{"item_id":"foo"} |
||||
|
``` |
||||
|
|
||||
|
## Paramètres de chemin typés |
||||
|
|
||||
|
Vous pouvez déclarer le type d'un paramètre de chemin dans la fonction, en utilisant les annotations de type Python : |
||||
|
|
||||
|
|
||||
|
```Python hl_lines="7" |
||||
|
{!../../../docs_src/path_params/tutorial002.py!} |
||||
|
``` |
||||
|
|
||||
|
Ici, `item_id` est déclaré comme `int`. |
||||
|
|
||||
|
!!! hint "Astuce" |
||||
|
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. |
||||
|
|
||||
|
## <abbr title="aussi appelé sérialisation, ou parfois parsing ou marshalling en anglais">Conversion</abbr> de données |
||||
|
|
||||
|
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 : |
||||
|
|
||||
|
```JSON |
||||
|
{"item_id":3} |
||||
|
``` |
||||
|
|
||||
|
!!! hint "Astuce" |
||||
|
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"`. |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
## Validation de données |
||||
|
|
||||
|
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 : |
||||
|
|
||||
|
```JSON |
||||
|
{ |
||||
|
"detail": [ |
||||
|
{ |
||||
|
"loc": [ |
||||
|
"path", |
||||
|
"item_id" |
||||
|
], |
||||
|
"msg": "value is not a valid integer", |
||||
|
"type": "type_error.integer" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
car le paramètre de chemin `item_id` possède comme valeur `"foo"`, qui ne peut pas être convertie en entier (`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>. |
||||
|
|
||||
|
|
||||
|
!!! hint "Astuce" |
||||
|
Donc, avec ces mêmes déclarations de type Python, **FastAPI** vous fournit de la validation de données. |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
## 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 : |
||||
|
|
||||
|
<img src="/img/tutorial/path-params/image01.png"> |
||||
|
|
||||
|
!!! info |
||||
|
À nouveau, en utilisant uniquement les déclarations de type Python, **FastAPI** vous fournit automatiquement une documentation interactive (via Swagger UI). |
||||
|
|
||||
|
On voit bien dans la documentation que `item_id` est déclaré comme entier. |
||||
|
|
||||
|
## Les avantages d'avoir une documentation basée sur une norme, et la documentation alternative. |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
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> : |
||||
|
|
||||
|
<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. |
||||
|
|
||||
|
## Pydantic |
||||
|
|
||||
|
Toute la validation de données est effectué en arrière-plan avec <a href="https://pydantic-docs.helpmanual.io/" 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. |
||||
|
|
||||
|
## L'ordre importe |
||||
|
|
||||
|
Quand vous créez des *fonctions de chemins*, vous pouvez vous retrouver dans une situation où vous avez un chemin fixe. |
||||
|
|
||||
|
Tel que `/users/me`, disons pour récupérer les données sur l'utilisateur actuel. |
||||
|
|
||||
|
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 |
||||
|
|
||||
|
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}` : |
||||
|
|
||||
|
```Python hl_lines="6 11" |
||||
|
{!../../../docs_src/path_params/tutorial003.py!} |
||||
|
``` |
||||
|
|
||||
|
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"`. |
||||
|
|
||||
|
## Valeurs prédéfinies |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
### Création d'un `Enum` |
||||
|
|
||||
|
Importez `Enum` et créez une sous-classe qui hérite de `str` et `Enum`. |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
Créez ensuite des attributs de classe avec des valeurs fixes, qui seront les valeurs autorisées pour cette énumération. |
||||
|
|
||||
|
```Python hl_lines="1 6-9" |
||||
|
{!../../../docs_src/path_params/tutorial005.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! info |
||||
|
<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. |
||||
|
|
||||
|
!!! 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. |
||||
|
|
||||
|
### Déclarer un paramètre de chemin |
||||
|
|
||||
|
Créez ensuite un *paramètre de chemin* avec une annotation de type désignant l'énumération créée précédemment (`ModelName`) : |
||||
|
|
||||
|
```Python hl_lines="16" |
||||
|
{!../../../docs_src/path_params/tutorial005.py!} |
||||
|
``` |
||||
|
|
||||
|
### Documentation |
||||
|
|
||||
|
Les valeurs disponibles pour le *paramètre de chemin* sont bien prédéfinies, la documentation les affiche correctement : |
||||
|
|
||||
|
<img src="/img/tutorial/path-params/image03.png"> |
||||
|
|
||||
|
### Manipuler les *énumérations* Python |
||||
|
|
||||
|
La valeur du *paramètre de chemin* sera un des "membres" de l'énumération. |
||||
|
|
||||
|
#### Comparer les *membres d'énumération* |
||||
|
|
||||
|
Vous pouvez comparer ce paramètre avec les membres de votre énumération `ModelName` : |
||||
|
|
||||
|
```Python hl_lines="17" |
||||
|
{!../../../docs_src/path_params/tutorial005.py!} |
||||
|
``` |
||||
|
|
||||
|
#### Récupérer la *valeur de l'énumération* |
||||
|
|
||||
|
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` : |
||||
|
|
||||
|
```Python hl_lines="20" |
||||
|
{!../../../docs_src/path_params/tutorial005.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! tip "Astuce" |
||||
|
Vous pouvez aussi accéder la valeur `"lenet"` avec `ModelName.lenet.value`. |
||||
|
|
||||
|
#### Retourner des *membres d'énumération* |
||||
|
|
||||
|
Vous pouvez retourner des *membres d'énumération* dans vos *fonctions de chemin*, même imbriquée dans un JSON (e.g. un `dict`). |
||||
|
|
||||
|
Ils seront convertis vers leurs valeurs correspondantes (chaînes de caractères ici) avant d'être transmis au client : |
||||
|
|
||||
|
```Python hl_lines="18 21 23" |
||||
|
{!../../../docs_src/path_params/tutorial005.py!} |
||||
|
``` |
||||
|
|
||||
|
Le client recevra une réponse JSON comme celle-ci : |
||||
|
|
||||
|
```JSON |
||||
|
{ |
||||
|
"model_name": "alexnet", |
||||
|
"message": "Deep Learning FTW!" |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Paramètres de chemin contenant des chemins |
||||
|
|
||||
|
Disons que vous avez une *fonction de chemin* liée au chemin `/files/{file_path}`. |
||||
|
|
||||
|
Mais que `file_path` lui-même doit contenir un *chemin*, comme `home/johndoe/myfile.txt` par exemple. |
||||
|
|
||||
|
Donc, l'URL pour ce fichier pourrait être : `/files/home/johndoe/myfile.txt`. |
||||
|
|
||||
|
### Support d'OpenAPI |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
Néanmoins, cela reste faisable dans **FastAPI**, via les 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*. |
||||
|
|
||||
|
### Convertisseur de *chemin* |
||||
|
|
||||
|
En utilisant une option de Starlette directement, 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*. |
||||
|
|
||||
|
Vous pouvez donc l'utilisez comme tel : |
||||
|
|
||||
|
```Python hl_lines="6" |
||||
|
{!../../../docs_src/path_params/tutorial004.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! tip "Astuce" |
||||
|
Vous pourriez avoir besoin que le paramètre contienne `/home/johndoe/myfile.txt`, avec un slash au début (`/`). |
||||
|
|
||||
|
Dans ce cas, l'URL serait : `/files//home/johndoe/myfile.txt`, avec un double slash (`//`) entre `files` et `home`. |
||||
|
|
||||
|
## Récapitulatif |
||||
|
|
||||
|
Avec **FastAPI**, en utilisant les déclarations de type rapides, intuitives et standards de Python, vous bénéficiez de : |
||||
|
|
||||
|
* 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. |
||||
|
|
||||
|
Et vous n'avez besoin de le déclarer qu'une fois. |
||||
|
|
||||
|
C'est probablement l'avantage visible principal de **FastAPI** comparé aux autres *frameworks* (outre les performances pures). |
Loading…
Reference in new issue