FastAPI Le framework Web de Python, qui est un microframework comme Flask. Ses atouts comprennent des performances élevées, une facilité d'écriture, une conception fortement axée sur les opérations de production et des fonctions modernes.
FastAPI est écrit sur l'épaule de Starlette, et le traitement asynchrone est facile à gérer. En particulier, il présente les caractéristiques suivantes.
Franchement, c'est très similaire à responder. (Parce que l'heure à laquelle il est sorti est proche et que le répondeur est également basé sur Starlette) Cependant, les deux derniers sont conçus pour être beaucoup plus faciles à utiliser avec l'API Fast. Du point de vue suivant, je pense que l'API Fast est uniquement destinée aux opérations de production. (Personnellement, je pense que le répondeur est plus facile à utiliser si vous voulez écrire librement.)
J'ai également comparé les performances avec certains frameworks Python et j'ai constaté que l'API Fast semblait bien fonctionner. (Référence: Comparaison des performances du framework Web Python (Django, Flask, responder, FastAPI, japronto))
Je pense que le tutoriel officiel est approprié si vous voulez apprécier l'API Fast. C'est très facile à comprendre car le contenu est substantiel. Cependant, d'un autre côté, il est relativement difficile de s'y référer pour commencer. Par conséquent, je voudrais réorganiser et introduire le contenu afin que l'API Fast puisse être utilisée avec le minimum nécessaire.
En outre, cet article est rédigé en supposant ce qui suit.
--Comprendre la notation de base de certains microframework en python
Les exemples de code correspondant au contenu présenté ici sont résumés dans ici. Veuillez l'utiliser lorsque vous souhaitez toucher uniquement Swagger.
intro install FastAPI Installez fastapi et son serveur ASGI, uvicorn.
$ pip install fastapi uvicorn
intro code
Créons une API qui renvoie `{" text ":" hello world! "}`
`Dans json quand GET.
intro.py
from fastapi import FastAPI
app = FastAPI()
@app.get('/') #Spécification de la méthode et du point de terminaison
async def hello():
return {"text": "hello world!"}
Je pense que c'est celui qui peut être écrit de manière concise dans le microframework Python.
run server
Le serveur démarrera comme suit. (--Recharger est pratique pendant le développement car le serveur est mis à jour à chaque fois que le fichier est modifié.) Intro: app
partie est `nom de fichier: nom d'instance FastAPI ()
est. Veuillez remplacer selon le cas.
$ uvicorn intro:app --reload
Accédez à http://127.0.0.1:8000/docs. Cela ouvrira l'interface utilisateur de Swagger. Vous pouvez accéder à l'API ici.
Vous pourrez également vérifier les schémas de demande et de réponse en utilisant la méthode décrite ci-dessous. L'une des grandes forces de FastAPI est que ce document est généré automatiquement. Si vous développez normalement, le document sera généré sans autorisation.
Les éléments suivants sont traités.
GET method
Vous pouvez obtenir un paramètre simplement en mettant le nom du paramètre dans l'argument. Une fois que
--Le nom du paramètre déclaré comme
/ {param}
au point de terminaison est ** paramètre de chemin **
--Autre que cela, il pointe vers le ** paramètre de requête **
Essaye de comprendre. De plus, l'ordre des arguments n'a pas d'importance. Ensuite, selon que la valeur par défaut est déclarée ou non, le traitement lorsque le paramètre inclus dans l'argument n'est pas inclus au moment de GET change.
`{" detail ":" Not Found "}`
est retourné.Et, la fonctionnalité de Fast API est d'ajouter ** type hint ** de python comme argument comme suit.
@app.get('/get/{path}')
async def path_and_query_params(
path: str,
query: int,
default_none: Optional[str] = None):
return {"text": f"hello, {path}, {query} and {default_none}"}
En faisant cela, lors de l'obtention du paramètre, l'API Fast prendra en compte l'indication de type python,
`{" detail ":" Not Found "}`
si la conversion vers le type spécifié n'est pas possibletenir. Si vous vérifiez réellement Swagger, vous pouvez vérifier les informations de type de paramètre comme indiqué ci-dessous.
validation En plus de ce qui précède, vous pouvez effectuer des opérations avancées en utilisant la requête et le chemin suivants. La requête concerne le paramètre de requête et le chemin le paramètre de chemin.
from fastapi import Query, Path
Utilisez comme suit. Vous pouvez utiliser essentiellement les mêmes arguments pour la requête et le chemin,
--Spécifiez la valeur par défaut du premier argument. Passez
... '' si vous ne voulez pas de valeur par défaut (obligatoire)
--alias: spécifiez le nom du paramètre. Il est utilisé lorsque vous souhaitez séparer le nom de l'argument et le nom du paramètre. Quand il viole la convention de dénomination Python
--Autre: vous pouvez spécifier la longueur des caractères, l'expression régulière et la plage de valeurs pour limiter la valeur reçue.
@app.get('/validation/{path}')
async def validation(
string: str = Query(None, min_length=2, max_length=5, regex=r'[a-c]+.'),
integer: int = Query(..., gt=1, le=3), # required
alias_query: str = Query('default', alias='alias-query'),
path: int = Path(10)):
return {"string": string, "integer": integer, "alias-query": alias_query, "path": path}
Vous pouvez également vérifier les restrictions de Swagger. Puisque vous pouvez accéder à l'API, essayez de modifier les valeurs et vérifiez si la validation est effectuée correctement. POST method
Explique comment recevoir des données de publication. Tout d'abord, en gros, après avoir hérité de
pydantic.BaseModel```, préparez une classe séparée avec des indices de type pour les attributs, et ajoutez des indices de type avec des arguments comme type de corps de requête. C'est bon.
from pydantic import BaseModel
from typing import Optional, List
class Data(BaseModel):
"""Classe avec indications de type pour les données de demande"""
string: str
default_none: Optional[int] = None
lists: List[int]
@app.post('/post')
async def declare_request_body(data: Data):
return {"text": f"hello, {data.string}, {data.default_none}, {data.lists}"}
Ici, le code ci-dessus suppose que le json suivant sera publié.
requestBody
{
"string": "string",
"default_none": 0,
"lists": [1, 2]
}
S'il n'y a pas assez de champs, le code d'état 422 sera renvoyé. (S'il y a un champ supplémentaire, il semble fonctionner normalement) De plus, après avoir effectué le traitement jusqu'à ce point, la structure de données du corps de requête attendu peut être confirmée à partir de l'interface utilisateur Swagger.
embed request body Un peu différent de l'exemple précédent, je vais expliquer la notation de la structure de données suivante.
requestBody
{
"data": {
"string": "string",
"default_none": 0,
"lists": [1, 2]
}
}
Pour une telle structure, utilisez la même classe Data qu'avant. Seule la structure peut être modifiée en utilisant fastapi.Body ''.
fastapi.Body``` est un compagnon de
pydantic.Query '' introduit dans la validation de la méthode GET. De même, le premier argument est la valeur par défaut. Il utilise un argument appelé embed qui n'a pas été trouvé dans
pydantic.Query```. La structure peut être modifiée avec les modifications mineures suivantes.
from fastapi import Body
@app.post('/post/embed')
async def declare_embedded_request_body(data: Data = Body(..., embed=True)):
return {"text": f"hello, {data.string}, {data.default_none}, {data.lists}"}
nested request body Ensuite, je vais expliquer comment gérer la structure des listes imbriquées et des dictionnaires comme suit. La structure de subData est sous la forme d'un corps de requête incorporé, mais je vais introduire une manière différente de l'écrire.
{
"subData": {
"strings": "string",
"integer": 0
},
"subDataList": [
{"strings": "string0", "integer": 0},
{"strings": "string1", "integer": 1},
{"strings": "string2", "integer": 2}
]
}
Les déclarations de type imbriquées sont souvent très grossières avec des indices de type python. (Si vous voulez avoir une idée approximative, il suffit de taper la sous-liste de données suivante comme
List [Any] '' ou
List [Dict [str, Any]
``)
D'autre part, FastAPI (ou pydantic) peut gérer des structures imbriquées complexes.
Vous pouvez définir fidèlement les sous-classes le long de la structure imbriquée et ajouter des indices de type comme indiqué ci-dessous.
class subDict(BaseModel):
strings: str
integer: int
class NestedData(BaseModel):
subData: subDict
subDataList: List[subDict]
@app.post('/post/nested')
async def declare_nested_request_body(data: NestedData):
return {"text": f"hello, {data.subData}, {data.subDataList}"}
validation
Ce que vous pouvez faire et ce que vous pouvez faire avec la méthode GET est presque identique. La différence est qu'il utilise
pydantic.Field au lieu de `` fastapi.Query
etc. Mais les arguments sont les mêmes.
Je viens d'introduire pydantic.Field '' à chaque classe utilisée dans le corps de la requête imbriquée. Vous pouvez également l'utiliser avec
`` fastapi.Query``` etc., mais il utilise l'exemple d'argument. Les données passées à cet argument seront la valeur par défaut lors de la frappe de l'API de Swagger.
from pydantic import Field
class ValidatedSubData(BaseModel):
strings: str = Field(None, min_length=2, max_length=5, regex=r'[a-b]+.')
integer: int = Field(..., gt=1, le=3) # required
class ValidatedNestedData(BaseModel):
subData: ValidatedSubData = Field(..., example={"strings": "aaa", "integer": 2})
subDataList: List[ValidatedSubData] = Field(...)
@app.post('/validation')
async def validation(data: ValidatedNestedData):
return {"text": f"hello, {data.subData}, {data.subDataList}"}
Vous pouvez également définir une classe comme celle définie dans le corps de la requête pour la réponse et effectuer la validation.
Si vous le transmettez à response_model, par défaut
Ici, si vous écrivez comme suit, integer
est supprimé du dictionnaire de retour, et aux
est complété pour retourner json. (Un exemple très simple est donné, mais s'il est imbriqué ou qu'une validation un peu compliquée est requise, vous pouvez utiliser la notation pour les indices de type comme mentionné dans "Traitement de la demande" tel quel)
class ItemOut(BaseModel):
strings: str
aux: int = 1
text: str
@app.get('/', response_model=ItemOut)
async def response(strings: str, integer: int):
return {"text": "hello world!", "strings": strings, "integer": integer}
À ce stade, vous pouvez vérifier le schéma des données de réponse de Swagger.
Il existe plusieurs options pour utiliser response_model.
#Réponse si pas dans le dictionnaire_La valeur par défaut des attributs du modèle"Je ne peux pas le mettre"
@app.get('/unset', response_model=ItemOut, response_model_exclude_unset=True)
async def response_exclude_unset(strings: str, integer: int):
return {"text": "hello world!", "strings": strings, "integer": integer}
# response_du modèle"strings", "aux"Ignorer-> "text"Seulement retour
@app.get('/exclude', response_model=ItemOut, response_model_exclude={"strings", "aux"})
async def response_exclude(strings: str, integer: int):
return {"text": "hello world!", "strings": strings, "integer": integer}
# response_du modèle"text"Ne considérez que-> "text"Seulement retour
@app.get('/include', response_model=ItemOut, response_model_include={"text"})
async def response_include(strings: str, integer: int):
return {"text": "hello world!", "strings": strings, "integer": integer}
Il existe trois étapes de gestion des codes d'état.
--Déclarer le code d'état par défaut: déclarer avec le décorateur
fastapi.HTTPException``` en placefrom fastapi import HTTPException
from starlette.responses import Response
from starlette.status import HTTP_201_CREATED
@app.get('/status', status_code=200) #spécification du code d'état par défaut
async def response_status_code(integer: int, response: Response):
if integer > 5:
# error handling
raise HTTPException(status_code=404, detail="this is error messages")
elif integer == 1:
# set manually
response.status_code = HTTP_201_CREATED
return {"text": "hello world, created!"}
else:
# default status code
return {"text": "hello world!"}
background process Vous pouvez utiliser le processus d'arrière-plan pour renvoyer uniquement la réponse avant la fin du traitement intensif. Ce processus est assez difficile pour les systèmes WSGI (Django, etc.). Cependant, ASGI basé sur Starlette rend ce processus très concis.
La procédure est
fastapi.BackgroundTasks```Il est difficile de prédire ce qui se passe, mais je pense que la description en elle-même est facile.
Comme exemple de traitement intensif, essayez d'exécuter un processus d'arrière-plan qui se met en veille pendant les secondes du paramètre de chemin reçu, puis s'imprime.
from fastapi import BackgroundTasks
from time import sleep
from datetime import datetime
def time_bomb(count: int):
sleep(count)
print(f'bomb!!! {datetime.utcnow()}')
@app.get('/{count}')
async def back(count: int, background_tasks: BackgroundTasks):
background_tasks.add_task(time_bomb, count)
return {"text": "finish"} # time_Renvoie une réponse sans attendre la fin de la bombe
Les résultats sont traités dans l'ordre suivant
Il semble donc qu'il soit correctement traité en arrière-plan.
unittest TestClient de Starlette est excellent et vous pouvez facilement accéder à l'api pour unittest. Cette fois, je vais faire un test unitaire avec pytest selon le tutoriel.
install
$ pip install requests pytest
├── intro.py
└── tests
├── __init__.py
└── test_intro.py
Maintenant, faisons le test unitaire suivant.
intro.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional, List
app = FastAPI()
@app.get('/')
async def hello():
return {"text": "hello world!"}
class Data(BaseModel):
string: str
default_none: Optional[int] = None
lists: List[int]
@app.post('/post')
async def declare_request_body(data: Data):
return {"text": f"hello, {data.string}, {data.default_none}, {data.lists}"}
unittest
L'argument de vente est que vous pouvez facilement frapper GET et POST avec `starlette.testclient.TestClient
et obtenir
`ʻassert``` de la réponse comme indiqué ci-dessous.
test_intro.py
from starlette.testclient import TestClient
from intro import app
# get and assign app to create test client
client = TestClient(app)
def test_read_hello():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"text": "hello world!"}
def test_read_declare_request_body():
response = client.post(
"/post",
json={
"string": "foo",
"lists": [1, 2],
}
)
assert response.status_code == 200
assert response.json() == {
"text": "hello, foo, None, [1, 2]",
}
$ pytest
========================= test session starts =========================
platform darwin -- Python 3.6.8, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: ***/***/***
collected 1 items
tests/test_intro.py . [100%]
========================== 1 passed in 0.44s ==========================
deployment Vous avez les options suivantes: C'est une application simple, donc je ne pense pas qu'il y aura de problèmes avec l'infrastructure.
--Si vous pouvez installer et démarrer uvicorn, cela fonctionne comme local --Docker image (Officiel): Il semble que la performance a été réglée. Surtout, c'est officiel, il y a donc un sentiment de confiance.
Fondamentalement, si vous pouvez utiliser docker, la dernière méthode est meilleure, et si autre que cela (comme créer une API rapide avec PaaS), la première méthode est meilleure.
Concernant les spécificités, il n'y a pas de traitement spécifique à FastAPI, et c'est une procédure qui n'est pas liée à d'autres microframework, donc je vais l'omettre cette fois. référence:
Voici une référence pour d'autres paramètres fréquemment utilisés et des questions contextuelles qui n'ont pas besoin d'être écrites sous forme de didacticiel.
C'est la fin du tutoriel minimum. Vous devriez maintenant être en mesure de développer un déploiement complet de serveur> API.
En plus du contenu traité cette fois-ci, si vous souhaitez traiter de la liaison de base de données, du rendu html, du websocket, de GraphQL, etc., je pense qu'il suffit de se référer uniquement aux chapitres associés.
Quoi qu'il en soit, il est pratique que Swagger soit généré automatiquement, alors essayez-le en bougeant vos mains!
Enfin, bien que cela ait peu à voir avec le contenu de cet article, je voudrais présenter les chapitres les plus intéressants de la documentation officielle de l'API Fast. Le processus de développement et les points qui le différencient des autres cadres sont mentionnés.
Refs
En ce qui concerne le miso avant, j'ai essayé de générer une définition de schéma -> swagger avec le répondeur avant, mais la quantité de description était complètement différente. (Comme il n'y a pas de description de FastAPI uniquement pour Swagger) ici, vous pouvez voir à quel point FastAPI est incroyable. Je pense que tu peux.
Recommended Posts