[FastAPI] Erste Schritte mit FastAPI, einem ASGI-Webframework von Python

FastAPI Pythons Web-Framework, ein Mikroframework wie Flask. Zu seinen Stärken zählen hohe Leistung, einfache Schreibweise, ein Design mit starkem Fokus auf Produktionsabläufe und moderne Funktionen.

FastAPI ist auf der Schulter von Starlette geschrieben, und die asynchrone Verarbeitung ist einfach zu handhaben. Insbesondere weist es die folgenden Merkmale auf.

Ehrlich gesagt ist es [Responder] sehr ähnlich (https://github.com/taoufik07/responder). (Weil die Zeit, als es herauskam, nahe ist und der Responder auch auf Starlette basiert) Die beiden unteren sind jedoch so konzipiert, dass sie mit der Fast-API viel einfacher zu verwenden sind. Aus den folgenden Perspektiven denke ich, dass die Fast API nur für den Produktionsbetrieb gedacht ist. (Ich persönlich denke, der Responder ist einfacher zu verwenden, wenn Sie frei schreiben möchten.)

--Dokumente sind höflich (Verknüpfung mit DB, Authentifizierung, https-Konvertierung usw. werden ebenfalls eingeführt)

Ich habe auch die Leistung mit einigen Python-Frameworks verglichen und festgestellt, dass die Fast-API anscheinend eine gute Leistung erbringt. (Referenz: Vergleich der Leistung des Python-Webframeworks (Django, Flask, Responder, FastAPI, japronto))

Zweck dieses Artikels

Ich denke, das offizielle Tutorial ist angemessen, wenn Sie die Fast API schätzen möchten. Es ist sehr leicht zu verstehen, da der Inhalt umfangreich ist. Auf der anderen Seite ist es jedoch ein wenig quantitativ schwer, sich darauf zu beziehen, nur um loszulegen. Daher möchte ich den Inhalt neu organisieren und einführen, damit die Fast API mit dem erforderlichen Minimum verwendet werden kann.

Darüber hinaus wurde dieser Artikel unter der folgenden Annahme verfasst.

Codebeispiele, die den hier vorgestellten Inhalten entsprechen, sind in hier zusammengefasst. Bitte verwenden Sie es, wenn Sie nur Swagger berühren möchten.

Inhaltsverzeichnis

intro install FastAPI Installieren Sie fastapi und seinen ASGI-Server uvicorn.

$ pip install fastapi uvicorn

intro code Lassen Sie uns eine API erstellen, die {{text ":" Hallo Welt! "}` In json zurückgibt, wenn GET.

intro.py


from fastapi import FastAPI

app = FastAPI()

@app.get('/') #Angabe der Methode und des Endpunkts
async def hello():
    return {"text": "hello world!"}

Ich denke, es ist dasjenige, das im Python-Mikroframework präzise geschrieben werden kann. run server Der Server wird wie folgt gestartet. (--Reload ist während der Entwicklung praktisch, da der Server jedes Mal aktualisiert wird, wenn die Datei geändert wird.) `Intro: app``` Teil ist` Dateiname: FastAPI () Instanzname ist. Bitte ersetzen Sie gegebenenfalls.

$ uvicorn intro:app --reload

Überprüfen Sie das automatisch generierte Dokument (Swagger UI)

Gehen Sie zu http://127.0.0.1:8000/docs. Dadurch wird die Swagger-Benutzeroberfläche geöffnet. Sie können die API hier klicken. f895d438c0b57a8272939ee4e3521af3.gif

Sie können die Anforderungs- und Antwortschemata auch mit der unten beschriebenen Methode überprüfen. Eine der großen Stärken von FastAPI ist, dass dieses Dokument automatisch generiert wird. Wenn Sie normal entwickeln, wird das Dokument ohne Erlaubnis generiert.

Bearbeitung von Anfragen

Die folgenden Elemente werden behandelt.

GET method

Pfadparameter und Abfrageparameter abrufen

Sie können Parameter abrufen, indem Sie den Parameternamen in das Argument einfügen. Einmal

Bitte verstehe. Auch die Reihenfolge der Argumente spielt keine Rolle. Abhängig davon, ob der Standardwert deklariert ist oder nicht, ändert sich dann die Verarbeitung, wenn der im Argument enthaltene Parameter zum Zeitpunkt von GET nicht enthalten ist.

Die Funktion von Fast API besteht darin, dem Argument wie folgt einen ** Typ-Hinweis ** von Python hinzuzufügen.

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

Auf diese Weise berücksichtigt die Fast-API beim Abrufen des Parameters den Python-Typ-Hinweis.

halten. Wenn Sie Swagger tatsächlich aktivieren, können Sie die Informationen zum Parametertyp wie unten gezeigt überprüfen. 5136078ab0a27e2f274d116438395bc2.png

validation Darüber hinaus können Sie mithilfe der folgenden Abfrage und des folgenden Pfads einige erweiterte Aufgaben ausführen. Die Abfrage bezieht sich auf den Abfrageparameter und der Pfad auf den Pfadparameter.

from fastapi import Query, Path

Verwenden Sie wie folgt. Sie können grundsätzlich dieselben Argumente für Abfrage und Pfad verwenden.

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

Sie können auch die Einschränkungen von Swagger überprüfen. Da Sie die API aufrufen können, versuchen Sie, den Wert auf verschiedene Arten zu ändern, und überprüfen Sie, ob die Validierung korrekt durchgeführt wurde. POST method

Anfragetext abrufen

Grundform

Erklärt, wie Postdaten empfangen werden. Bereiten Sie nach dem Erben von `` `pydantic.BaseModel``` zunächst eine separate Klasse mit Typhinweisen für Attribute vor und fügen Sie Typhinweise mit Argumenten als Typ des Anforderungshauptteils hinzu. Das ist gut.

from pydantic import BaseModel
from typing import Optional, List

class Data(BaseModel):
    """Klasse mit Typhinweisen für Anforderungsdaten"""
    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}"}

Hier setzt der obige Code voraus, dass der folgende JSON veröffentlicht wird.

requestBody


{
    "string": "string",
    "default_none": 0,
    "lists": [1, 2]
}

Wenn nicht genügend Felder vorhanden sind, wird der Statuscode 422 zurückgegeben. (Wenn es ein zusätzliches Feld gibt, scheint es normal zu funktionieren) Nachdem die Verarbeitung bis zu diesem Punkt durchgeführt wurde, kann die Datenstruktur des erwarteten Anforderungshauptteils über die Swagger-Benutzeroberfläche bestätigt werden. c21c87c01835cab42629eb3e88e30201.png

embed request body Etwas anders als im vorherigen Beispiel werde ich die Notation für die folgende Datenstruktur erläutern.

requestBody


{
    "data": {
        "string": "string",
        "default_none": 0,
        "lists": [1, 2]
    }
}

Verwenden Sie für eine solche Struktur dieselbe Datenklasse wie zuvor. Nur die Struktur kann mit `fastapi.Body``` geändert werden. fastapi.Body``` ist ein Begleiter von pydantic.Query```, das bei der Validierung der GET-Methode eingeführt wurde. Ebenso ist das erste Argument der Standardwert. Es wird ein Argument namens Embed verwendet, das in `pydantic.Query``` nicht gefunden wurde. Die Struktur kann mit den folgenden geringfügigen Änderungen geändert werden.

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 Als nächstes werde ich erklären, wie die Struktur verschachtelter Listen und Wörterbücher wie folgt behandelt wird. Die Struktur von subData besteht aus einem eingebetteten Anforderungshauptteil, aber ich werde eine andere Schreibweise einführen.

{
    "subData": {
        "strings": "string",
        "integer": 0
    },
    "subDataList": [
        {"strings": "string0", "integer": 0},
        {"strings": "string1", "integer": 1},
        {"strings": "string2", "integer": 2}
    ]
}

Verschachtelte Typdeklarationen sind häufig sehr grob mit Hinweisen zum Python-Typ. (Wenn Sie sich eine ungefähre Vorstellung machen möchten, reicht es aus, die folgende Unterdatenliste wie `List [Any]` oder List [Dict [str, Any] `einzugeben.) Andererseits kann FastAPI (oder pydantic) komplexe verschachtelte Strukturen verarbeiten. Sie können Unterklassen entlang der verschachtelten Struktur genau definieren und Typhinweise hinzufügen, wie unten gezeigt.

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 Was Sie mit der GET-Methode tun können und was Sie tun können, ist fast dasselbe. Der Unterschied besteht darin, dass `pydantic.Field``` anstelle von` fastapi.Query verwendet wird. Aber die Argumente sind die gleichen. Ich habe gerade `` `pydantic.Field für jede Klasse eingeführt, die im verschachtelten Anforderungshauptteil verwendet wird. Sie können es auch mit `` `fastapi.Query``` usw. verwenden, es wird jedoch das Argumentbeispiel verwendet. Die an dieses Argument übergebenen Daten sind der Standardwert, wenn die API von Swagger aufgerufen wird.

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}"}

Umgang mit der Antwort

Sie können auch eine Klasse wie die im Anforderungshauptteil für die Antwort definierte definieren und eine Validierung durchführen.

Grundform

Wenn Sie es standardmäßig an response_model übergeben

Wenn Sie hier wie folgt schreiben, wird `integer``` aus dem zurückgegebenen Wörterbuch entfernt und` aux``` wird ergänzt, um json zurückzugeben. (Es wird ein sehr einfaches Beispiel angegeben. Wenn es jedoch verschachtelt ist oder eine etwas komplizierte Validierung erforderlich ist, können Sie die Notation für Typhinweise verwenden, wie unter "Behandlung von Anforderungen" angegeben.)

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}

In dieser Phase können Sie das Schema der Antwortdaten von Swagger überprüfen. bb16c30d6110d5ec387b8e8edca89fc8.png

Abgeleitete Form

Es gibt verschiedene Optionen für die Verwendung von response_model.

#Antwort, wenn nicht im Wörterbuch_Der Standardwert der Attribute des Modells"Ich kann es nicht hineinstecken"
@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_des Modells"strings", "aux"Ignorieren-> "text"Nur zurück
@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_des Modells"text"Nur berücksichtigen-> "text"Nur zurück
@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}

Fehlerbehandlung und Statuscodeverwaltung

Es gibt drei Stufen der Statuscodeverwaltung.

from fastapi import HTTPException
from starlette.responses import Response
from starlette.status import HTTP_201_CREATED

@app.get('/status', status_code=200) #Standardstatuscode-Spezifikation
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 Sie können den Hintergrundprozess verwenden, um nur die Antwort zurückzugeben, bevor die schwere Verarbeitung abgeschlossen ist. Dieser Prozess ist für WSGI-Systeme (Django usw.) ziemlich schwierig. Starlette-basiertes ASGI macht diesen Prozess jedoch sehr präzise.

Das Verfahren ist

  1. Deklarieren Sie ein Argument vom Typ `` `fastapi.BackgroundTasks```
  2. Wirf eine Aufgabe mit `` `.add_task```

Es ist schwer vorherzusagen, was los ist, aber ich denke, die Beschreibung selbst ist einfach.

Versuchen Sie als Beispiel für eine umfangreiche Verarbeitung, einen Hintergrundprozess auszuführen, der für die empfangenen Pfadparameter Sekunden in den Ruhezustand versetzt und dann gedruckt wird.

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_Gibt eine Antwort zurück, ohne auf das Ende der Bombe zu warten

Die Ergebnisse werden in der folgenden Reihenfolge verarbeitet

  1. Datum der Antwortheader = 17:37:14
  2. Datumsausgabe zum Drucken = 17:37:25

Es scheint also, dass es im Hintergrund richtig verarbeitet wird. スクリーンショット 2020-01-03 2.39.19.png

unittest Der TestClient von Starlette ist ausgezeichnet und Sie können die API leicht für unittest drücken. Dieses Mal werde ich einen Unit-Test mit Pytest gemäß dem Tutorial durchführen.

install

$ pip install requests pytest

Verzeichnisplatzierung

├── intro.py
└── tests
    ├── __init__.py
    └── test_intro.py

Lassen Sie uns nun den folgenden Komponententest durchführen.

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 Das Verkaufsargument ist, dass Sie mit `starlette.testclient.TestClient einfach GET und POST drücken und `` assert``` der Antwort erhalten können, wie unten gezeigt.

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]",
    }

Führen Sie pytest aus

$ 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 Sie haben folgende Möglichkeiten: Es ist eine einfache Anwendung, daher denke ich nicht, dass es Probleme mit der Infrastruktur geben wird.

Wenn Sie Docker verwenden können, ist die letztere Methode grundsätzlich besser, und wenn dies nicht der Fall ist (z. B. das Erstellen einer schnellen API mit PaaS), ist die erstere Methode besser.

In Bezug auf die Besonderheiten gibt es keine für FastAPI spezifische Verarbeitung, und es handelt sich um ein Verfahren, das nicht mit anderen Mikroframeworks zusammenhängt. Daher werde ich es dieses Mal weglassen. Referenz:

Hier finden Sie eine Referenz für andere häufig verwendete Einstellungen und kontextsensitive Themen, die nicht als Lernprogramm geschrieben werden müssen. - CORS-Problem (Cross-Origin Resource Sharing) behoben: Dies ist ein Problem, das auftritt, wenn sich das Frontend auf einem anderen Server als das Backend befindet. Authentifizierung: Enthält Beispiele für die OAuth2- und HTTP-Basisauthentifizierung. Dokumente werden automatisch zur Authentifizierung generiert. Zusammenfassung Dies ist das Ende des Mindest-Lernprogramms. Sie sollten nun in der Lage sein, eine vollständige API-Server-> Bereitstellung zu entwickeln.

Zusätzlich zu den Inhalten, die in dieser Zeit behandelt wurden, reicht es meiner Meinung nach aus, nur auf die zugehörigen Kapitel zu verweisen, wenn Sie sich mit Datenbankverknüpfungen, HTML-Rendering, Websocket, GraphQL usw. befassen möchten.

Wie auch immer, es ist praktisch, dass Swagger automatisch generiert wird. Probieren Sie es also aus, während Sie Ihre Hände bewegen!

Obwohl es wenig mit dem Inhalt dieses Artikels zu tun hat, möchte ich schließlich die interessantesten Kapitel in der offiziellen Fast API-Dokumentation vorstellen. Der Entwicklungsprozess und die Punkte, die ihn von anderen Frameworks unterscheiden, werden erwähnt.

Refs

Ergänzung

Was das Front-Miso betrifft, habe ich zuvor versucht, eine Schemadefinition-> Prahlerei mit dem Responder zu generieren, aber die Menge der Beschreibung war völlig anders. (Da es keine Beschreibung von FastAPI nur für Swagger gibt) hier können Sie sehen, wie erstaunlich FastAPI ist. Ich glaube du kannst.

Recommended Posts

[FastAPI] Erste Schritte mit FastAPI, einem ASGI-Webframework von Python
Erste Schritte mit Python-Webanwendungen
Erste Schritte mit Python Web Scraping Practice
Erste Schritte mit Python Web Scraping Practice
1.1 Erste Schritte mit Python
Erste Schritte mit Python
Erste Schritte mit Python
Einführung in Tornado (1): Python Web Framework mit Tornado gestartet
Einführung in Python-Funktionen
Erste Schritte mit Python Django (4)
Erste Schritte mit Python Django (3)
Einführung in Python Django (6)
Erste Schritte mit Python Django (5)
Erste Schritte mit Python Responder v2
Erste Schritte mit Python für PHPer-Klassen
Erste Schritte mit Python Grundlagen von Python
Erste Schritte mit genetischen Python-Algorithmen
Erste Schritte mit Python 3.8 unter Windows
Erste Schritte mit Python für PHPer-Funktionen
Erste Schritte mit Python3 # 1 Grundkenntnisse erlernen
Erste Schritte mit Python für PHPer-Super Basics
Erste Schritte mit Dynamo von Python Boto
Erstellen Sie ein Webframework mit Python! (1)
Erstellen Sie ein Webframework mit Python! (2)
Erste Schritte mit Python mit 100 Klopfen bei der Sprachverarbeitung
Python Web Content mit Lolipop billigen Server gemacht
Erste Schritte mit AWS IoT in Python
Materialien zum Lesen, wenn Sie mit Python beginnen
Einstellungen für den Einstieg in MongoDB mit Python
Django 1.11 wurde mit Python3.6 gestartet
Erste Schritte mit Python3 # 2 Erfahren Sie mehr über Typen und Variablen
Django Python Web Framework
Erste Schritte mit apache2
Erste Schritte mit Django 1
Webanwendung erstellt mit Python3.4 + Django (Teil.1 Umgebungskonstruktion)
Einführung in die Optimierung
Erste Schritte mit Google App Engine für Python und PHP
Erste Schritte mit Numpy
Erste Schritte mit Spark
Erste Schritte mit Pydantic
Erste Schritte mit Jython
Erste Schritte mit Django 2
Leistungsvergleich des Python-Webframeworks (Django, Flask, Responder, FastAPI, Japronto)
Erste Schritte mit Python3 # 3 Versuchen Sie erweiterte Berechnungen mit der import-Anweisung
Erste Schritte mit Mathematik Beginnen mit Python Programming Challenge Persönliche Notizen-Problem 1-1
Übersetzen Erste Schritte mit TensorFlow
Einführung in Tkinter 2: Button
Web Scraping mit Python + JupyterLab
Erste Schritte mit Go Assembly
EXE Web API von Python
Ich habe einen Blackjack mit Python gemacht!
Ein Ei mit Python erstellen
Web-API mit Python + Falcon
Python wurde von C-Programmierern gestartet