committed by
GitHub
34 changed files with 4200 additions and 9 deletions
@ -0,0 +1,3 @@ |
|||
# Über |
|||
|
|||
Über FastAPI, sein Design, seine Inspiration und mehr. 🤓 |
@ -0,0 +1,69 @@ |
|||
# Zusätzliche Statuscodes |
|||
|
|||
Standardmäßig liefert **FastAPI** die Rückgabewerte (Responses) als `JSONResponse` zurück und fügt den Inhalt der jeweiligen *Pfadoperation* in das `JSONResponse` Objekt ein. |
|||
|
|||
Es wird der Default-Statuscode oder derjenige verwendet, den Sie in Ihrer *Pfadoperation* festgelegt haben. |
|||
|
|||
## Zusätzliche Statuscodes |
|||
|
|||
Wenn Sie neben dem Hauptstatuscode weitere Statuscodes zurückgeben möchten, können Sie dies tun, indem Sie direkt eine `Response` zurückgeben, wie etwa eine `JSONResponse`, und den zusätzlichen Statuscode direkt festlegen. |
|||
|
|||
Angenommen, Sie möchten eine *Pfadoperation* haben, die das Aktualisieren von Artikeln ermöglicht und bei Erfolg den HTTP-Statuscode 200 „OK“ zurückgibt. |
|||
|
|||
Sie möchten aber auch, dass sie neue Artikel akzeptiert. Und wenn die Elemente vorher nicht vorhanden waren, werden diese Elemente erstellt und der HTTP-Statuscode 201 „Created“ zurückgegeben. |
|||
|
|||
Um dies zu erreichen, importieren Sie `JSONResponse`, und geben Sie Ihren Inhalt direkt zurück, indem Sie den gewünschten `status_code` setzen: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="4 25" |
|||
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="4 25" |
|||
{!> ../../../docs_src/additional_status_codes/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="4 26" |
|||
{!> ../../../docs_src/additional_status_codes/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="2 23" |
|||
{!> ../../../docs_src/additional_status_codes/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="4 25" |
|||
{!> ../../../docs_src/additional_status_codes/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! warning "Achtung" |
|||
Wenn Sie eine `Response` direkt zurückgeben, wie im obigen Beispiel, wird sie direkt zurückgegeben. |
|||
|
|||
Sie wird nicht mit einem Modell usw. serialisiert. |
|||
|
|||
Stellen Sie sicher, dass sie die gewünschten Daten enthält und dass die Werte gültiges JSON sind (wenn Sie `JSONResponse` verwenden). |
|||
|
|||
!!! note "Technische Details" |
|||
Sie können auch `from starlette.responses import JSONResponse` verwenden. |
|||
|
|||
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette. Das Gleiche gilt für `status`. |
|||
|
|||
## OpenAPI- und API-Dokumentation |
|||
|
|||
Wenn Sie zusätzliche Statuscodes und Responses direkt zurückgeben, werden diese nicht in das OpenAPI-Schema (die API-Dokumentation) aufgenommen, da FastAPI keine Möglichkeit hat, im Voraus zu wissen, was Sie zurückgeben werden. |
|||
|
|||
Sie können das jedoch in Ihrem Code dokumentieren, indem Sie Folgendes verwenden: [Zusätzliche Responses](additional-responses.md){.internal-link target=_blank}. |
@ -0,0 +1,300 @@ |
|||
# Benutzerdefinierte Response – HTML, Stream, Datei, andere |
|||
|
|||
Standardmäßig gibt **FastAPI** die Responses mittels `JSONResponse` zurück. |
|||
|
|||
Sie können das überschreiben, indem Sie direkt eine `Response` zurückgeben, wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} gezeigt. |
|||
|
|||
Wenn Sie jedoch direkt eine `Response` zurückgeben, werden die Daten nicht automatisch konvertiert und die Dokumentation wird nicht automatisch generiert (zum Beispiel wird der spezifische „Medientyp“, der im HTTP-Header `Content-Type` angegeben ist, nicht Teil der generierten OpenAPI). |
|||
|
|||
Sie können aber auch die `Response`, die Sie verwenden möchten, im *Pfadoperation-Dekorator* deklarieren. |
|||
|
|||
Der Inhalt, den Sie von Ihrer *Pfadoperation-Funktion* zurückgeben, wird in diese `Response` eingefügt. |
|||
|
|||
Und wenn diese `Response` einen JSON-Medientyp (`application/json`) hat, wie es bei `JSONResponse` und `UJSONResponse` der Fall ist, werden die von Ihnen zurückgegebenen Daten automatisch mit jedem Pydantic `response_model` konvertiert (und gefiltert), das Sie im *Pfadoperation-Dekorator* deklariert haben. |
|||
|
|||
!!! note "Hinweis" |
|||
Wenn Sie eine Response-Klasse ohne Medientyp verwenden, erwartet FastAPI, dass Ihre Response keinen Inhalt hat, und dokumentiert daher das Format der Response nicht in deren generierter OpenAPI-Dokumentation. |
|||
|
|||
## `ORJSONResponse` verwenden |
|||
|
|||
Um beispielsweise noch etwas Leistung herauszuholen, können Sie <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> installieren und verwenden, und die Response als `ORJSONResponse` deklarieren. |
|||
|
|||
Importieren Sie die `Response`-Klasse (-Unterklasse), die Sie verwenden möchten, und deklarieren Sie sie im *Pfadoperation-Dekorator*. |
|||
|
|||
Bei umfangreichen Responses ist die direkte Rückgabe einer `Response` viel schneller als ein Dictionary zurückzugeben. |
|||
|
|||
Das liegt daran, dass FastAPI standardmäßig jedes enthaltene Element überprüft und sicherstellt, dass es als JSON serialisierbar ist, und zwar unter Verwendung desselben [JSON-kompatiblen Encoders](../tutorial/encoder.md){.internal-link target=_blank}, der im Tutorial erläutert wurde. Dadurch können Sie **beliebige Objekte** zurückgeben, zum Beispiel Datenbankmodelle. |
|||
|
|||
Wenn Sie jedoch sicher sind, dass der von Ihnen zurückgegebene Inhalt **mit JSON serialisierbar** ist, können Sie ihn direkt an die Response-Klasse übergeben und die zusätzliche Arbeit vermeiden, die FastAPI hätte, indem es Ihren zurückgegebenen Inhalt durch den `jsonable_encoder` leitet, bevor es ihn an die Response-Klasse übergibt. |
|||
|
|||
```Python hl_lines="2 7" |
|||
{!../../../docs_src/custom_response/tutorial001b.py!} |
|||
``` |
|||
|
|||
!!! info |
|||
Der Parameter `response_class` wird auch verwendet, um den „Medientyp“ der Response zu definieren. |
|||
|
|||
In diesem Fall wird der HTTP-Header `Content-Type` auf `application/json` gesetzt. |
|||
|
|||
Und er wird als solcher in OpenAPI dokumentiert. |
|||
|
|||
!!! tip "Tipp" |
|||
Die `ORJSONResponse` ist derzeit nur in FastAPI verfügbar, nicht in Starlette. |
|||
|
|||
## HTML-Response |
|||
|
|||
Um eine Response mit HTML direkt von **FastAPI** zurückzugeben, verwenden Sie `HTMLResponse`. |
|||
|
|||
* Importieren Sie `HTMLResponse`. |
|||
* Übergeben Sie `HTMLResponse` als den Parameter `response_class` Ihres *Pfadoperation-Dekorators*. |
|||
|
|||
```Python hl_lines="2 7" |
|||
{!../../../docs_src/custom_response/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! info |
|||
Der Parameter `response_class` wird auch verwendet, um den „Medientyp“ der Response zu definieren. |
|||
|
|||
In diesem Fall wird der HTTP-Header `Content-Type` auf `text/html` gesetzt. |
|||
|
|||
Und er wird als solcher in OpenAPI dokumentiert. |
|||
|
|||
### Eine `Response` zurückgeben |
|||
|
|||
Wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} gezeigt, können Sie die Response auch direkt in Ihrer *Pfadoperation* überschreiben, indem Sie diese zurückgeben. |
|||
|
|||
Das gleiche Beispiel von oben, das eine `HTMLResponse` zurückgibt, könnte so aussehen: |
|||
|
|||
```Python hl_lines="2 7 19" |
|||
{!../../../docs_src/custom_response/tutorial003.py!} |
|||
``` |
|||
|
|||
!!! warning "Achtung" |
|||
Eine `Response`, die direkt von Ihrer *Pfadoperation-Funktion* zurückgegeben wird, wird in OpenAPI nicht dokumentiert (zum Beispiel wird der `Content-Type` nicht dokumentiert) und ist in der automatischen interaktiven Dokumentation nicht sichtbar. |
|||
|
|||
!!! info |
|||
Natürlich stammen der eigentliche `Content-Type`-Header, der Statuscode, usw., aus dem `Response`-Objekt, das Sie zurückgegeben haben. |
|||
|
|||
### In OpenAPI dokumentieren und `Response` überschreiben |
|||
|
|||
Wenn Sie die Response innerhalb der Funktion überschreiben und gleichzeitig den „Medientyp“ in OpenAPI dokumentieren möchten, können Sie den `response_class`-Parameter verwenden UND ein `Response`-Objekt zurückgeben. |
|||
|
|||
Die `response_class` wird dann nur zur Dokumentation der OpenAPI-Pfadoperation* verwendet, Ihre `Response` wird jedoch unverändert verwendet. |
|||
|
|||
#### Eine `HTMLResponse` direkt zurückgeben |
|||
|
|||
Es könnte zum Beispiel so etwas sein: |
|||
|
|||
```Python hl_lines="7 21 23" |
|||
{!../../../docs_src/custom_response/tutorial004.py!} |
|||
``` |
|||
|
|||
In diesem Beispiel generiert die Funktion `generate_html_response()` bereits eine `Response` und gibt sie zurück, anstatt das HTML in einem `str` zurückzugeben. |
|||
|
|||
Indem Sie das Ergebnis des Aufrufs von `generate_html_response()` zurückgeben, geben Sie bereits eine `Response` zurück, die das Standardverhalten von **FastAPI** überschreibt. |
|||
|
|||
Aber da Sie die `HTMLResponse` auch in der `response_class` übergeben haben, weiß **FastAPI**, dass sie in OpenAPI und der interaktiven Dokumentation als HTML mit `text/html` zu dokumentieren ist: |
|||
|
|||
<img src="/img/tutorial/custom-response/image01.png"> |
|||
|
|||
## Verfügbare Responses |
|||
|
|||
Hier sind einige der verfügbaren Responses. |
|||
|
|||
Bedenken Sie, dass Sie `Response` verwenden können, um alles andere zurückzugeben, oder sogar eine benutzerdefinierte Unterklasse zu erstellen. |
|||
|
|||
!!! note "Technische Details" |
|||
Sie können auch `from starlette.responses import HTMLResponse` verwenden. |
|||
|
|||
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette. |
|||
|
|||
### `Response` |
|||
|
|||
Die Hauptklasse `Response`, alle anderen Responses erben von ihr. |
|||
|
|||
Sie können sie direkt zurückgeben. |
|||
|
|||
Sie akzeptiert die folgenden Parameter: |
|||
|
|||
* `content` – Ein `str` oder `bytes`. |
|||
* `status_code` – Ein `int`-HTTP-Statuscode. |
|||
* `headers` – Ein `dict` von Strings. |
|||
* `media_type` – Ein `str`, der den Medientyp angibt. Z. B. `"text/html"`. |
|||
|
|||
FastAPI (eigentlich Starlette) fügt automatisch einen Content-Length-Header ein. Außerdem wird es einen Content-Type-Header einfügen, der auf dem media_type basiert, und für Texttypen einen Zeichensatz (charset) anfügen. |
|||
|
|||
```Python hl_lines="1 18" |
|||
{!../../../docs_src/response_directly/tutorial002.py!} |
|||
``` |
|||
|
|||
### `HTMLResponse` |
|||
|
|||
Nimmt Text oder Bytes entgegen und gibt eine HTML-Response zurück, wie Sie oben gelesen haben. |
|||
|
|||
### `PlainTextResponse` |
|||
|
|||
Nimmt Text oder Bytes entgegen und gibt eine Plain-Text-Response zurück. |
|||
|
|||
```Python hl_lines="2 7 9" |
|||
{!../../../docs_src/custom_response/tutorial005.py!} |
|||
``` |
|||
|
|||
### `JSONResponse` |
|||
|
|||
Nimmt einige Daten entgegen und gibt eine `application/json`-codierte Response zurück. |
|||
|
|||
Dies ist die Standard-Response, die in **FastAPI** verwendet wird, wie Sie oben gelesen haben. |
|||
|
|||
### `ORJSONResponse` |
|||
|
|||
Eine schnelle alternative JSON-Response mit <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, wie Sie oben gelesen haben. |
|||
|
|||
### `UJSONResponse` |
|||
|
|||
Eine alternative JSON-Response mit <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>. |
|||
|
|||
!!! warning "Achtung" |
|||
`ujson` ist bei der Behandlung einiger Sonderfälle weniger sorgfältig als Pythons eingebaute Implementierung. |
|||
|
|||
```Python hl_lines="2 7" |
|||
{!../../../docs_src/custom_response/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! tip "Tipp" |
|||
Möglicherweise ist `ORJSONResponse` eine schnellere Alternative. |
|||
|
|||
### `RedirectResponse` |
|||
|
|||
Gibt eine HTTP-Weiterleitung (HTTP-Redirect) zurück. Verwendet standardmäßig den Statuscode 307 – Temporäre Weiterleitung (Temporary Redirect). |
|||
|
|||
Sie können eine `RedirectResponse` direkt zurückgeben: |
|||
|
|||
```Python hl_lines="2 9" |
|||
{!../../../docs_src/custom_response/tutorial006.py!} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
Oder Sie können sie im Parameter `response_class` verwenden: |
|||
|
|||
|
|||
```Python hl_lines="2 7 9" |
|||
{!../../../docs_src/custom_response/tutorial006b.py!} |
|||
``` |
|||
|
|||
Wenn Sie das tun, können Sie die URL direkt von Ihrer *Pfadoperation*-Funktion zurückgeben. |
|||
|
|||
In diesem Fall ist der verwendete `status_code` der Standardcode für die `RedirectResponse`, also `307`. |
|||
|
|||
--- |
|||
|
|||
Sie können den Parameter `status_code` auch in Kombination mit dem Parameter `response_class` verwenden: |
|||
|
|||
```Python hl_lines="2 7 9" |
|||
{!../../../docs_src/custom_response/tutorial006c.py!} |
|||
``` |
|||
|
|||
### `StreamingResponse` |
|||
|
|||
Nimmt einen asynchronen Generator oder einen normalen Generator/Iterator und streamt den Responsebody. |
|||
|
|||
```Python hl_lines="2 14" |
|||
{!../../../docs_src/custom_response/tutorial007.py!} |
|||
``` |
|||
|
|||
#### Verwendung von `StreamingResponse` mit dateiähnlichen Objekten |
|||
|
|||
Wenn Sie ein dateiähnliches (file-like) Objekt haben (z. B. das von `open()` zurückgegebene Objekt), können Sie eine Generatorfunktion erstellen, um über dieses dateiähnliche Objekt zu iterieren. |
|||
|
|||
Auf diese Weise müssen Sie nicht alles zuerst in den Arbeitsspeicher lesen und können diese Generatorfunktion an `StreamingResponse` übergeben und zurückgeben. |
|||
|
|||
Das umfasst viele Bibliotheken zur Interaktion mit Cloud-Speicher, Videoverarbeitung und anderen. |
|||
|
|||
```{ .python .annotate hl_lines="2 10-12 14" } |
|||
{!../../../docs_src/custom_response/tutorial008.py!} |
|||
``` |
|||
|
|||
1. Das ist die Generatorfunktion. Es handelt sich um eine „Generatorfunktion“, da sie `yield`-Anweisungen enthält. |
|||
2. Durch die Verwendung eines `with`-Blocks stellen wir sicher, dass das dateiähnliche Objekt geschlossen wird, nachdem die Generatorfunktion fertig ist. Also, nachdem sie mit dem Senden der Response fertig ist. |
|||
3. Dieses `yield from` weist die Funktion an, über das Ding namens `file_like` zu iterieren. Und dann für jeden iterierten Teil, diesen Teil so zurückzugeben, als wenn er aus dieser Generatorfunktion (`iterfile`) stammen würde. |
|||
|
|||
Es handelt sich also hier um eine Generatorfunktion, die die „generierende“ Arbeit intern auf etwas anderes überträgt. |
|||
|
|||
Auf diese Weise können wir das Ganze in einen `with`-Block einfügen und so sicherstellen, dass das dateiartige Objekt nach Abschluss geschlossen wird. |
|||
|
|||
!!! tip "Tipp" |
|||
Beachten Sie, dass wir, da wir Standard-`open()` verwenden, welches `async` und `await` nicht unterstützt, hier die Pfadoperation mit normalen `def` deklarieren. |
|||
|
|||
### `FileResponse` |
|||
|
|||
Streamt eine Datei asynchron als Response. |
|||
|
|||
Nimmt zur Instanziierung einen anderen Satz von Argumenten entgegen als die anderen Response-Typen: |
|||
|
|||
* `path` – Der Dateipfad zur Datei, die gestreamt werden soll. |
|||
* `headers` – Alle benutzerdefinierten Header, die inkludiert werden sollen, als Dictionary. |
|||
* `media_type` – Ein String, der den Medientyp angibt. Wenn nicht gesetzt, wird der Dateiname oder Pfad verwendet, um auf einen Medientyp zu schließen. |
|||
* `filename` – Wenn gesetzt, wird das in der `Content-Disposition` der Response eingefügt. |
|||
|
|||
Datei-Responses enthalten die entsprechenden `Content-Length`-, `Last-Modified`- und `ETag`-Header. |
|||
|
|||
```Python hl_lines="2 10" |
|||
{!../../../docs_src/custom_response/tutorial009.py!} |
|||
``` |
|||
|
|||
Sie können auch den Parameter `response_class` verwenden: |
|||
|
|||
```Python hl_lines="2 8 10" |
|||
{!../../../docs_src/custom_response/tutorial009b.py!} |
|||
``` |
|||
|
|||
In diesem Fall können Sie den Dateipfad direkt von Ihrer *Pfadoperation*-Funktion zurückgeben. |
|||
|
|||
## Benutzerdefinierte Response-Klasse |
|||
|
|||
Sie können Ihre eigene benutzerdefinierte Response-Klasse erstellen, die von `Response` erbt und diese verwendet. |
|||
|
|||
Nehmen wir zum Beispiel an, dass Sie <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> verwenden möchten, aber mit einigen benutzerdefinierten Einstellungen, die in der enthaltenen `ORJSONResponse`-Klasse nicht verwendet werden. |
|||
|
|||
Sie möchten etwa, dass Ihre Response eingerücktes und formatiertes JSON zurückgibt. Dafür möchten Sie die orjson-Option `orjson.OPT_INDENT_2` verwenden. |
|||
|
|||
Sie könnten eine `CustomORJSONResponse` erstellen. Das Wichtigste, was Sie tun müssen, ist, eine `Response.render(content)`-Methode zu erstellen, die den Inhalt als `bytes` zurückgibt: |
|||
|
|||
```Python hl_lines="9-14 17" |
|||
{!../../../docs_src/custom_response/tutorial009c.py!} |
|||
``` |
|||
|
|||
Statt: |
|||
|
|||
```json |
|||
{"message": "Hello World"} |
|||
``` |
|||
|
|||
... wird die Response jetzt Folgendes zurückgeben: |
|||
|
|||
```json |
|||
{ |
|||
"message": "Hello World" |
|||
} |
|||
``` |
|||
|
|||
Natürlich werden Sie wahrscheinlich viel bessere Möglichkeiten finden, Vorteil daraus zu ziehen, als JSON zu formatieren. 😉 |
|||
|
|||
## Standard-Response-Klasse |
|||
|
|||
Beim Erstellen einer **FastAPI**-Klasseninstanz oder eines `APIRouter`s können Sie angeben, welche Response-Klasse standardmäßig verwendet werden soll. |
|||
|
|||
Der Parameter, der das definiert, ist `default_response_class`. |
|||
|
|||
Im folgenden Beispiel verwendet **FastAPI** standardmäßig `ORJSONResponse` in allen *Pfadoperationen*, anstelle von `JSONResponse`. |
|||
|
|||
```Python hl_lines="2 4" |
|||
{!../../../docs_src/custom_response/tutorial010.py!} |
|||
``` |
|||
|
|||
!!! tip "Tipp" |
|||
Sie können dennoch weiterhin `response_class` in *Pfadoperationen* überschreiben, wie bisher. |
|||
|
|||
## Zusätzliche Dokumentation |
|||
|
|||
Sie können auch den Medientyp und viele andere Details in OpenAPI mit `responses` deklarieren: [Zusätzliche Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}. |
@ -0,0 +1,286 @@ |
|||
# Clients generieren |
|||
|
|||
Da **FastAPI** auf der OpenAPI-Spezifikation basiert, erhalten Sie automatische Kompatibilität mit vielen Tools, einschließlich der automatischen API-Dokumentation (bereitgestellt von Swagger UI). |
|||
|
|||
Ein besonderer Vorteil, der nicht unbedingt offensichtlich ist, besteht darin, dass Sie für Ihre API **Clients generieren** können (manchmal auch <abbr title="Software Development Kits">**SDKs**</abbr> genannt), für viele verschiedene **Programmiersprachen**. |
|||
|
|||
## OpenAPI-Client-Generatoren |
|||
|
|||
Es gibt viele Tools zum Generieren von Clients aus **OpenAPI**. |
|||
|
|||
Ein gängiges Tool ist <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>. |
|||
|
|||
Wenn Sie ein **Frontend** erstellen, ist <a href="https://github.com/ferdikoomen/openapi-typescript-codegen" class="external-link" target="_blank">openapi-typescript-codegen</a> eine sehr interessante Alternative. |
|||
|
|||
## Client- und SDK-Generatoren – Sponsor |
|||
|
|||
Es gibt auch einige **vom Unternehmen entwickelte** Client- und SDK-Generatoren, die auf OpenAPI (FastAPI) basieren. In einigen Fällen können diese Ihnen **weitere Funktionalität** zusätzlich zu qualitativ hochwertigen generierten SDKs/Clients bieten. |
|||
|
|||
Einige von diesen ✨ [**sponsern FastAPI**](../help-fastapi.md#den-autor-sponsern){.internal-link target=_blank} ✨, das gewährleistet die kontinuierliche und gesunde **Entwicklung** von FastAPI und seinem **Ökosystem**. |
|||
|
|||
Und es zeigt deren wahres Engagement für FastAPI und seine **Community** (Sie), da diese Ihnen nicht nur einen **guten Service** bieten möchten, sondern auch sicherstellen möchten, dass Sie über ein **gutes und gesundes Framework** verfügen, FastAPI. 🙇 |
|||
|
|||
Beispielsweise könnten Sie <a href="https://speakeasyapi.dev/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a> ausprobieren. |
|||
|
|||
Es gibt auch mehrere andere Unternehmen, welche ähnliche Dienste anbieten und die Sie online suchen und finden können. 🤓 |
|||
|
|||
## Einen TypeScript-Frontend-Client generieren |
|||
|
|||
Beginnen wir mit einer einfachen FastAPI-Anwendung: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="7-9 12-13 16-17 21" |
|||
{!> ../../../docs_src/generate_clients/tutorial001_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9-11 14-15 18 19 23" |
|||
{!> ../../../docs_src/generate_clients/tutorial001.py!} |
|||
``` |
|||
|
|||
Beachten Sie, dass die *Pfadoperationen* die Modelle definieren, welche diese für die Request- und Response-<abbr title="Die eigentlichen Nutzdaten, abzüglich der Metadaten">Payload</abbr> verwenden, indem sie die Modelle `Item` und `ResponseMessage` verwenden. |
|||
|
|||
### API-Dokumentation |
|||
|
|||
Wenn Sie zur API-Dokumentation gehen, werden Sie sehen, dass diese die **Schemas** für die Daten enthält, welche in Requests gesendet und in Responses empfangen werden: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image01.png"> |
|||
|
|||
Sie können diese Schemas sehen, da sie mit den Modellen in der Anwendung deklariert wurden. |
|||
|
|||
Diese Informationen sind im **OpenAPI-Schema** der Anwendung verfügbar und werden dann in der API-Dokumentation angezeigt (von Swagger UI). |
|||
|
|||
Und dieselben Informationen aus den Modellen, die in OpenAPI enthalten sind, können zum **Generieren des Client-Codes** verwendet werden. |
|||
|
|||
### Einen TypeScript-Client generieren |
|||
|
|||
Nachdem wir nun die Anwendung mit den Modellen haben, können wir den Client-Code für das Frontend generieren. |
|||
|
|||
#### `openapi-typescript-codegen` installieren |
|||
|
|||
Sie können `openapi-typescript-codegen` in Ihrem Frontend-Code installieren mit: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ npm install openapi-typescript-codegen --save-dev |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
#### Client-Code generieren |
|||
|
|||
Um den Client-Code zu generieren, können Sie das Kommandozeilentool `openapi` verwenden, das soeben installiert wurde. |
|||
|
|||
Da es im lokalen Projekt installiert ist, könnten Sie diesen Befehl wahrscheinlich nicht direkt aufrufen, sondern würden ihn in Ihre Datei `package.json` einfügen. |
|||
|
|||
Diese könnte so aussehen: |
|||
|
|||
```JSON hl_lines="7" |
|||
{ |
|||
"name": "frontend-app", |
|||
"version": "1.0.0", |
|||
"description": "", |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes" |
|||
}, |
|||
"author": "", |
|||
"license": "", |
|||
"devDependencies": { |
|||
"openapi-typescript-codegen": "^0.20.1", |
|||
"typescript": "^4.6.2" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Nachdem Sie das NPM-Skript `generate-client` dort stehen haben, können Sie es ausführen mit: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ npm run generate-client |
|||
|
|||
[email protected] generate-client /home/user/code/frontend-app |
|||
> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Dieser Befehl generiert Code in `./src/client` und verwendet intern `axios` (die Frontend-HTTP-Bibliothek). |
|||
|
|||
### Den Client-Code ausprobieren |
|||
|
|||
Jetzt können Sie den Client-Code importieren und verwenden. Er könnte wie folgt aussehen, beachten Sie, dass Sie automatische Codevervollständigung für die Methoden erhalten: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image02.png"> |
|||
|
|||
Sie erhalten außerdem automatische Vervollständigung für die zu sendende Payload: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image03.png"> |
|||
|
|||
!!! tip "Tipp" |
|||
Beachten Sie die automatische Vervollständigung für `name` und `price`, welche in der FastAPI-Anwendung im `Item`-Modell definiert wurden. |
|||
|
|||
Sie erhalten Inline-Fehlerberichte für die von Ihnen gesendeten Daten: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image04.png"> |
|||
|
|||
Das Response-Objekt hat auch automatische Vervollständigung: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image05.png"> |
|||
|
|||
## FastAPI-Anwendung mit Tags |
|||
|
|||
In vielen Fällen wird Ihre FastAPI-Anwendung größer sein und Sie werden wahrscheinlich Tags verwenden, um verschiedene Gruppen von *Pfadoperationen* zu separieren. |
|||
|
|||
Beispielsweise könnten Sie einen Abschnitt für **Items (Artikel)** und einen weiteren Abschnitt für **Users (Benutzer)** haben, und diese könnten durch Tags getrennt sein: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="21 26 34" |
|||
{!> ../../../docs_src/generate_clients/tutorial002_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="23 28 36" |
|||
{!> ../../../docs_src/generate_clients/tutorial002.py!} |
|||
``` |
|||
|
|||
### Einen TypeScript-Client mit Tags generieren |
|||
|
|||
Wenn Sie unter Verwendung von Tags einen Client für eine FastAPI-Anwendung generieren, wird normalerweise auch der Client-Code anhand der Tags getrennt. |
|||
|
|||
Auf diese Weise können Sie die Dinge für den Client-Code richtig ordnen und gruppieren: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image06.png"> |
|||
|
|||
In diesem Fall haben Sie: |
|||
|
|||
* `ItemsService` |
|||
* `UsersService` |
|||
|
|||
### Client-Methodennamen |
|||
|
|||
Im Moment sehen die generierten Methodennamen wie `createItemItemsPost` nicht sehr sauber aus: |
|||
|
|||
```TypeScript |
|||
ItemsService.createItemItemsPost({name: "Plumbus", price: 5}) |
|||
``` |
|||
|
|||
... das liegt daran, dass der Client-Generator für jede *Pfadoperation* die OpenAPI-interne **Operation-ID** verwendet. |
|||
|
|||
OpenAPI erfordert, dass jede Operation-ID innerhalb aller *Pfadoperationen* eindeutig ist. Daher verwendet FastAPI den **Funktionsnamen**, den **Pfad** und die **HTTP-Methode/-Operation**, um diese Operation-ID zu generieren. Denn so kann sichergestellt werden, dass die Operation-IDs eindeutig sind. |
|||
|
|||
Aber ich zeige Ihnen als nächstes, wie Sie das verbessern können. 🤓 |
|||
|
|||
## Benutzerdefinierte Operation-IDs und bessere Methodennamen |
|||
|
|||
Sie können die Art und Weise, wie diese Operation-IDs **generiert** werden, **ändern**, um sie einfacher zu machen und **einfachere Methodennamen** in den Clients zu haben. |
|||
|
|||
In diesem Fall müssen Sie auf andere Weise sicherstellen, dass jede Operation-ID **eindeutig** ist. |
|||
|
|||
Sie könnten beispielsweise sicherstellen, dass jede *Pfadoperation* einen Tag hat, und dann die Operation-ID basierend auf dem **Tag** und dem **Namen** der *Pfadoperation* (dem Funktionsnamen) generieren. |
|||
|
|||
### Funktion zum Generieren einer eindeutigen ID erstellen |
|||
|
|||
FastAPI verwendet eine **eindeutige ID** für jede *Pfadoperation*, diese wird für die **Operation-ID** und auch für die Namen aller benötigten benutzerdefinierten Modelle für Requests oder Responses verwendet. |
|||
|
|||
Sie können diese Funktion anpassen. Sie nimmt eine `APIRoute` und gibt einen String zurück. |
|||
|
|||
Hier verwendet sie beispielsweise den ersten Tag (Sie werden wahrscheinlich nur einen Tag haben) und den Namen der *Pfadoperation* (den Funktionsnamen). |
|||
|
|||
Anschließend können Sie diese benutzerdefinierte Funktion als Parameter `generate_unique_id_function` an **FastAPI** übergeben: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="6-7 10" |
|||
{!> ../../../docs_src/generate_clients/tutorial003_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="8-9 12" |
|||
{!> ../../../docs_src/generate_clients/tutorial003.py!} |
|||
``` |
|||
|
|||
### Einen TypeScript-Client mit benutzerdefinierten Operation-IDs generieren |
|||
|
|||
Wenn Sie nun den Client erneut generieren, werden Sie feststellen, dass er über die verbesserten Methodennamen verfügt: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image07.png"> |
|||
|
|||
Wie Sie sehen, haben die Methodennamen jetzt den Tag und dann den Funktionsnamen, aber keine Informationen aus dem URL-Pfad und der HTTP-Operation. |
|||
|
|||
### Vorab-Modifikation der OpenAPI-Spezifikation für den Client-Generator |
|||
|
|||
Der generierte Code enthält immer noch etwas **verdoppelte Information**. |
|||
|
|||
Wir wissen bereits, dass diese Methode mit den **Items** zusammenhängt, da sich dieses Wort in `ItemsService` befindet (vom Tag übernommen), aber wir haben auch immer noch den Tagnamen im Methodennamen vorangestellt. 😕 |
|||
|
|||
Wir werden das wahrscheinlich weiterhin für OpenAPI im Allgemeinen beibehalten wollen, da dadurch sichergestellt wird, dass die Operation-IDs **eindeutig** sind. |
|||
|
|||
Aber für den generierten Client könnten wir die OpenAPI-Operation-IDs direkt vor der Generierung der Clients **modifizieren**, um diese Methodennamen schöner und **sauberer** zu machen. |
|||
|
|||
Wir könnten das OpenAPI-JSON in eine Datei `openapi.json` herunterladen und dann mit einem Skript wie dem folgenden **den vorangestellten Tag entfernen**: |
|||
|
|||
=== "Python" |
|||
|
|||
```Python |
|||
{!> ../../../docs_src/generate_clients/tutorial004.py!} |
|||
``` |
|||
|
|||
=== "Node.js" |
|||
|
|||
```Javascript |
|||
{!> ../../../docs_src/generate_clients/tutorial004.js!} |
|||
``` |
|||
|
|||
Damit würden die Operation-IDs von Dingen wie `items-get_items` in `get_items` umbenannt, sodass der Client-Generator einfachere Methodennamen generieren kann. |
|||
|
|||
### Einen TypeScript-Client mit der modifizierten OpenAPI generieren |
|||
|
|||
Da das Endergebnis nun in einer Datei `openapi.json` vorliegt, würden Sie die `package.json` ändern, um diese lokale Datei zu verwenden, zum Beispiel: |
|||
|
|||
```JSON hl_lines="7" |
|||
{ |
|||
"name": "frontend-app", |
|||
"version": "1.0.0", |
|||
"description": "", |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"generate-client": "openapi --input ./openapi.json --output ./src/client --client axios --useOptions --useUnionTypes" |
|||
}, |
|||
"author": "", |
|||
"license": "", |
|||
"devDependencies": { |
|||
"openapi-typescript-codegen": "^0.20.1", |
|||
"typescript": "^4.6.2" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Nach der Generierung des neuen Clients hätten Sie nun **saubere Methodennamen** mit allen **Autovervollständigungen**, **Inline-Fehlerberichten**, usw.: |
|||
|
|||
<img src="/img/tutorial/generate-clients/image08.png"> |
|||
|
|||
## Vorteile |
|||
|
|||
Wenn Sie die automatisch generierten Clients verwenden, erhalten Sie **automatische Codevervollständigung** für: |
|||
|
|||
* Methoden. |
|||
* Request-Payloads im Body, Query-Parameter, usw. |
|||
* Response-Payloads. |
|||
|
|||
Außerdem erhalten Sie für alles **Inline-Fehlerberichte**. |
|||
|
|||
Und wann immer Sie den Backend-Code aktualisieren und das Frontend **neu generieren**, stehen alle neuen *Pfadoperationen* als Methoden zur Verfügung, die alten werden entfernt und alle anderen Änderungen werden im generierten Code reflektiert. 🤓 |
|||
|
|||
Das bedeutet auch, dass, wenn sich etwas ändert, dies automatisch im Client-Code **reflektiert** wird. Und wenn Sie den Client **erstellen**, kommt es zu einer Fehlermeldung, wenn die verwendeten Daten **nicht übereinstimmen**. |
|||
|
|||
Sie würden also sehr früh im Entwicklungszyklus **viele Fehler erkennen**, anstatt darauf warten zu müssen, dass die Fehler Ihren Endbenutzern in der Produktion angezeigt werden, und dann zu versuchen, zu debuggen, wo das Problem liegt. ✨ |
@ -0,0 +1,51 @@ |
|||
# OpenAPI-Webhooks |
|||
|
|||
Es gibt Fälle, in denen Sie Ihren API-Benutzern mitteilen möchten, dass Ihre Anwendung mit einigen Daten *deren* Anwendung aufrufen (ein Request senden) könnte, normalerweise um über ein bestimmtes **Event** zu **benachrichtigen**. |
|||
|
|||
Das bedeutet, dass anstelle des normalen Prozesses, bei dem Benutzer Requests an Ihre API senden, **Ihre API** (oder Ihre Anwendung) **Requests an deren System** (an deren API, deren Anwendung) senden könnte. |
|||
|
|||
Das wird normalerweise als **Webhook** bezeichnet. |
|||
|
|||
## Webhooks-Schritte |
|||
|
|||
Der Prozess besteht normalerweise darin, dass **Sie in Ihrem Code definieren**, welche Nachricht Sie senden möchten, den **Body des Requests**. |
|||
|
|||
Sie definieren auch auf irgendeine Weise, zu welchen **Momenten** Ihre Anwendung diese Requests oder Events sendet. |
|||
|
|||
Und **Ihre Benutzer** definieren auf irgendeine Weise (zum Beispiel irgendwo in einem Web-Dashboard) die **URL**, an die Ihre Anwendung diese Requests senden soll. |
|||
|
|||
Die gesamte **Logik** zur Registrierung der URLs für Webhooks und der Code zum tatsächlichen Senden dieser Requests liegt bei Ihnen. Sie schreiben es so, wie Sie möchten, in **Ihrem eigenen Code**. |
|||
|
|||
## Webhooks mit **FastAPI** und OpenAPI dokumentieren |
|||
|
|||
Mit **FastAPI** können Sie mithilfe von OpenAPI die Namen dieser Webhooks, die Arten von HTTP-Operationen, die Ihre Anwendung senden kann (z. B. `POST`, `PUT`, usw.) und die Request**bodys** definieren, die Ihre Anwendung senden würde. |
|||
|
|||
Dies kann es Ihren Benutzern viel einfacher machen, **deren APIs zu implementieren**, um Ihre **Webhook**-Requests zu empfangen. Möglicherweise können diese sogar einen Teil des eigenem API-Codes automatisch generieren. |
|||
|
|||
!!! info |
|||
Webhooks sind in OpenAPI 3.1.0 und höher verfügbar und werden von FastAPI `0.99.0` und höher unterstützt. |
|||
|
|||
## Eine Anwendung mit Webhooks |
|||
|
|||
Wenn Sie eine **FastAPI**-Anwendung erstellen, gibt es ein `webhooks`-Attribut, mit dem Sie *Webhooks* definieren können, genauso wie Sie *Pfadoperationen* definieren würden, zum Beispiel mit `@app.webhooks.post()`. |
|||
|
|||
```Python hl_lines="9-13 36-53" |
|||
{!../../../docs_src/openapi_webhooks/tutorial001.py!} |
|||
``` |
|||
|
|||
Die von Ihnen definierten Webhooks landen im **OpenAPI**-Schema und der automatischen **Dokumentations-Oberfläche**. |
|||
|
|||
!!! info |
|||
Das `app.webhooks`-Objekt ist eigentlich nur ein `APIRouter`, derselbe Typ, den Sie verwenden würden, wenn Sie Ihre Anwendung mit mehreren Dateien strukturieren. |
|||
|
|||
Beachten Sie, dass Sie bei Webhooks tatsächlich keinen *Pfad* (wie `/items/`) deklarieren, sondern dass der Text, den Sie dort übergeben, lediglich eine **Kennzeichnung** des Webhooks (der Name des Events) ist. Zum Beispiel ist in `@app.webhooks.post("new-subscription")` der Webhook-Name `new-subscription`. |
|||
|
|||
Das liegt daran, dass erwartet wird, dass **Ihre Benutzer** den tatsächlichen **URL-Pfad**, an dem diese den Webhook-Request empfangen möchten, auf andere Weise definieren (z. B. über ein Web-Dashboard). |
|||
|
|||
### Es in der Dokumentation ansehen |
|||
|
|||
Jetzt können Sie Ihre Anwendung mit Uvicorn starten und auf <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> gehen. |
|||
|
|||
Sie werden sehen, dass Ihre Dokumentation die normalen *Pfadoperationen* und jetzt auch einige **Webhooks** enthält: |
|||
|
|||
<img src="/img/tutorial/openapi-webhooks/image01.png"> |
@ -0,0 +1,3 @@ |
|||
# Hilfe |
|||
|
|||
Helfen und Hilfe erhalten, beitragen, mitmachen. 🤝 |
@ -0,0 +1,5 @@ |
|||
# Lernen |
|||
|
|||
Hier finden Sie die einführenden Kapitel und Tutorials zum Erlernen von **FastAPI**. |
|||
|
|||
Sie könnten dies als **Buch**, als **Kurs**, als **offizielle** und empfohlene Methode zum Erlernen von FastAPI betrachten. 😎 |
@ -0,0 +1,8 @@ |
|||
# Referenz – Code-API |
|||
|
|||
Hier ist die Referenz oder Code-API, die Klassen, Funktionen, Parameter, Attribute und alle FastAPI-Teile, die Sie in Ihren Anwendungen verwenden können. |
|||
|
|||
Wenn Sie **FastAPI** lernen möchten, ist es viel besser, das [FastAPI-Tutorial](https://fastapi.tiangolo.com/tutorial/) zu lesen. |
|||
|
|||
!!! note "Hinweis Deutsche Übersetzung" |
|||
Die nachfolgende API wird aus der Quelltext-Dokumentation erstellt, daher sind nur die Einleitungen auf Deutsch. |
@ -0,0 +1,3 @@ |
|||
# Ressourcen |
|||
|
|||
Zusätzliche Ressourcen, externe Links, Artikel und mehr. ✈️ |
@ -0,0 +1,61 @@ |
|||
# Middleware |
|||
|
|||
Sie können Middleware zu **FastAPI**-Anwendungen hinzufügen. |
|||
|
|||
Eine „Middleware“ ist eine Funktion, die mit jedem **Request** arbeitet, bevor er von einer bestimmten *Pfadoperation* verarbeitet wird. Und auch mit jeder **Response**, bevor sie zurückgegeben wird. |
|||
|
|||
* Sie nimmt jeden **Request** entgegen, der an Ihre Anwendung gesendet wird. |
|||
* Sie kann dann etwas mit diesem **Request** tun oder beliebigen Code ausführen. |
|||
* Dann gibt sie den **Request** zur Verarbeitung durch den Rest der Anwendung weiter (durch eine bestimmte *Pfadoperation*). |
|||
* Sie nimmt dann die **Response** entgegen, die von der Anwendung generiert wurde (durch eine bestimmte *Pfadoperation*). |
|||
* Sie kann etwas mit dieser **Response** tun oder beliebigen Code ausführen. |
|||
* Dann gibt sie die **Response** zurück. |
|||
|
|||
!!! note "Technische Details" |
|||
Wenn Sie Abhängigkeiten mit `yield` haben, wird der Exit-Code *nach* der Middleware ausgeführt. |
|||
|
|||
Wenn es Hintergrundaufgaben gab (später dokumentiert), werden sie *nach* allen Middlewares ausgeführt. |
|||
|
|||
## Erstellung einer Middleware |
|||
|
|||
Um eine Middleware zu erstellen, verwenden Sie den Dekorator `@app.middleware("http")` über einer Funktion. |
|||
|
|||
Die Middleware-Funktion erhält: |
|||
|
|||
* Den `request`. |
|||
* Eine Funktion `call_next`, die den `request` als Parameter erhält. |
|||
* Diese Funktion gibt den `request` an die entsprechende *Pfadoperation* weiter. |
|||
* Dann gibt es die von der entsprechenden *Pfadoperation* generierte `response` zurück. |
|||
* Sie können die `response` dann weiter modifizieren, bevor Sie sie zurückgeben. |
|||
|
|||
```Python hl_lines="8-9 11 14" |
|||
{!../../../docs_src/middleware/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! tip "Tipp" |
|||
Beachten Sie, dass benutzerdefinierte proprietäre Header hinzugefügt werden können. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">Verwenden Sie dafür das Präfix 'X-'</a>. |
|||
|
|||
Wenn Sie jedoch benutzerdefinierte Header haben, die ein Client in einem Browser sehen soll, müssen Sie sie zu Ihrer CORS-Konfigurationen ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) hinzufügen, indem Sie den Parameter `expose_headers` verwenden, der in der <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette-CORS-Dokumentation</a> dokumentiert ist. |
|||
|
|||
!!! note "Technische Details" |
|||
Sie könnten auch `from starlette.requests import Request` verwenden. |
|||
|
|||
**FastAPI** bietet es als Komfort für Sie, den Entwickler, an. Aber es stammt direkt von Starlette. |
|||
|
|||
### Vor und nach der `response` |
|||
|
|||
Sie können Code hinzufügen, der mit dem `request` ausgeführt wird, bevor dieser von einer beliebigen *Pfadoperation* empfangen wird. |
|||
|
|||
Und auch nachdem die `response` generiert wurde, bevor sie zurückgegeben wird. |
|||
|
|||
Sie könnten beispielsweise einen benutzerdefinierten Header `X-Process-Time` hinzufügen, der die Zeit in Sekunden enthält, die benötigt wurde, um den Request zu verarbeiten und eine Response zu generieren: |
|||
|
|||
```Python hl_lines="10 12-13" |
|||
{!../../../docs_src/middleware/tutorial001.py!} |
|||
``` |
|||
|
|||
## Andere Middlewares |
|||
|
|||
Sie können später mehr über andere Middlewares in [Handbuch für fortgeschrittene Benutzer: Fortgeschrittene Middleware](../advanced/middleware.md){.internal-link target=_blank} lesen. |
|||
|
|||
In der nächsten Sektion erfahren Sie, wie Sie <abbr title="Cross-Origin Resource Sharing">CORS</abbr> mit einer Middleware behandeln können. |
@ -0,0 +1,34 @@ |
|||
# JSON互換エンコーダ |
|||
|
|||
データ型(Pydanticモデルのような)をJSONと互換性のあるもの(`dict`や`list`など)に変更する必要がある場合があります。 |
|||
|
|||
例えば、データベースに保存する必要がある場合です。 |
|||
|
|||
そのために、**FastAPI** は`jsonable_encoder()`関数を提供しています。 |
|||
|
|||
## `jsonable_encoder`の使用 |
|||
|
|||
JSON互換のデータのみを受信するデータベース`fase_db`があるとしましょう。 |
|||
|
|||
例えば、`datetime`オブジェクトはJSONと互換性がないので、このデーターベースには受け取られません。 |
|||
|
|||
そのため、`datetime`オブジェクトは<a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">ISO形式</a>のデータを含む`str`に変換されなければなりません。 |
|||
|
|||
同様に、このデータベースはPydanticモデル(属性を持つオブジェクト)を受け取らず、`dict`だけを受け取ります。 |
|||
|
|||
そのために`jsonable_encoder`を使用することができます。 |
|||
|
|||
Pydanticモデルのようなオブジェクトを受け取り、JSON互換版を返します: |
|||
|
|||
```Python hl_lines="5 22" |
|||
{!../../../docs_src/encoder/tutorial001.py!} |
|||
``` |
|||
|
|||
この例では、Pydanticモデルを`dict`に、`datetime`を`str`に変換します。 |
|||
|
|||
呼び出した結果は、Pythonの標準の<a href="https://docs.python.org/3/library/json.html#json.dumps" class="external-link" target="_blank">`json.dumps()`</a>でエンコードできるものです。 |
|||
|
|||
これはJSON形式のデータを含む大きな`str`を(文字列として)返しません。JSONと互換性のある値とサブの値を持つPython標準のデータ構造(例:`dict`)を返します。 |
|||
|
|||
!!! note "備考" |
|||
`jsonable_encoder`は実際には **FastAPI** が内部的にデータを変換するために使用します。しかしこれは他の多くのシナリオで有用です。 |
@ -0,0 +1,66 @@ |
|||
# 追加データ型 |
|||
|
|||
今までは、以下のような一般的なデータ型を使用してきました: |
|||
|
|||
* `int` |
|||
* `float` |
|||
* `str` |
|||
* `bool` |
|||
|
|||
しかし、より複雑なデータ型を使用することもできます。 |
|||
|
|||
そして、今まで見てきたのと同じ機能を持つことになります: |
|||
|
|||
* 素晴らしいエディタのサポート |
|||
* 受信したリクエストからのデータ変換 |
|||
* レスポンスデータのデータ変換 |
|||
* データの検証 |
|||
* 自動注釈と文書化 |
|||
|
|||
## 他のデータ型 |
|||
|
|||
ここでは、使用できる追加のデータ型のいくつかを紹介します: |
|||
|
|||
* `UUID`: |
|||
* 多くのデータベースやシステムで共通のIDとして使用される、標準的な「ユニバーサルにユニークな識別子」です。 |
|||
* リクエストとレスポンスでは`str`として表現されます。 |
|||
* `datetime.datetime`: |
|||
* Pythonの`datetime.datetime`です。 |
|||
* リクエストとレスポンスはISO 8601形式の`str`で表現されます: `2008-09-15T15:53:00+05:00` |
|||
* `datetime.date`: |
|||
* Pythonの`datetime.date`です。 |
|||
* リクエストとレスポンスはISO 8601形式の`str`で表現されます: `2008-09-15` |
|||
* `datetime.time`: |
|||
* Pythonの`datetime.time`. |
|||
* リクエストとレスポンスはISO 8601形式の`str`で表現されます: `14:23:55.003` |
|||
* `datetime.timedelta`: |
|||
* Pythonの`datetime.timedelta`です。 |
|||
* リクエストとレスポンスでは合計秒数の`float`で表現されます。 |
|||
* Pydanticでは「ISO 8601 time diff encoding」として表現することも可能です。<a href="https://pydantic-docs.helpmanual.io/#json-serialisation" class="external-link" target="_blank">詳細はドキュメントを参照してください</a>。 |
|||
* `frozenset`: |
|||
* リクエストとレスポンスでは`set`と同じように扱われます: |
|||
* リクエストでは、リストが読み込まれ、重複を排除して`set`に変換されます。 |
|||
* レスポンスでは`set`が`list`に変換されます。 |
|||
* 生成されたスキーマは`set`の値が一意であることを指定します(JSON Schemaの`uniqueItems`を使用します)。 |
|||
* `bytes`: |
|||
* Pythonの標準的な`bytes`です。 |
|||
* リクエストとレスポンスでは`str`として扱われます。 |
|||
* 生成されたスキーマは`str`で`binary`の「フォーマット」持つことを指定します。 |
|||
* `Decimal`: |
|||
* Pythonの標準的な`Decimal`です。 |
|||
* リクエストやレスポンスでは`float`と同じように扱います。 |
|||
|
|||
* Pydanticの全ての有効な型はこちらで確認できます: <a href="https://pydantic-docs.helpmanual.io/usage/types" class="external-link" target="_blank">Pydantic data types</a>。 |
|||
## 例 |
|||
|
|||
ここでは、上記の型のいくつかを使用したパラメータを持つ*path operation*の例を示します。 |
|||
|
|||
```Python hl_lines="1 2 12-16" |
|||
{!../../../docs_src/extra_data_types/tutorial001.py!} |
|||
``` |
|||
|
|||
関数内のパラメータは自然なデータ型を持っていることに注意してください。そして、以下のように通常の日付操作を行うことができます: |
|||
|
|||
```Python hl_lines="18 19" |
|||
{!../../../docs_src/extra_data_types/tutorial001.py!} |
|||
``` |
@ -0,0 +1,698 @@ |
|||
# 컨테이너의 FastAPI - 도커 |
|||
|
|||
FastAPI 어플리케이션을 배포할 때 일반적인 접근 방법은 **리눅스 컨테이너 이미지**를 생성하는 것입니다. 이 방법은 주로 <a href="https://www.docker.com/" class="external-link" target="_blank">**도커**</a>를 사용해 이루어집니다. 그런 다음 해당 컨테이너 이미지를 몇가지 방법으로 배포할 수 있습니다. |
|||
|
|||
리눅스 컨테이너를 사용하는 데에는 **보안**, **반복 가능성**, **단순함** 등의 장점이 있습니다. |
|||
|
|||
!!! 팁 |
|||
시간에 쫓기고 있고 이미 이런것들을 알고 있다면 [`Dockerfile`👇](#build-a-docker-image-for-fastapi)로 점프할 수 있습니다. |
|||
|
|||
<details> |
|||
<summary>도커파일 미리보기 👀</summary> |
|||
|
|||
```Dockerfile |
|||
FROM python:3.9 |
|||
|
|||
WORKDIR /code |
|||
|
|||
COPY ./requirements.txt /code/requirements.txt |
|||
|
|||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
|||
|
|||
COPY ./app /code/app |
|||
|
|||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] |
|||
|
|||
# If running behind a proxy like Nginx or Traefik add --proxy-headers |
|||
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"] |
|||
``` |
|||
|
|||
</details> |
|||
|
|||
## 컨테이너란 |
|||
|
|||
컨테이너(주로 리눅스 컨테이너)는 어플리케이션의 의존성과 필요한 파일들을 모두 패키징하는 매우 **가벼운** 방법입니다. 컨테이너는 같은 시스템에 있는 다른 컨테이너(다른 어플리케이션이나 요소들)와 독립적으로 유지됩니다. |
|||
|
|||
리눅스 컨테이너는 호스트(머신, 가상 머신, 클라우드 서버 등)와 같은 리눅스 커널을 사용해 실행됩니다. 이말은 리눅스 컨테이너가 (전체 운영체제를 모방하는 다른 가상 머신과 비교했을 때) 매우 가볍다는 것을 의미합니다. |
|||
|
|||
이 방법을 통해, 컨테이너는 직접 프로세스를 실행하는 것과 비슷한 정도의 **적은 자원**을 소비합니다 (가상 머신은 훨씬 많은 자원을 소비할 것입니다). |
|||
|
|||
컨테이너는 또한 그들만의 **독립된** 실행 프로세스 (일반적으로 하나의 프로세스로 충분합니다), 파일 시스템, 그리고 네트워크를 가지므로 배포, 보안, 개발 및 기타 과정을 단순화 합니다. |
|||
|
|||
## 컨테이너 이미지란 |
|||
|
|||
**컨테이너**는 **컨테이너 이미지**를 실행한 것 입니다. |
|||
|
|||
컨테이너 이미지란 컨테이너에 필요한 모든 파일, 환경 변수 그리고 디폴트 명령/프로그램의 **정적** 버전입니다. 여기서 **정적**이란 말은 컨테이너 **이미지**가 작동되거나 실행되지 않으며, 단지 패키지 파일과 메타 데이터라는 것을 의미합니다. |
|||
|
|||
저장된 정적 컨텐츠인 **컨테이너 이미지**와 대조되게, **컨테이너**란 보통 실행될 수 있는 작동 인스턴스를 의미합니다. |
|||
|
|||
**컨테이너**가 (**컨테이너 이미지**로 부터) 시작되고 실행되면, 컨테이너는 파일이나 환경 변수를 생성하거나 변경할 수 있습니다. 이러한 변화는 오직 컨테이너에서만 존재하며, 그 기반이 되는 컨테이너 이미지에는 지속되지 않습니다 (즉 디스크에는 저장되지 않습니다). |
|||
|
|||
컨테이너 이미지는 **프로그램** 파일과 컨텐츠, 즉 `python`과 어떤 파일 `main.py`에 비교할 수 있습니다. |
|||
|
|||
그리고 (**컨테이너 이미지**와 대비해서) **컨테이너**는 이미지의 실제 실행 인스턴스로 **프로세스**에 비교할 수 있습니다. 사실, 컨테이너는 **프로세스 러닝**이 있을 때만 실행됩니다 (그리고 보통 하나의 프로세스 입니다). 컨테이너는 내부에서 실행되는 프로세스가 없으면 종료됩니다. |
|||
|
|||
## 컨테이너 이미지 |
|||
|
|||
도커는 **컨테이너 이미지**와 **컨테이너**를 생성하고 관리하는데 주요 도구 중 하나가 되어왔습니다. |
|||
|
|||
그리고 <a href="https://hub.docker.com/" class="external-link" target="_blank">도커 허브</a>에 다양한 도구, 환경, 데이터베이스, 그리고 어플리케이션에 대해 미리 만들어진 **공식 컨테이너 이미지**가 공개되어 있습니다. |
|||
|
|||
예를 들어, 공식 <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">파이썬 이미지</a>가 있습니다. |
|||
|
|||
또한 다른 대상, 예를 들면 데이터베이스를 위한 이미지들도 있습니다: |
|||
|
|||
* <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> 등 |
|||
|
|||
미리 만들어진 컨테이너 이미지를 사용하면 서로 다른 도구들을 **결합**하기 쉽습니다. 대부분의 경우에, **공식 이미지들**을 사용하고 환경 변수를 통해 설정할 수 있습니다. |
|||
|
|||
이런 방법으로 대부분의 경우에 컨테이너와 도커에 대해 배울 수 있으며 다양한 도구와 요소들에 대한 지식을 재사용할 수 있습니다. |
|||
|
|||
따라서, 서로 다른 **다중 컨테이너**를 생성한 다음 이들을 연결할 수 있습니다. 예를 들어 데이터베이스, 파이썬 어플리케이션, 리액트 프론트엔드 어플리케이션을 사용하는 웹 서버에 대한 컨테이너를 만들어 이들의 내부 네트워크로 각 컨테이너를 연결할 수 있습니다. |
|||
|
|||
모든 컨테이너 관리 시스템(도커나 쿠버네티스)은 이러한 네트워킹 특성을 포함하고 있습니다. |
|||
|
|||
## 컨테이너와 프로세스 |
|||
|
|||
**컨테이너 이미지**는 보통 **컨테이너**를 시작하기 위해 필요한 메타데이터와 디폴트 커맨드/프로그램과 그 프로그램에 전달하기 위한 파라미터들을 포함합니다. 이는 커맨드 라인에서 프로그램을 실행할 때 필요한 값들과 유사합니다. |
|||
|
|||
**컨테이너**가 시작되면, 해당 커맨드/프로그램이 실행됩니다 (그러나 다른 커맨드/프로그램을 실행하도록 오버라이드 할 수 있습니다). |
|||
|
|||
컨테이너는 **메인 프로세스**(커맨드 또는 프로그램)이 실행되는 동안 실행됩니다. |
|||
|
|||
컨테이너는 일반적으로 **단일 프로세스**를 가지고 있지만, 메인 프로세스의 서브 프로세스를 시작하는 것도 가능하며, 이 방법으로 하나의 컨테이너에 **다중 프로세스**를 가질 수 있습니다. |
|||
|
|||
그러나 **최소한 하나의 실행중인 프로세스**를 가지지 않고서는 실행중인 컨테이너를 가질 수 없습니다. 만약 메인 프로세스가 중단되면, 컨테이너도 중단됩니다. |
|||
|
|||
## FastAPI를 위한 도커 이미지 빌드하기 |
|||
|
|||
이제 무언가를 만들어 봅시다! 🚀 |
|||
|
|||
**공식 파이썬** 이미지에 기반하여, FastAPI를 위한 **도커 이미지**를 **맨 처음부터** 생성하는 방법을 보이겠습니다. |
|||
|
|||
**대부분의 경우**에 다음과 같은 것들을 하게 됩니다. 예를 들면: |
|||
|
|||
* **쿠버네티스** 또는 유사한 도구 사용하기 |
|||
* **라즈베리 파이**로 실행하기 |
|||
* 컨테이너 이미지를 실행할 클라우드 서비스 사용하기 등 |
|||
|
|||
### 요구 패키지 |
|||
|
|||
일반적으로는 어플리케이션의 특정 파일을 위한 **패키지 요구 조건**이 있을 것입니다. |
|||
|
|||
그 요구 조건을 **설치**하는 방법은 여러분이 사용하는 도구에 따라 다를 것입니다. |
|||
|
|||
가장 일반적인 방법은 패키지 이름과 버전이 줄 별로 기록된 `requirements.txt` 파일을 만드는 것입니다. |
|||
|
|||
버전의 범위를 설정하기 위해서는 [FastAPI 버전들에 대하여](./versions.md){.internal-link target=_blank}에 쓰여진 것과 같은 아이디어를 사용합니다. |
|||
|
|||
예를 들어, `requirements.txt` 파일은 다음과 같을 수 있습니다: |
|||
|
|||
``` |
|||
fastapi>=0.68.0,<0.69.0 |
|||
pydantic>=1.8.0,<2.0.0 |
|||
uvicorn>=0.15.0,<0.16.0 |
|||
``` |
|||
|
|||
그리고 일반적으로 패키지 종속성은 `pip`로 설치합니다. 예를 들어: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install -r requirements.txt |
|||
---> 100% |
|||
Successfully installed fastapi pydantic uvicorn |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! 정보 |
|||
패키지 종속성을 정의하고 설치하기 위한 방법과 도구는 다양합니다. |
|||
|
|||
나중에 아래 세션에서 Poetry를 사용한 예시를 보이겠습니다. 👇 |
|||
|
|||
### **FastAPI** 코드 생성하기 |
|||
|
|||
* `app` 디렉터리를 생성하고 이동합니다. |
|||
* 빈 파일 `__init__.py`을 생성합니다. |
|||
* 다음과 같은 `main.py`을 생성합니다: |
|||
|
|||
```Python |
|||
from typing import Union |
|||
|
|||
from fastapi import FastAPI |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
@app.get("/") |
|||
def read_root(): |
|||
return {"Hello": "World"} |
|||
|
|||
|
|||
@app.get("/items/{item_id}") |
|||
def read_item(item_id: int, q: Union[str, None] = None): |
|||
return {"item_id": item_id, "q": q} |
|||
``` |
|||
|
|||
### 도커파일 |
|||
|
|||
이제 같은 프로젝트 디렉터리에 다음과 같은 파일 `Dockerfile`을 생성합니다: |
|||
|
|||
```{ .dockerfile .annotate } |
|||
# (1) |
|||
FROM python:3.9 |
|||
|
|||
# (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 ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
1. 공식 파이썬 베이스 이미지에서 시작합니다. |
|||
|
|||
2. 현재 워킹 디렉터리를 `/code`로 설정합니다. |
|||
|
|||
여기에 `requirements.txt` 파일과 `app` 디렉터리를 위치시킬 것입니다. |
|||
|
|||
3. 요구 조건과 파일을 `/code` 디렉터리로 복사합니다. |
|||
|
|||
처음에는 **오직** 요구 조건이 필요한 파일만 복사하고, 이외의 코드는 그대로 둡니다. |
|||
|
|||
이 파일이 **자주 바뀌지 않기 때문에**, 도커는 파일을 탐지하여 이 단계의 **캐시**를 사용하여 다음 단계에서도 캐시를 사용할 수 있도록 합니다. |
|||
|
|||
4. 요구 조건 파일에 있는 패키지 종속성을 설치합니다. |
|||
|
|||
`--no-cache-dir` 옵션은 `pip`에게 다운로드한 패키지들을 로컬 환경에 저장하지 않도록 전달합니다. 이는 마치 같은 패키지를 설치하기 위해 오직 `pip`만 다시 실행하면 될 것 같지만, 컨테이너로 작업하는 경우 그렇지는 않습니다. |
|||
|
|||
!!! 노트 |
|||
`--no-cache-dir` 는 오직 `pip`와 관련되어 있으며, 도커나 컨테이너와는 무관합니다. |
|||
|
|||
`--upgrade` 옵션은 `pip`에게 설치된 패키지들을 업데이트하도록 합니다. |
|||
|
|||
이전 단계에서 파일을 복사한 것이 **도커 캐시**에 의해 탐지되기 때문에, 이 단계에서도 가능한 한 **도커 캐시**를 사용하게 됩니다. |
|||
|
|||
이 단계에서 캐시를 사용하면 **매번** 모든 종속성을 다운로드 받고 설치할 필요가 없어, 개발 과정에서 이미지를 지속적으로 생성하는 데에 드는 **시간**을 많이 **절약**할 수 있습니다. |
|||
|
|||
5. `/code` 디렉터리에 `./app` 디렉터리를 복사합니다. |
|||
|
|||
**자주 변경되는** 모든 코드를 포함하고 있기 때문에, 도커 **캐시**는 이 단계나 **이후의 단계에서** 잘 사용되지 않습니다. |
|||
|
|||
그러므로 컨테이너 이미지 빌드 시간을 최적화하기 위해 `Dockerfile`의 **거의 끝 부분**에 입력하는 것이 중요합니다. |
|||
|
|||
6. `uvicorn` 서버를 실행하기 위해 **커맨드**를 설정합니다. |
|||
|
|||
`CMD`는 문자열 리스트를 입력받고, 각 문자열은 커맨드 라인의 각 줄에 입력할 문자열입니다. |
|||
|
|||
이 커맨드는 **현재 워킹 디렉터리**에서 실행되며, 이는 위에서 `WORKDIR /code`로 설정한 `/code` 디렉터리와 같습니다. |
|||
|
|||
프로그램이 `/code`에서 시작하고 그 속에 `./app` 디렉터리가 여러분의 코드와 함께 들어있기 때문에, **Uvicorn**은 이를 보고 `app`을 `app.main`으로부터 **불러 올** 것입니다. |
|||
|
|||
!!! 팁 |
|||
각 코드 라인을 코드의 숫자 버블을 클릭하여 리뷰할 수 있습니다. 👆 |
|||
|
|||
이제 여러분은 다음과 같은 디렉터리 구조를 가지고 있을 것입니다: |
|||
|
|||
``` |
|||
. |
|||
├── app |
|||
│ ├── __init__.py |
|||
│ └── main.py |
|||
├── Dockerfile |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
#### TLS 종료 프록시의 배후 |
|||
|
|||
만약 여러분이 컨테이너를 Nginx 또는 Traefik과 같은 TLS 종료 프록시 (로드 밸런서) 뒤에서 실행하고 있다면, `--proxy-headers` 옵션을 더하는 것이 좋습니다. 이 옵션은 Uvicorn에게 어플리케이션이 HTTPS 등의 뒤에서 실행되고 있으므로 프록시에서 전송된 헤더를 신뢰할 수 있다고 알립니다. |
|||
|
|||
```Dockerfile |
|||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
#### 도커 캐시 |
|||
|
|||
이 `Dockerfile`에는 중요한 트릭이 있는데, 처음에는 **의존성이 있는 파일만** 복사하고, 나머지 코드는 그대로 둡니다. 왜 이런 방법을 써야하는지 설명하겠습니다. |
|||
|
|||
```Dockerfile |
|||
COPY ./requirements.txt /code/requirements.txt |
|||
``` |
|||
|
|||
도커와 다른 도구들은 컨테이너 이미지를 **증가하는 방식으로 빌드**합니다. `Dockerfile`의 맨 윗 부분부터 시작해, 레이어 위에 새로운 레이어를 더하는 방식으로, `Dockerfile`의 각 지시 사항으로 부터 생성된 어떤 파일이든 더해갑니다. |
|||
|
|||
도커 그리고 이와 유사한 도구들은 이미지 생성 시에 **내부 캐시**를 사용합니다. 만약 어떤 파일이 마지막으로 컨테이너 이미지를 빌드한 때로부터 바뀌지 않았다면, 파일을 다시 복사하여 새로운 레이어를 처음부터 생성하는 것이 아니라, 마지막에 생성했던 **같은 레이어를 재사용**합니다. |
|||
|
|||
단지 파일 복사를 지양하는 것으로 효율이 많이 향상되는 것은 아니지만, 그 단계에서 캐시를 사용했기 때문에, **다음 단계에서도 마찬가지로 캐시를 사용**할 수 있습니다. 예를 들어, 다음과 같은 의존성을 설치하는 지시 사항을 위한 캐시를 사용할 수 있습니다: |
|||
|
|||
```Dockerfile |
|||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
|||
``` |
|||
|
|||
패키지를 포함하는 파일은 **자주 변경되지 않습니다**. 따라서 해당 파일만 복사하므로서, 도커는 그 단계의 **캐시를 사용**할 수 있습니다. |
|||
|
|||
그 다음으로, 도커는 **다음 단계에서** 의존성을 다운로드하고 설치하는 **캐시를 사용**할 수 있게 됩니다. 바로 이 과정에서 우리는 **많은 시간을 절약**하게 됩니다. ✨ ...그리고 기다리는 지루함도 피할 수 있습니다. 😪😆 |
|||
|
|||
패키지 의존성을 다운로드 받고 설치하는 데이는 **수 분이 걸릴 수 있지만**, **캐시**를 사용하면 최대 **수 초만에** 끝낼 수 있습니다. |
|||
|
|||
또한 여러분이 개발 과정에서 코드의 변경 사항이 반영되었는지 확인하기 위해 컨테이너 이미지를 계속해서 빌드하면, 절약된 시간은 축적되어 더욱 커질 것입니다. |
|||
|
|||
그리고 나서 `Dockerfile`의 거의 끝 부분에서, 모든 코드를 복사합니다. 이것이 **가장 빈번하게 변경**되는 부분이며, 대부분의 경우에 이 다음 단계에서는 캐시를 사용할 수 없기 때문에 가장 마지막에 둡니다. |
|||
|
|||
```Dockerfile |
|||
COPY ./app /code/app |
|||
``` |
|||
|
|||
### 도커 이미지 생성하기 |
|||
|
|||
이제 모든 파일이 제자리에 있으니, 컨테이너 이미지를 빌드합니다. |
|||
|
|||
* (여러분의 `Dockerfile`과 `app` 디렉터리가 위치한) 프로젝트 디렉터리로 이동합니다. |
|||
* FastAPI 이미지를 빌드합니다: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ docker build -t myimage . |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! 팁 |
|||
맨 끝에 있는 `.` 에 주목합시다. 이는 `./`와 동등하며, 도커에게 컨테이너 이미지를 빌드하기 위한 디렉터리를 알려줍니다. |
|||
|
|||
이 경우에는 현재 디렉터리(`.`)와 같습니다. |
|||
|
|||
### 도커 컨테이너 시작하기 |
|||
|
|||
* 여러분의 이미지에 기반하여 컨테이너를 실행합니다: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ docker run -d --name mycontainer -p 80:80 myimage |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## 체크하기 |
|||
|
|||
여러분의 도커 컨테이너 URL에서 실행 사항을 체크할 수 있습니다. 예를 들어: <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> 또는 <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> (또는 동일하게, 여러분의 도커 호스트를 이용해서 체크할 수도 있습니다). |
|||
|
|||
아래와 비슷한 것을 보게 될 것입니다: |
|||
|
|||
```JSON |
|||
{"item_id": 5, "q": "somequery"} |
|||
``` |
|||
|
|||
## 인터랙티브 API 문서 |
|||
|
|||
이제 여러분은 <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> 또는 <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a>로 이동할 수 있습니다(또는, 여러분의 도커 호스트를 이용할 수 있습니다). |
|||
|
|||
여러분은 자동으로 생성된 인터랙티브 API(<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>에서 제공된)를 볼 수 있습니다: |
|||
|
|||
 |
|||
|
|||
## 대안 API 문서 |
|||
|
|||
또한 여러분은 <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> 또는 <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a>으로 이동할 수 있습니다(또는, 여러분의 도커 호스트를 이용할 수 있습니다). |
|||
|
|||
여러분은 자동으로 생성된 대안 문서(<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>에서 제공된)를 볼 수 있습니다: |
|||
|
|||
 |
|||
|
|||
## 단일 파일 FastAPI로 도커 이미지 생성하기 |
|||
|
|||
만약 여러분의 FastAPI가 하나의 파일이라면, 예를 들어 `./app` 디렉터리 없이 `main.py` 파일만으로 이루어져 있다면, 파일 구조는 다음과 유사할 것입니다: |
|||
|
|||
``` |
|||
. |
|||
├── Dockerfile |
|||
├── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
그러면 여러분들은 `Dockerfile` 내에 있는 파일을 복사하기 위해 그저 상응하는 경로를 바꾸기만 하면 됩니다: |
|||
|
|||
```{ .dockerfile .annotate hl_lines="10 13" } |
|||
FROM python:3.9 |
|||
|
|||
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 ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
1. `main.py` 파일을 `/code` 디렉터리로 곧바로 복사합니다(`./app` 디렉터리는 고려하지 않습니다). |
|||
|
|||
2. Uvicorn을 실행해 `app` 객체를 (`app.main` 대신) `main`으로 부터 불러오도록 합니다. |
|||
|
|||
그 다음 Uvicorn 커맨드를 조정해서 FastAPI 객체를 불러오는데 `app.main` 대신에 새로운 모듈 `main`을 사용하도록 합니다. |
|||
|
|||
## 배포 개념 |
|||
|
|||
이제 컨테이너의 측면에서 [배포 개념](./concepts.md){.internal-link target=_blank}에서 다루었던 것과 같은 배포 개념에 대해 이야기해 보겠습니다. |
|||
|
|||
컨테이너는 주로 어플리케이션을 빌드하고 배포하기 위한 과정을 단순화하는 도구이지만, **배포 개념**에 대한 특정한 접근법을 강요하지 않기 때문에 가능한 배포 전략에는 여러가지가 있습니다. |
|||
|
|||
**좋은 소식**은 서로 다른 전략들을 포괄하는 배포 개념이 있다는 점입니다. 🎉 |
|||
|
|||
컨테이너 측면에서 **배포 개념**을 리뷰해 보겠습니다: |
|||
|
|||
* HTTPS |
|||
* 구동하기 |
|||
* 재시작 |
|||
* 복제 (실행 중인 프로세스 개수) |
|||
* 메모리 |
|||
* 시작하기 전 단계들 |
|||
|
|||
## HTTPS |
|||
|
|||
만약 우리가 FastAPI 어플리케이션을 위한 **컨테이너 이미지**에만 집중한다면 (그리고 나중에 실행될 **컨테이너**에), HTTPS는 일반적으로 다른 도구에 의해 **외부적으로** 다루어질 것 입니다. |
|||
|
|||
**HTTPS**와 **인증서**의 **자동** 취득을 다루는 것은 다른 컨테이너가 될 수 있는데, 예를 들어 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>을 사용하는 것입니다. |
|||
|
|||
!!! 팁 |
|||
Traefik은 도커, 쿠버네티스, 그리고 다른 도구와 통합되어 있어 여러분의 컨테이너를 포함하는 HTTPS를 셋업하고 설정하는 것이 매우 쉽습니다. |
|||
|
|||
대안적으로, HTTPS는 클라우드 제공자에 의해 서비스의 일환으로 다루어질 수도 있습니다 (이때도 어플리케이션은 여전히 컨테이너에서 실행될 것입니다). |
|||
|
|||
## 구동과 재시작 |
|||
|
|||
여러분의 컨테이너를 **시작하고 실행하는** 데에 일반적으로 사용되는 도구는 따로 있습니다. |
|||
|
|||
이는 **도커** 자체일 수도 있고, **도커 컴포즈**, **쿠버네티스**, **클라우드 서비스** 등이 될 수 있습니다. |
|||
|
|||
대부분 (또는 전체) 경우에, 컨테이너를 구동하거나 고장시에 재시작하도록 하는 간단한 옵션이 있습니다. 예를 들어, 도커에서는, 커맨드 라인 옵션 `--restart` 입니다. |
|||
|
|||
컨테이너를 사용하지 않고서는, 어플리케이션을 구동하고 재시작하는 것이 매우 번거롭고 어려울 수 있습니다. 하지만 **컨테이너를 사용한다면** 대부분의 경우에 이런 기능은 기본적으로 포함되어 있습니다. ✨ |
|||
|
|||
## 복제 - 프로세스 개수 |
|||
|
|||
만약 여러분이 **쿠버네티스**와 머신 <abbr title="A group of machines that are configured to be connected and work together in some way.">클러스터</abbr>, 도커 스왐 모드, 노마드, 또는 다른 여러 머신 위에 분산 컨테이너를 관리하는 복잡한 시스템을 다루고 있다면, 여러분은 각 컨테이너에서 (워커와 함께 사용하는 Gunicorn 같은) **프로세스 매니저** 대신 **클러스터 레벨**에서 **복제를 다루**고 싶을 것입니다. |
|||
|
|||
쿠버네티스와 같은 분산 컨테이너 관리 시스템 중 일부는 일반적으로 들어오는 요청에 대한 **로드 밸런싱**을 지원하면서 **컨테이너 복제**를 다루는 통합된 방법을 가지고 있습니다. 모두 **클러스터 레벨**에서 말이죠. |
|||
|
|||
이런 경우에, 여러분은 [위에서 묘사된 것](#dockerfile)처럼 **처음부터 도커 이미지를** 빌드해서, 의존성을 설치하고, Uvicorn 워커를 관리하는 Gunicorn 대신 **단일 Uvicorn 프로세스**를 실행하고 싶을 것입니다. |
|||
|
|||
### 로드 밸런서 |
|||
|
|||
컨테이너로 작업할 때, 여러분은 일반적으로 **메인 포트의 상황을 감지하는** 요소를 가지고 있을 것입니다. 이는 **HTTPS**를 다루는 **TLS 종료 프록시**와 같은 다른 컨테이너일 수도 있고, 유사한 다른 도구일 수도 있습니다. |
|||
|
|||
이 요소가 요청들의 **로드**를 읽어들이고 각 워커에게 (바라건대) **균형적으로** 분배한다면, 이 요소는 일반적으로 **로드 밸런서**라고 불립니다. |
|||
|
|||
!!! 팁 |
|||
HTTPS를 위해 사용된 **TLS 종료 프록시** 요소 또한 **로드 밸런서**가 될 수 있습니다. |
|||
|
|||
또한 컨테이너로 작업할 때, 컨테이너를 시작하고 관리하기 위해 사용한 것과 동일한 시스템은 이미 해당 **로드 밸런서**로 부터 여러분의 앱에 해당하는 컨테이너로 **네트워크 통신**(예를 들어, HTTP 요청)을 전송하는 내부적인 도구를 가지고 있을 것입니다 (여기서도 로드 밸런서는 **TLS 종료 프록시**일 수 있습니다). |
|||
|
|||
### 하나의 로드 밸런서 - 다중 워커 컨테이너 |
|||
|
|||
**쿠버네티스**나 또는 다른 분산 컨테이너 관리 시스템으로 작업할 때, 시스템 내부의 네트워킹 메커니즘을 이용함으로써 메인 **포트**를 감지하고 있는 단일 **로드 밸런서**는 여러분의 앱에서 실행되고 있는 **여러개의 컨테이너**에 통신(요청들)을 전송할 수 있게 됩니다. |
|||
|
|||
여러분의 앱에서 실행되고 있는 각각의 컨테이너는 일반적으로 **하나의 프로세스**만 가질 것입니다 (예를 들어, FastAPI 어플리케이션에서 실행되는 하나의 Uvicorn 프로세스처럼). 이 컨테이너들은 모두 같은 것을 실행하는 점에서 **동일한 컨테이너**이지만, 프로세스, 메모리 등은 공유하지 않습니다. 이 방식으로 여러분은 CPU의 **서로 다른 코어들** 또는 **서로 다른 머신들**을 **병렬화**하는 이점을 얻을 수 있습니다. |
|||
|
|||
또한 **로드 밸런서**가 있는 분산 컨테이너 시스템은 여러분의 앱에 있는 컨테이너 각각에 **차례대로 요청을 분산**시킬 것 입니다. 따라서 각 요청은 여러분의 앱에서 실행되는 여러개의 **복제된 컨테이너들** 중 하나에 의해 다루어질 것 입니다. |
|||
|
|||
그리고 일반적으로 **로드 밸런서**는 여러분의 클러스터에 있는 *다른* 앱으로 가는 요청들도 다룰 수 있으며 (예를 들어, 다른 도메인으로 가거나 다른 URL 경로 접두사를 가지는 경우), 이 통신들을 클러스터에 있는 *바로 그 다른* 어플리케이션으로 제대로 전송할 수 있습니다. |
|||
|
|||
### 단일 프로세스를 가지는 컨테이너 |
|||
|
|||
이 시나리오의 경우, 여러분은 이미 클러스터 레벨에서 복제를 다루고 있을 것이므로 **컨테이너 당 단일 (Uvicorn) 프로세스**를 가지고자 할 것입니다. |
|||
|
|||
따라서, 여러분은 Gunicorn 이나 Uvicorn 워커, 또는 Uvicorn 워커를 사용하는 Uvicorn 매니저와 같은 프로세스 매니저를 가지고 싶어하지 **않을** 것입니다. 여러분은 컨테이너 당 **단일 Uvicorn 프로세스**를 가지고 싶어할 것입니다 (그러나 아마도 다중 컨테이너를 가질 것입니다). |
|||
|
|||
이미 여러분이 클러스터 시스템을 관리하고 있으므로, (Uvicorn 워커를 관리하는 Gunicorn 이나 Uvicorn 처럼) 컨테이너 내에 다른 프로세스 매니저를 가지는 것은 **불필요한 복잡성**만 더하게 될 것입니다. |
|||
|
|||
### 다중 프로세스를 가지는 컨테이너와 특수한 경우들 |
|||
|
|||
당연한 말이지만, 여러분이 내부적으로 **Uvicorn 워커 프로세스들**를 시작하는 **Gunicorn 프로세스 매니저**를 가지는 단일 컨테이너를 원하는 **특수한 경우**도 있을 것입니다. |
|||
|
|||
그런 경우에, 여러분들은 **Gunicorn**을 프로세스 매니저로 포함하는 **공식 도커 이미지**를 사용할 수 있습니다. 이 프로세스 매니저는 다중 **Uvicorn 워커 프로세스들**을 실행하며, 디폴트 세팅으로 현재 CPU 코어에 기반하여 자동으로 워커 개수를 조정합니다. 이 사항에 대해서는 아래의 [Gunicorn과 함께하는 공식 도커 이미지 - Uvicorn](#official-docker-image-with-gunicorn-uvicorn)에서 더 다루겠습니다. |
|||
|
|||
이런 경우에 해당하는 몇가지 예시가 있습니다: |
|||
|
|||
#### 단순한 앱 |
|||
|
|||
만약 여러분의 어플리케이션이 **충분히 단순**해서 (적어도 아직은) 프로세스 개수를 파인-튠 할 필요가 없거나 클러스터가 아닌 **단일 서버**에서 실행하고 있다면, 여러분은 컨테이너 내에 프로세스 매니저를 사용하거나 (공식 도커 이미지에서) 자동으로 설정되는 디폴트 값을 사용할 수 있습니다. |
|||
|
|||
#### 도커 구성 |
|||
|
|||
여러분은 **도커 컴포즈**로 (클러스터가 아닌) **단일 서버로** 배포할 수 있으며, 이 경우에 공유된 네트워크와 **로드 밸런싱**을 포함하는 (도커 컴포즈로) 컨테이너의 복제를 관리하는 단순한 방법이 없을 수도 있습니다. |
|||
|
|||
그렇다면 여러분은 **프로세스 매니저**와 함께 내부에 **몇개의 워커 프로세스들**을 시작하는 **단일 컨테이너**를 필요로 할 수 있습니다. |
|||
|
|||
#### Prometheus와 다른 이유들 |
|||
|
|||
여러분은 **단일 프로세스**를 가지는 **다중 컨테이너** 대신 **다중 프로세스**를 가지는 **단일 컨테이너**를 채택하는 **다른 이유**가 있을 수 있습니다. |
|||
|
|||
예를 들어 (여러분의 장치 설정에 따라) Prometheus 익스포터와 같이 같은 컨테이너에 들어오는 **각 요청에 대해** 접근권한을 가지는 도구를 사용할 수 있습니다. |
|||
|
|||
이 경우에 여러분이 **여러개의 컨테이너들**을 가지고 있다면, Prometheus가 **메트릭을 읽어 들일 때**, 디폴트로 **매번 하나의 컨테이너**(특정 리퀘스트를 관리하는 바로 그 컨테이너)로 부터 읽어들일 것입니다. 이는 모든 복제된 컨테이너에 대해 **축적된 메트릭들**을 읽어들이는 것과 대비됩니다. |
|||
|
|||
그렇다면 이 경우에는 **다중 프로세스**를 가지는 **하나의 컨테이너**를 두어서 같은 컨테이너에서 모든 내부 프로세스에 대한 Prometheus 메트릭을 수집하는 로컬 도구(예를 들어 Prometheus 익스포터 같은)를 두어서 이 메그릭들을 하나의 컨테이너에 내에서 공유하는 방법이 더 단순할 것입니다. |
|||
|
|||
--- |
|||
|
|||
요점은, 이 중의 **어느것도** 여러분들이 반드시 따라야하는 **확정된 사실**이 아니라는 것입니다. 여러분은 이 아이디어들을 **여러분의 고유한 이용 사례를 평가**하는데 사용하고, 여러분의 시스템에 가장 적합한 접근법이 어떤 것인지 결정하며, 다음의 개념들을 관리하는 방법을 확인할 수 있습니다: |
|||
|
|||
* 보안 - HTTPS |
|||
* 구동하기 |
|||
* 재시작 |
|||
* 복제 (실행 중인 프로세스 개수) |
|||
* 메모리 |
|||
* 시작하기 전 단계들 |
|||
|
|||
## 메모리 |
|||
|
|||
만약 여러분이 **컨테이너 당 단일 프로세스**를 실행한다면, 여러분은 각 컨테이너(복제된 경우에는 여러개의 컨테이너들)에 대해 잘 정의되고, 안정적이며, 제한된 용량의 메모리 소비량을 가지고 있을 것입니다. |
|||
|
|||
그러면 여러분의 컨테이너 관리 시스템(예를 들어 **쿠버네티스**) 설정에서 앞서 정의된 것과 같은 메모리 제한과 요구사항을 설정할 수 있습니다. 이런 방법으로 **가용 머신**이 필요로하는 메모리와 클러스터에 있는 가용 머신들을 염두에 두고 **컨테이너를 복제**할 수 있습니다. |
|||
|
|||
만약 여러분의 어플리케이션이 **단순**하다면, 이것은 **문제가 되지 않을** 것이고, 고정된 메모리 제한을 구체화할 필요도 없을 것입니다. 하지만 여러분의 어플리케이션이 (예를 들어 **머신 러닝** 모델같이) **많은 메모리를 소요한다면**, 어플리케이션이 얼마나 많은 양의 메모리를 사용하는지 확인하고 **각 머신에서** 사용하는 **컨테이너의 수**를 조정할 필요가 있습니다 (그리고 필요에 따라 여러분의 클러스터에 머신을 추가할 수 있습니다). |
|||
|
|||
만약 여러분이 **컨테이너 당 여러개의 프로세스**를 실행한다면 (예를 들어 공식 도커 이미지 처럼), 여러분은 시작된 프로세스 개수가 가용한 것 보다 **더 많은 메모리를 소비**하지 않는지 확인해야 합니다. |
|||
|
|||
## 시작하기 전 단계들과 컨테이너 |
|||
|
|||
만약 여러분이 컨테이너(예를 들어 도커, 쿠버네티스)를 사용한다면, 여러분이 접근할 수 있는 주요 방법은 크게 두가지가 있습니다. |
|||
|
|||
### 다중 컨테이너 |
|||
|
|||
만약 여러분이 **여러개의 컨테이너**를 가지고 있다면, 아마도 각각의 컨테이너는 **하나의 프로세스**를 가지고 있을 것입니다(예를 들어, **쿠버네티스** 클러스터에서). 그러면 여러분은 복제된 워커 컨테이너를 실행하기 **이전에**, 하나의 컨테이너에 있는 **이전의 단계들을** 수행하는 단일 프로세스를 가지는 **별도의 컨테이너들**을 가지고 싶을 것입니다. |
|||
|
|||
!!! 정보 |
|||
만약 여러분이 쿠버네티스를 사용하고 있다면, 아마도 이는 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>일 것입니다. |
|||
|
|||
만약 여러분의 이용 사례에서 이전 단계들을 **병렬적으로 여러번** 수행하는데에 문제가 없다면 (예를 들어 데이터베이스 이전을 실행하지 않고 데이터베이스가 준비되었는지 확인만 하는 경우), 메인 프로세스를 시작하기 전에 이 단계들을 각 컨테이너에 넣을 수 있습니다. |
|||
|
|||
### 단일 컨테이너 |
|||
|
|||
만약 여러분의 셋업이 **다중 프로세스**(또는 하나의 프로세스)를 시작하는 **하나의 컨테이너**를 가지는 단순한 셋업이라면, 사전 단계들을 앱을 포함하는 프로세스를 시작하기 직전에 같은 컨테이너에서 실행할 수 있습니다. 공식 도커 이미지는 이를 내부적으로 지원합니다. |
|||
|
|||
## Gunicorn과 함께하는 공식 도커 이미지 - Uvicorn |
|||
|
|||
앞 챕터에서 자세하게 설명된 것 처럼, Uvicorn 워커와 같이 실행되는 Gunicorn을 포함하는 공식 도커 이미지가 있습니다: [서버 워커 - Uvicorn과 함께하는 Gunicorn](./server-workers.md){.internal-link target=_blank}. |
|||
|
|||
이 이미지는 주로 위에서 설명된 상황에서 유용할 것입니다: [다중 프로세스를 가지는 컨테이너와 특수한 경우들](#containers-with-multiple-processes-and-special-cases). |
|||
|
|||
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. |
|||
|
|||
!!! 경고 |
|||
여러분이 이 베이스 이미지 또는 다른 유사한 이미지를 필요로 하지 **않을** 높은 가능성이 있으며, [위에서 설명된 것처럼: FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi) 처음부터 이미지를 빌드하는 것이 더 나을 수 있습니다. |
|||
|
|||
이 이미지는 가능한 CPU 코어에 기반한 **몇개의 워커 프로세스**를 설정하는 **자동-튜닝** 메커니즘을 포함하고 있습니다. |
|||
|
|||
이 이미지는 **민감한 디폴트** 값을 가지고 있지만, 여러분들은 여전히 **환경 변수** 또는 설정 파일을 통해 설정값을 수정하고 업데이트 할 수 있습니다. |
|||
|
|||
또한 스크립트를 통해 <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**시작하기 전 사전 단계**</a>를 실행하는 것을 지원합니다. |
|||
|
|||
!!! 팁 |
|||
모든 설정과 옵션을 보려면, 도커 이미지 페이지로 이동합니다: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. |
|||
|
|||
### 공식 도커 이미지에 있는 프로세스 개수 |
|||
|
|||
이 이미지에 있는 **프로세스 개수**는 가용한 CPU **코어들**로 부터 **자동으로 계산**됩니다. |
|||
|
|||
이것이 의미하는 바는 이미지가 CPU로부터 **최대한의 성능**을 **쥐어짜낸다**는 것입니다. |
|||
|
|||
여러분은 이 설정 값을 **환경 변수**나 기타 방법들로 조정할 수 있습니다. |
|||
|
|||
그러나 프로세스의 개수가 컨테이너가 실행되고 있는 CPU에 의존한다는 것은 또한 **소요되는 메모리의 크기** 또한 이에 의존한다는 것을 의미합니다. |
|||
|
|||
그렇기 때문에, 만약 여러분의 어플리케이션이 많은 메모리를 요구하고 (예를 들어 머신러닝 모델처럼), 여러분의 서버가 CPU 코어 수는 많지만 **적은 메모리**를 가지고 있다면, 여러분의 컨테이너는 가용한 메모리보다 많은 메모리를 사용하려고 시도할 수 있으며, 결국 퍼포먼스를 크게 떨어뜨릴 수 있습니다(심지어 고장이 날 수도 있습니다). 🚨 |
|||
|
|||
### `Dockerfile` 생성하기 |
|||
|
|||
이 이미지에 기반해 `Dockerfile`을 생성하는 방법은 다음과 같습니다: |
|||
|
|||
```Dockerfile |
|||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 |
|||
|
|||
COPY ./requirements.txt /app/requirements.txt |
|||
|
|||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt |
|||
|
|||
COPY ./app /app |
|||
``` |
|||
|
|||
### 더 큰 어플리케이션 |
|||
|
|||
만약 여러분이 [다중 파일을 가지는 더 큰 어플리케이션](../tutorial/bigger-applications.md){.internal-link target=_blank}을 생성하는 섹션을 따랐다면, 여러분의 `Dockerfile`은 대신 이렇게 생겼을 것입니다: |
|||
|
|||
```Dockerfile hl_lines="7" |
|||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 |
|||
|
|||
COPY ./requirements.txt /app/requirements.txt |
|||
|
|||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt |
|||
|
|||
COPY ./app /app/app |
|||
``` |
|||
|
|||
### 언제 사용할까 |
|||
|
|||
여러분들이 **쿠버네티스**(또는 유사한 다른 도구) 사용하거나 클러스터 레벨에서 다중 컨테이너를 이용해 이미 **사본**을 설정하고 있다면, 공식 베이스 이미지(또는 유사한 다른 이미지)를 사용하지 **않는** 것 좋습니다. 그런 경우에 여러분은 다음에 설명된 것 처럼 **처음부터 이미지를 빌드하는 것**이 더 낫습니다: [FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi). |
|||
|
|||
이 이미지는 위의 [다중 프로세스를 가지는 컨테이너와 특수한 경우들](#containers-with-multiple-processes-and-special-cases)에서 설명된 특수한 경우에 대해서만 주로 유용할 것입니다. 예를 들어, 만약 여러분의 어플리케이션이 **충분히 단순**해서 CPU에 기반한 디폴트 프로세스 개수를 설정하는 것이 잘 작동한다면, 클러스터 레벨에서 수동으로 사본을 설정할 필요가 없을 것이고, 여러분의 앱에서 하나 이상의 컨테이너를 실행하지도 않을 것입니다. 또는 만약에 여러분이 **도커 컴포즈**로 배포하거나, 단일 서버에서 실행하거나 하는 경우에도 마찬가지입니다. |
|||
|
|||
## 컨테이너 이미지 배포하기 |
|||
|
|||
컨테이너 (도커) 이미지를 완성한 뒤에 이를 배포하는 방법에는 여러가지 방법이 있습니다. |
|||
|
|||
예를 들어: |
|||
|
|||
* 단일 서버에서 **도커 컴포즈**로 배포하기 |
|||
* **쿠버네티스** 클러스터로 배포하기 |
|||
* 도커 스왐 모드 클러스터로 배포하기 |
|||
* 노마드 같은 다른 도구로 배포하기 |
|||
* 여러분의 컨테이너 이미지를 배포해주는 클라우드 서비스로 배포하기 |
|||
|
|||
## Poetry의 도커 이미지 |
|||
|
|||
만약 여러분들이 프로젝트 의존성을 관리하기 위해 <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a>를 사용한다면, 도커의 멀티-스테이지 빌딩을 사용할 수 있습니다: |
|||
|
|||
```{ .dockerfile .annotate } |
|||
# (1) |
|||
FROM python:3.9 as requirements-stage |
|||
|
|||
# (2) |
|||
WORKDIR /tmp |
|||
|
|||
# (3) |
|||
RUN pip install poetry |
|||
|
|||
# (4) |
|||
COPY ./pyproject.toml ./poetry.lock* /tmp/ |
|||
|
|||
# (5) |
|||
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes |
|||
|
|||
# (6) |
|||
FROM python:3.9 |
|||
|
|||
# (7) |
|||
WORKDIR /code |
|||
|
|||
# (8) |
|||
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt |
|||
|
|||
# (9) |
|||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
|||
|
|||
# (10) |
|||
COPY ./app /code/app |
|||
|
|||
# (11) |
|||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
1. 첫 스테이지로, `requirements-stage`라고 이름 붙였습니다. |
|||
|
|||
2. `/tmp`를 현재의 워킹 디렉터리로 설정합니다. |
|||
|
|||
이 위치에 우리는 `requirements.txt` 파일을 생성할 것입니다. |
|||
|
|||
3. 이 도커 스테이지에서 Poetry를 설치합니다. |
|||
|
|||
4. 파일 `pyproject.toml`와 `poetry.lock`를 `/tmp` 디렉터리로 복사합니다. |
|||
|
|||
`./poetry.lock*` (`*`로 끝나는) 파일을 사용하기 때문에, 파일이 아직 사용가능하지 않더라도 고장나지 않을 것입니다. |
|||
|
|||
5. `requirements.txt` 파일을 생성합니다. |
|||
|
|||
6. 이것이 마지막 스테이지로, 여기에 위치한 모든 것이 마지막 컨테이너 이미지에 포함될 것입니다. |
|||
|
|||
7. 현재의 워킹 디렉터리를 `/code`로 설정합니다. |
|||
|
|||
8. 파일 `requirements.txt`를 `/code` 디렉터리로 복사합니다. |
|||
|
|||
이 파일은 오직 이전의 도커 스테이지에만 존재하며, 때문에 복사하기 위해서 `--from-requirements-stage` 옵션이 필요합니다. |
|||
|
|||
9. 생성된 `requirements.txt` 파일에 패키지 의존성을 설치합니다. |
|||
|
|||
10. `app` 디렉터리를 `/code` 디렉터리로 복사합니다. |
|||
|
|||
11. `uvicorn` 커맨드를 실행하여, `app.main`에서 불러온 `app` 객체를 사용하도록 합니다. |
|||
|
|||
!!! 팁 |
|||
버블 숫자를 클릭해 각 줄이 하는 일을 알아볼 수 있습니다. |
|||
|
|||
**도커 스테이지**란 `Dockefile`의 일부로서 나중에 사용하기 위한 파일들을 생성하기 위한 **일시적인 컨테이너 이미지**로 작동합니다. |
|||
|
|||
첫 스테이지는 오직 **Poetry를 설치**하고 Poetry의 `pyproject.toml` 파일로부터 프로젝트 의존성을 위한 **`requirements.txt`를 생성**하기 위해 사용됩니다. |
|||
|
|||
이 `requirements.txt` 파일은 **다음 스테이지**에서 `pip`로 사용될 것입니다. |
|||
|
|||
마지막 컨테이너 이미지에는 **오직 마지막 스테이지만** 보존됩니다. 이전 스테이지(들)은 버려집니다. |
|||
|
|||
Poetry를 사용할 때 **도커 멀티-스테이지 빌드**를 사용하는 것이 좋은데, 여러분들의 프로젝트 의존성을 설치하기 위해 마지막 컨테이너 이미지에 **오직** `requirements.txt` 파일만 필요하지, Poetry와 그 의존성은 있을 필요가 없기 때문입니다. |
|||
|
|||
이 다음 (또한 마지막) 스테이지에서 여러분들은 이전에 설명된 것과 비슷한 방식으로 방식으로 이미지를 빌드할 수 있습니다. |
|||
|
|||
### TLS 종료 프록시의 배후 - Poetry |
|||
|
|||
이전에 언급한 것과 같이, 만약 여러분이 컨테이너를 Nginx 또는 Traefik과 같은 TLS 종료 프록시 (로드 밸런서) 뒤에서 실행하고 있다면, 커맨드에 `--proxy-headers` 옵션을 추가합니다: |
|||
|
|||
```Dockerfile |
|||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
## 요약 |
|||
|
|||
컨테이너 시스템(예를 들어 **도커**나 **쿠버네티스**)을 사용하여 모든 **배포 개념**을 다루는 것은 꽤 간단합니다: |
|||
|
|||
* HTTPS |
|||
* 구동하기 |
|||
* 재시작 |
|||
* 복제 (실행 중인 프로세스 개수) |
|||
* 메모리 |
|||
* 시작하기 전 단계들 |
|||
|
|||
대부분의 경우에서 여러분은 어떤 베이스 이미지도 사용하지 않고 공식 파이썬 도커 이미지에 기반해 **처음부터 컨테이너 이미지를 빌드**할 것입니다. |
|||
|
|||
`Dockerfile`에 있는 지시 사항을 **순서대로** 다루고 **도커 캐시**를 사용하는 것으로 여러분은 **빌드 시간을 최소화**할 수 있으며, 이로써 생산성을 최대화할 수 있습니다 (그리고 지루함을 피할 수 있죠) 😎 |
|||
|
|||
특별한 경우에는, FastAPI를 위한 공식 도커 이미지를 사용할 수도 있습니다. 🤓 |
@ -0,0 +1,21 @@ |
|||
# 배포하기 - 들어가면서 |
|||
|
|||
**FastAPI**을 배포하는 것은 비교적 쉽습니다. |
|||
|
|||
## 배포의 의미 |
|||
|
|||
**배포**란 애플리케이션을 **사용자가 사용**할 수 있도록 하는 데 필요한 단계를 수행하는 것을 의미합니다. |
|||
|
|||
**웹 API**의 경우, 일반적으로 **사용자**가 중단이나 오류 없이 애플리케이션에 효율적으로 **접근**할 수 있도록 좋은 성능, 안정성 등을 제공하는 **서버 프로그램과** 함께 **원격 시스템**에 이를 설치하는 작업을 의미합니다. |
|||
|
|||
이는 지속적으로 코드를 변경하고, 지우고, 수정하고, 개발 서버를 중지했다가 다시 시작하는 등의 **개발** 단계와 대조됩니다. |
|||
|
|||
## 배포 전략 |
|||
|
|||
사용하는 도구나 특정 사례에 따라 여러 가지 방법이 있습니다. |
|||
|
|||
배포도구들을 사용하여 직접 **서버에 배포**하거나, 배포작업의 일부를 수행하는 **클라우드 서비스** 또는 다른 방법을 사용할 수도 있습니다. |
|||
|
|||
**FastAPI** 애플리케이션을 배포할 때 선택할 수 있는 몇 가지 주요 방법을 보여 드리겠습니다 (대부분 다른 유형의 웹 애플리케이션에도 적용됩니다). |
|||
|
|||
다음 차례에 자세한 내용과 이를 위한 몇 가지 기술을 볼 수 있습니다. ✨ |
@ -0,0 +1,180 @@ |
|||
# 서버 워커 - 구니콘과 유비콘 |
|||
|
|||
전단계에서의 배포 개념들을 다시 확인해보겠습니다: |
|||
|
|||
* 보안 - HTTPS |
|||
* 서버 시작과 동시에 실행하기 |
|||
* 재시작 |
|||
* **복제본 (실행 중인 프로세스의 숫자)** |
|||
* 메모리 |
|||
* 시작하기 전의 여러 단계들 |
|||
|
|||
지금까지 문서의 모든 튜토리얼을 참고하여 **단일 프로세스**로 Uvicorn과 같은 **서버 프로그램**을 실행했을 것입니다. |
|||
|
|||
애플리케이션을 배포할 때 **다중 코어**를 활용하고 더 많은 요청을 처리할 수 있도록 **프로세스 복제본**이 필요합니다. |
|||
|
|||
전 과정이었던 [배포 개념들](./concepts.md){.internal-link target=_blank}에서 본 것처럼 여러가지 방법이 존재합니다. |
|||
|
|||
지금부터 <a href="https://gunicorn.org/" class="external-link" target="_blank">**구니콘**</a>을 **유비콘 워커 프로세스**와 함께 사용하는 방법을 알려드리겠습니다. |
|||
|
|||
!!! 정보 |
|||
만약 도커와 쿠버네티스 같은 컨테이너를 사용하고 있다면 다음 챕터 [FastAPI와 컨테이너 - 도커](./docker.md){.internal-link target=_blank}에서 더 많은 정보를 얻을 수 있습니다. |
|||
|
|||
특히, 쿠버네티스에서 실행할 때는 구니콘을 사용하지 않고 대신 컨테이너당 하나의 유비콘 프로세스를 실행하는 것이 좋습니다. 이 장의 뒷부분에서 설명하겠습니다. |
|||
|
|||
## 구니콘과 유비콘 워커 |
|||
|
|||
**Gunicorn**은 **WSGI 표준**을 주로 사용하는 애플리케이션 서버입니다. 이것은 구니콘이 플라스크와 쟝고와 같은 애플리케이션을 제공할 수 있다는 것을 의미합니다. 구니콘 자체는 최신 **<a href="https://asgi.readthedocs.io/en/latest/" class="external-link" target="_blank">ASGI 표준</a>**을 사용하기 때문에 FastAPI와 호환되지 않습니다. |
|||
|
|||
하지만 구니콘은 **프로세스 관리자**역할을 하고 사용자에게 특정 **워커 프로세스 클래스**를 알려줍니다. 그런 다음 구니콘은 해당 클래스를 사용하여 하나 이상의 **워커 프로세스**를 시작합니다. |
|||
|
|||
그리고 **유비콘**은 **구니콘과 호환되는 워커 클래스**가 있습니다. |
|||
|
|||
이 조합을 사용하여 구니콘은 **프로세스 관리자** 역할을 하며 **포트**와 **IP**를 관찰하고, **유비콘 클래스**를 실행하는 워커 프로세스로 통신 정보를 **전송**합니다. |
|||
|
|||
그리고 나서 구니콘과 호환되는 **유비콘 워커** 클래스는 구니콘이 보낸 데이터를 FastAPI에서 사용하기 위한 ASGI 표준으로 변환하는 일을 담당합니다. |
|||
|
|||
## 구니콘과 유비콘 설치하기 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "uvicorn[standard]" gunicorn |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
이 명령어는 유비콘 `standard` 추가 패키지(좋은 성능을 위한)와 구니콘을 설치할 것입니다. |
|||
|
|||
## 구니콘을 유비콘 워커와 함께 실행하기 |
|||
|
|||
설치 후 구니콘 실행하기: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80 |
|||
|
|||
[19499] [INFO] Starting gunicorn 20.1.0 |
|||
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499) |
|||
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker |
|||
[19511] [INFO] Booting worker with pid: 19511 |
|||
[19513] [INFO] Booting worker with pid: 19513 |
|||
[19514] [INFO] Booting worker with pid: 19514 |
|||
[19515] [INFO] Booting worker with pid: 19515 |
|||
[19511] [INFO] Started server process [19511] |
|||
[19511] [INFO] Waiting for application startup. |
|||
[19511] [INFO] Application startup complete. |
|||
[19513] [INFO] Started server process [19513] |
|||
[19513] [INFO] Waiting for application startup. |
|||
[19513] [INFO] Application startup complete. |
|||
[19514] [INFO] Started server process [19514] |
|||
[19514] [INFO] Waiting for application startup. |
|||
[19514] [INFO] Application startup complete. |
|||
[19515] [INFO] Started server process [19515] |
|||
[19515] [INFO] Waiting for application startup. |
|||
[19515] [INFO] Application startup complete. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
각 옵션이 무엇을 의미하는지 살펴봅시다: |
|||
|
|||
* 이것은 유비콘과 똑같은 문법입니다. `main`은 파이썬 모듈 네임 "`main`"을 의미하므로 `main.py`파일을 뜻합니다. 그리고 `app`은 **FastAPI** 어플리케이션이 들어 있는 변수의 이름입니다. |
|||
* `main:app`이 파이썬의 `import` 문법과 흡사한 면이 있다는 걸 알 수 있습니다: |
|||
|
|||
```Python |
|||
from main import app |
|||
``` |
|||
|
|||
* 곧, `main:app`안에 있는 콜론의 의미는 파이썬에서 `from main import app`에서의 `import`와 같습니다. |
|||
* `--workers`: 사용할 워커 프로세스의 개수이며 숫자만큼의 유비콘 워커를 실행합니다. 이 예제에서는 4개의 워커를 실행합니다. |
|||
* `--worker-class`: 워커 프로세스에서 사용하기 위한 구니콘과 호환되는 워커클래스. |
|||
* 이런식으로 구니콘이 import하여 사용할 수 있는 클래스를 전달해줍니다: |
|||
|
|||
```Python |
|||
import uvicorn.workers.UvicornWorker |
|||
``` |
|||
|
|||
* `--bind`: 구니콘이 관찰할 IP와 포트를 의미합니다. 콜론 (`:`)을 사용하여 IP와 포트를 구분합니다. |
|||
* 만약에 `--bind 0.0.0.0:80` (구니콘 옵션) 대신 유비콘을 직접 실행하고 싶다면 `--host 0.0.0.0`과 `--port 80`을 사용해야 합니다. |
|||
|
|||
출력에서 각 프로세스에 대한 **PID** (process ID)를 확인할 수 있습니다. (단순한 숫자입니다) |
|||
|
|||
출력 내용: |
|||
|
|||
* 구니콘 **프로세스 매니저**는 PID `19499`로 실행됩니다. (직접 실행할 경우 숫자가 다를 수 있습니다) |
|||
* 다음으로 `Listening at: http://0.0.0.0:80`을 시작합니다. |
|||
* 그런 다음 사용해야할 `uvicorn.workers.UvicornWorker`의 워커클래스를 탐지합니다. |
|||
* 그리고 PID `19511`, `19513`, `19514`, 그리고 `19515`를 가진 **4개의 워커**를 실행합니다. |
|||
|
|||
|
|||
또한 구니콘은 워커의 수를 유지하기 위해 **죽은 프로세스**를 관리하고 **재시작**하는 작업을 책임집니다. 이것은 이번 장 상단 목록의 **재시작** 개념을 부분적으로 도와주는 것입니다. |
|||
|
|||
그럼에도 불구하고 필요할 경우 외부에서 **구니콘을 재시작**하고, 혹은 **서버를 시작할 때 실행**할 수 있도록 하고 싶어할 것입니다. |
|||
|
|||
## 유비콘과 워커 |
|||
|
|||
유비콘은 몇 개의 **워커 프로세스**와 함께 실행할 수 있는 선택지가 있습니다. |
|||
|
|||
그럼에도 불구하고, 유비콘은 워커 프로세스를 다루는 데에 있어서 구니콘보다 더 제한적입니다. 따라서 이 수준(파이썬 수준)의 프로세스 관리자를 사용하려면 구니콘을 프로세스 관리자로 사용하는 것이 좋습니다. |
|||
|
|||
보통 이렇게 실행할 수 있습니다: |
|||
|
|||
<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> |
|||
|
|||
새로운 옵션인 `--workers`은 유비콘에게 4개의 워커 프로세스를 사용한다고 알려줍니다. |
|||
|
|||
각 프로세스의 **PID**를 확인할 수 있습니다. `27365`는 상위 프로세스(**프로세스 매니저**), 그리고 각각의 워커프로세스는 `27368`, `27369`, `27370`, 그리고 `27367`입니다. |
|||
|
|||
## 배포 개념들 |
|||
|
|||
여기에서는 **유비콘 워커 프로세스**를 관리하는 **구니콘**(또는 유비콘)을 사용하여 애플리케이션을 **병렬화**하고, CPU **멀티 코어**의 장점을 활용하고, **더 많은 요청**을 처리할 수 있는 방법을 살펴보았습니다. |
|||
|
|||
워커를 사용하는 것은 배포 개념 목록에서 주로 **복제본** 부분과 **재시작**에 약간 도움이 되지만 다른 배포 개념들도 다루어야 합니다: |
|||
|
|||
* **보안 - HTTPS** |
|||
* **서버 시작과 동시에 실행하기** |
|||
* ***재시작*** |
|||
* 복제본 (실행 중인 프로세스의 숫자) |
|||
* **메모리** |
|||
* **시작하기 전의 여러 단계들** |
|||
|
|||
|
|||
## 컨테이너와 도커 |
|||
|
|||
다음 장인 [FastAPI와 컨테이너 - 도커](./docker.md){.internal-link target=_blank}에서 다른 **배포 개념들**을 다루는 전략들을 알려드리겠습니다. |
|||
|
|||
또한 간단한 케이스에서 사용할 수 있는, **구니콘과 유비콘 워커**가 포함돼 있는 **공식 도커 이미지**와 함께 몇 가지 기본 구성을 보여드리겠습니다. |
|||
|
|||
그리고 단일 유비콘 프로세스(구니콘 없이)를 실행할 수 있도록 **사용자 자신의 이미지를 처음부터 구축**하는 방법도 보여드리겠습니다. 이는 간단한 과정이며, **쿠버네티스**와 같은 분산 컨테이너 관리 시스템을 사용할 때 수행할 작업입니다. |
|||
|
|||
## 요약 |
|||
|
|||
당신은 **구니콘**(또는 유비콘)을 유비콘 워커와 함께 프로세스 관리자로 사용하여 **멀티-코어 CPU**를 활용하는 **멀티 프로세스를 병렬로 실행**할 수 있습니다. |
|||
|
|||
다른 배포 개념을 직접 다루면서 **자신만의 배포 시스템**을 구성하는 경우 이러한 도구와 개념들을 활용할 수 있습니다. |
|||
|
|||
다음 장에서 컨테이너(예: 도커 및 쿠버네티스)와 함께하는 **FastAPI**에 대해 배워보세요. 이러한 툴에는 다른 **배포 개념**들을 간단히 해결할 수 있는 방법이 있습니다. ✨ |
@ -0,0 +1,5 @@ |
|||
# 배우기 |
|||
|
|||
여기 **FastAPI**를 배우기 위한 입문 자료와 자습서가 있습니다. |
|||
|
|||
여러분은 FastAPI를 배우기 위해 **책**, **강의**, **공식 자료** 그리고 추천받은 방법을 고려할 수 있습니다. 😎 |
@ -0,0 +1,315 @@ |
|||
# 파이썬 타입 소개 |
|||
|
|||
파이썬은 선택적으로 "타입 힌트(type hints)"를 지원합니다. |
|||
|
|||
이러한 **타입 힌트**들은 변수의 <abbr title="예를 들면: str, int, float, bool">타입</abbr>을 선언할 수 있게 해주는 특수한 구문입니다. |
|||
|
|||
변수의 타입을 지정하면 에디터와 툴이 더 많은 도움을 줄 수 있게 됩니다. |
|||
|
|||
이 문서는 파이썬 타입 힌트에 대한 **빠른 자습서 / 내용환기** 수준의 문서입니다. 여기서는 **FastAPI**를 쓰기 위한 최소한의 내용만을 다룹니다. |
|||
|
|||
**FastAPI**는 타입 힌트에 기반을 두고 있으며, 이는 많은 장점과 이익이 있습니다. |
|||
|
|||
비록 **FastAPI**를 쓰지 않는다고 하더라도, 조금이라도 알아두면 도움이 될 것입니다. |
|||
|
|||
!!! note "참고" |
|||
파이썬에 능숙하셔서 타입 힌트에 대해 모두 아신다면, 다음 챕터로 건너뛰세요. |
|||
|
|||
## 동기 부여 |
|||
|
|||
간단한 예제부터 시작해봅시다: |
|||
|
|||
```Python |
|||
{!../../../docs_src/python_types/tutorial001.py!} |
|||
``` |
|||
|
|||
이 프로그램을 실행한 결과값: |
|||
|
|||
``` |
|||
John Doe |
|||
``` |
|||
|
|||
함수는 아래와 같이 실행됩니다: |
|||
|
|||
* `first_name`과 `last_name`를 받습니다. |
|||
* `title()`로 각 첫 문자를 대문자로 변환시킵니다. |
|||
* 두 단어를 중간에 공백을 두고 <abbr title="두 개를 하나로 차례차례 이어지게 하다">연결</abbr>합니다. |
|||
|
|||
```Python hl_lines="2" |
|||
{!../../../docs_src/python_types/tutorial001.py!} |
|||
``` |
|||
|
|||
### 코드 수정 |
|||
|
|||
이건 매우 간단한 프로그램입니다. |
|||
|
|||
그런데 처음부터 작성한다고 생각을 해봅시다. |
|||
|
|||
여러분은 매개변수를 준비했고, 함수를 정의하기 시작했을 겁니다. |
|||
|
|||
이때 "첫 글자를 대문자로 바꾸는 함수"를 호출해야 합니다. |
|||
|
|||
`upper`였나? 아니면 `uppercase`? `first_uppercase`? `capitalize`? |
|||
|
|||
그때 개발자들의 오랜 친구, 에디터 자동완성을 시도해봅니다. |
|||
|
|||
당신은 `first_name`를 입력한 뒤 점(`.`)을 입력하고 자동완성을 켜기 위해서 `Ctrl+Space`를 눌렀습니다. |
|||
|
|||
하지만 슬프게도 아무런 도움이 되지 않습니다: |
|||
|
|||
<img src="/img/python-types/image01.png"> |
|||
|
|||
### 타입 추가하기 |
|||
|
|||
이전 버전에서 한 줄만 수정해봅시다. |
|||
|
|||
저희는 이 함수의 매개변수 부분: |
|||
|
|||
```Python |
|||
first_name, last_name |
|||
``` |
|||
|
|||
을 아래와 같이 바꿀 겁니다: |
|||
|
|||
```Python |
|||
first_name: str, last_name: str |
|||
``` |
|||
|
|||
이게 다입니다. |
|||
|
|||
이게 "타입 힌트"입니다: |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial002.py!} |
|||
``` |
|||
|
|||
타입힌트는 다음과 같이 기본 값을 선언하는 것과는 다릅니다: |
|||
|
|||
```Python |
|||
first_name="john", last_name="doe" |
|||
``` |
|||
|
|||
이는 다른 것입니다. |
|||
|
|||
등호(`=`) 대신 콜론(`:`)을 쓰고 있습니다. |
|||
|
|||
일반적으로 타입힌트를 추가한다고 해서 특별하게 어떤 일이 일어나지도 않습니다. |
|||
|
|||
그렇지만 이제, 다시 함수를 만드는 도중이라고 생각해봅시다. 다만 이번엔 타입 힌트가 있습니다. |
|||
|
|||
같은 상황에서 `Ctrl+Space`로 자동완성을 작동시키면, |
|||
|
|||
<img src="/img/python-types/image02.png"> |
|||
|
|||
아래와 같이 "그렇지!"하는 옵션이 나올때까지 스크롤을 내려서 볼 수 있습니다: |
|||
|
|||
<img src="/img/python-types/image03.png"> |
|||
|
|||
## 더 큰 동기부여 |
|||
|
|||
아래 함수를 보면, 이미 타입 힌트가 적용되어 있는 걸 볼 수 있습니다: |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial003.py!} |
|||
``` |
|||
|
|||
편집기가 변수의 타입을 알고 있기 때문에, 자동완성 뿐 아니라 에러도 확인할 수 있습니다: |
|||
|
|||
<img src="/img/python-types/image04.png"> |
|||
|
|||
이제 고쳐야하는 걸 알기 때문에, `age`를 `str(age)`과 같이 문자열로 바꾸게 됩니다: |
|||
|
|||
```Python hl_lines="2" |
|||
{!../../../docs_src/python_types/tutorial004.py!} |
|||
``` |
|||
|
|||
## 타입 선언 |
|||
|
|||
방금 함수의 매개변수로써 타입 힌트를 선언하는 주요 장소를 보았습니다. |
|||
|
|||
이 위치는 여러분이 **FastAPI**와 함께 이를 사용하는 주요 장소입니다. |
|||
|
|||
### Simple 타입 |
|||
|
|||
`str`뿐 아니라 모든 파이썬 표준 타입을 선언할 수 있습니다. |
|||
|
|||
예를 들면: |
|||
|
|||
* `int` |
|||
* `float` |
|||
* `bool` |
|||
* `bytes` |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial005.py!} |
|||
``` |
|||
|
|||
### 타입 매개변수를 활용한 Generic(제네릭) 타입 |
|||
|
|||
`dict`, `list`, `set`, `tuple`과 같은 값을 저장할 수 있는 데이터 구조가 있고, 내부의 값은 각자의 타입을 가질 수도 있습니다. |
|||
|
|||
타입과 내부 타입을 선언하기 위해서는 파이썬 표준 모듈인 `typing`을 이용해야 합니다. |
|||
|
|||
구체적으로는 아래 타입 힌트를 지원합니다. |
|||
|
|||
#### `List` |
|||
|
|||
예를 들면, `str`의 `list`인 변수를 정의해봅시다. |
|||
|
|||
`typing`에서 `List`(대문자 `L`)를 import 합니다. |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
콜론(`:`) 문법을 이용하여 변수를 선언합니다. |
|||
|
|||
타입으로는 `List`를 넣어줍니다. |
|||
|
|||
이때 배열은 내부 타입을 포함하는 타입이기 때문에 대괄호 안에 넣어줍니다. |
|||
|
|||
```Python hl_lines="4" |
|||
{!../../../docs_src/python_types/tutorial006.py!} |
|||
``` |
|||
|
|||
!!! tip "팁" |
|||
대괄호 안의 내부 타입은 "타입 매개변수(type paramters)"라고 합니다. |
|||
|
|||
이번 예제에서는 `str`이 `List`에 들어간 타입 매개변수 입니다. |
|||
|
|||
이는 "`items`은 `list`인데, 배열에 들어있는 아이템 각각은 `str`이다"라는 뜻입니다. |
|||
|
|||
이렇게 함으로써, 에디터는 배열에 들어있는 아이템을 처리할때도 도움을 줄 수 있게 됩니다: |
|||
|
|||
<img src="/img/python-types/image05.png"> |
|||
|
|||
타입이 없으면 이건 거의 불가능이나 다름 없습니다. |
|||
|
|||
변수 `item`은 `items`의 개별 요소라는 사실을 알아두세요. |
|||
|
|||
그리고 에디터는 계속 `str`라는 사실을 알고 도와줍니다. |
|||
|
|||
#### `Tuple`과 `Set` |
|||
|
|||
`tuple`과 `set`도 동일하게 선언할 수 있습니다. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial007.py!} |
|||
``` |
|||
|
|||
이 뜻은 아래와 같습니다: |
|||
|
|||
* 변수 `items_t`는, 차례대로 `int`, `int`, `str`인 `tuple`이다. |
|||
* 변수 `items_s`는, 각 아이템이 `bytes`인 `set`이다. |
|||
|
|||
#### `Dict` |
|||
|
|||
`dict`를 선언하려면 컴마로 구분된 2개의 파라미터가 필요합니다. |
|||
|
|||
첫 번째 매개변수는 `dict`의 키(key)이고, |
|||
|
|||
두 번째 매개변수는 `dict`의 값(value)입니다. |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial008.py!} |
|||
``` |
|||
|
|||
이 뜻은 아래와 같습니다: |
|||
|
|||
* 변수 `prices`는 `dict`이다: |
|||
* `dict`의 키(key)는 `str`타입이다. (각 아이템의 이름(name)) |
|||
* `dict`의 값(value)는 `float`타입이다. (각 아이템의 가격(price)) |
|||
|
|||
#### `Optional` |
|||
|
|||
`str`과 같이 타입을 선언할 때 `Optional`을 쓸 수도 있는데, "선택적(Optional)"이기때문에 `None`도 될 수 있습니다: |
|||
|
|||
```Python hl_lines="1 4" |
|||
{!../../../docs_src/python_types/tutorial009.py!} |
|||
``` |
|||
|
|||
`Optional[str]`을 `str` 대신 쓰게 되면, 특정 값이 실제로는 `None`이 될 수도 있는데 항상 `str`이라고 가정하는 상황에서 에디터가 에러를 찾게 도와줄 수 있습니다. |
|||
|
|||
#### Generic(제네릭) 타입 |
|||
|
|||
이 타입은 대괄호 안에 매개변수를 가지며, 종류는: |
|||
|
|||
* `List` |
|||
* `Tuple` |
|||
* `Set` |
|||
* `Dict` |
|||
* `Optional` |
|||
* ...등등 |
|||
|
|||
위와 같은 타입은 **Generic(제네릭) 타입** 혹은 **Generics(제네릭스)**라고 불립니다. |
|||
|
|||
### 타입으로서의 클래스 |
|||
|
|||
변수의 타입으로 클래스를 선언할 수도 있습니다. |
|||
|
|||
이름(name)을 가진 `Person` 클래스가 있다고 해봅시다. |
|||
|
|||
```Python hl_lines="1-3" |
|||
{!../../../docs_src/python_types/tutorial010.py!} |
|||
``` |
|||
|
|||
그렇게 하면 변수를 `Person`이라고 선언할 수 있게 됩니다. |
|||
|
|||
```Python hl_lines="6" |
|||
{!../../../docs_src/python_types/tutorial010.py!} |
|||
``` |
|||
|
|||
그리고 역시나 모든 에디터 도움을 받게 되겠죠. |
|||
|
|||
<img src="/img/python-types/image06.png"> |
|||
|
|||
## Pydantic 모델 |
|||
|
|||
<a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a>은 데이터 검증(Validation)을 위한 파이썬 라이브러리입니다. |
|||
|
|||
당신은 속성들을 포함한 클래스 형태로 "모양(shape)"을 선언할 수 있습니다. |
|||
|
|||
그리고 각 속성은 타입을 가지고 있습니다. |
|||
|
|||
이 클래스를 활용하여서 값을 가지고 있는 인스턴스를 만들게 되면, 필요한 경우에는 적당한 타입으로 변환까지 시키기도 하여 데이터가 포함된 객체를 반환합니다. |
|||
|
|||
그리고 결과 객체에 대해서는 에디터의 도움을 받을 수 있게 됩니다. |
|||
|
|||
Pydantic 공식 문서 예시: |
|||
|
|||
```Python |
|||
{!../../../docs_src/python_types/tutorial011.py!} |
|||
``` |
|||
|
|||
!!! info "정보" |
|||
Pydantic<에 대해 더 배우고 싶다면 <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">공식 문서</a>를 참고하세요.</a> |
|||
|
|||
|
|||
**FastAPI**는 모두 Pydantic을 기반으로 되어 있습니다. |
|||
|
|||
이 모든 것이 실제로 어떻게 사용되는지에 대해서는 [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank} 에서 더 많이 확인하실 수 있습니다. |
|||
|
|||
## **FastAPI**에서의 타입 힌트 |
|||
|
|||
**FastAPI**는 여러 부분에서 타입 힌트의 장점을 취하고 있습니다. |
|||
|
|||
**FastAPI**에서 타입 힌트와 함께 매개변수를 선언하면 장점은: |
|||
|
|||
* **에디터 도움**. |
|||
* **타입 확인**. |
|||
|
|||
...그리고 **FastAPI**는 같은 정의를 아래에도 적용합니다: |
|||
|
|||
* **요구사항 정의**: 요청 경로 매개변수, 쿼리 매개변수, 헤더, 바디, 의존성 등. |
|||
* **데이터 변환**: 요청에서 요구한 타입으로. |
|||
* **데이터 검증**: 각 요청마다: |
|||
* 데이터가 유효하지 않은 경우에는 **자동으로 에러**를 발생합니다. |
|||
* OpenAPI를 활용한 **API 문서화**: |
|||
* 자동으로 상호작용하는 유저 인터페이스에 쓰이게 됩니다. |
|||
|
|||
위 내용이 다소 추상적일 수도 있지만, 걱정마세요. [자습서 - 사용자 안내서](tutorial/index.md){.internal-link target=_blank}에서 전부 확인 가능합니다. |
|||
|
|||
가장 중요한 건, 표준 파이썬 타입을 한 곳에서(클래스를 더하거나, 데코레이터 사용하는 대신) 사용함으로써 **FastAPI**가 당신을 위해 많은 일을 해준다는 사실이죠. |
|||
|
|||
!!! info "정보" |
|||
만약 모든 자습서를 다 보았음에도 타입에 대해서 더 보고자 방문한 경우에는 <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">`mypy`에서 제공하는 "cheat sheet"</a>이 좋은 자료가 될 겁니다. |
@ -0,0 +1,170 @@ |
|||
# 본문 - 다중 매개변수 |
|||
|
|||
지금부터 `Path`와 `Query`를 어떻게 사용하는지 확인하겠습니다. |
|||
|
|||
요청 본문 선언에 대한 심화 사용법을 알아보겠습니다. |
|||
|
|||
## `Path`, `Query` 및 본문 매개변수 혼합 |
|||
|
|||
당연하게 `Path`, `Query` 및 요청 본문 매개변수 선언을 자유롭게 혼합해서 사용할 수 있고, **FastAPI**는 어떤 동작을 할지 압니다. |
|||
|
|||
또한, 기본 값을 `None`으로 설정해 본문 매개변수를 선택사항으로 선언할 수 있습니다. |
|||
|
|||
```Python hl_lines="19-21" |
|||
{!../../../docs_src/body_multiple_params/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "참고" |
|||
이 경우에는 본문으로 부터 가져온 ` item`은 기본값이 `None`이기 때문에, 선택사항이라는 점을 유의해야 합니다. |
|||
|
|||
## 다중 본문 매개변수 |
|||
|
|||
이전 예제에서 보듯이, *경로 동작*은 아래와 같이 `Item` 속성을 가진 JSON 본문을 예상합니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
``` |
|||
|
|||
하지만, 다중 본문 매개변수 역시 선언할 수 있습니다. 예. `item`과 `user`: |
|||
|
|||
```Python hl_lines="22" |
|||
{!../../../docs_src/body_multiple_params/tutorial002.py!} |
|||
``` |
|||
|
|||
이 경우에, **FastAPI**는 이 함수 안에 한 개 이상의 본문 매개변수(Pydantic 모델인 두 매개변수)가 있다고 알 것입니다. |
|||
|
|||
그래서, 본문의 매개변수 이름을 키(필드 명)로 사용할 수 있고, 다음과 같은 본문을 예측합니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
}, |
|||
"user": { |
|||
"username": "dave", |
|||
"full_name": "Dave Grohl" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
!!! note "참고" |
|||
이전과 같이 `item`이 선언 되었더라도, 본문 내의 `item` 키가 있을 것이라고 예측합니다. |
|||
|
|||
FastAPI는 요청을 자동으로 변환해, 매개변수의 `item`과 `user`를 특별한 내용으로 받도록 할 것입니다. |
|||
|
|||
복합 데이터의 검증을 수행하고 OpenAPI 스키마 및 자동 문서를 문서화합니다. |
|||
|
|||
## 본문 내의 단일 값 |
|||
|
|||
쿼리 및 경로 매개변수에 대한 추가 데이터를 정의하는 `Query`와 `Path`와 같이, **FastAPI**는 동등한 `Body`를 제공합니다. |
|||
|
|||
예를 들어 이전의 모델을 확장하면, `item`과 `user`와 동일한 본문에 또 다른 `importance`라는 키를 갖도록 할 수있습니다. |
|||
|
|||
단일 값을 그대로 선언한다면, **FastAPI**는 쿼리 매개변수로 가정할 것입니다. |
|||
|
|||
하지만, **FastAPI**의 `Body`를 사용해 다른 본문 키로 처리하도록 제어할 수 있습니다: |
|||
|
|||
|
|||
```Python hl_lines="23" |
|||
{!../../../docs_src/body_multiple_params/tutorial003.py!} |
|||
``` |
|||
|
|||
이 경우에는 **FastAPI**는 본문을 이와 같이 예측할 것입니다: |
|||
|
|||
|
|||
```JSON |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
}, |
|||
"user": { |
|||
"username": "dave", |
|||
"full_name": "Dave Grohl" |
|||
}, |
|||
"importance": 5 |
|||
} |
|||
``` |
|||
|
|||
다시 말해, 데이터 타입, 검증, 문서 등을 변환합니다. |
|||
|
|||
## 다중 본문 매개변수와 쿼리 |
|||
|
|||
당연히, 필요할 때마다 추가적인 쿼리 매개변수를 선언할 수 있고, 이는 본문 매개변수에 추가됩니다. |
|||
|
|||
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요가 없고, 아래처럼 할 수 있습니다: |
|||
|
|||
```Python hl_lines="27" |
|||
{!../../../docs_src/body_multiple_params/tutorial004.py!} |
|||
``` |
|||
|
|||
이렇게: |
|||
|
|||
```Python |
|||
q: Optional[str] = None |
|||
``` |
|||
|
|||
!!! info "정보" |
|||
`Body` 또한 `Query`, `Path` 그리고 이후에 볼 다른 것들처럼 동일한 추가 검증과 메타데이터 매개변수를 갖고 있습니다. |
|||
|
|||
## 단일 본문 매개변수 삽입하기 |
|||
|
|||
Pydantic 모델 `Item`의 `item`을 본문 매개변수로 오직 한개만 갖고있다고 하겠습니다. |
|||
|
|||
기본적으로 **FastAPI**는 직접 본문으로 예측할 것입니다. |
|||
|
|||
하지만, 만약 모델 내용에 `item `키를 가진 JSON으로 예측하길 원한다면, 추가적인 본문 매개변수를 선언한 것처럼 `Body`의 특별한 매개변수인 `embed`를 사용할 수 있습니다: |
|||
|
|||
```Python hl_lines="17" |
|||
{!../../../docs_src/body_multiple_params/tutorial005.py!} |
|||
``` |
|||
|
|||
아래 처럼: |
|||
|
|||
```Python |
|||
item: Item = Body(..., embed=True) |
|||
``` |
|||
|
|||
이 경우에 **FastAPI**는 본문을 아래 대신에: |
|||
|
|||
```JSON hl_lines="2" |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
``` |
|||
|
|||
아래 처럼 예측할 것 입니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## 정리 |
|||
|
|||
요청이 단 한개의 본문을 가지고 있더라도, *경로 동작 함수*로 다중 본문 매개변수를 추가할 수 있습니다. |
|||
|
|||
하지만, **FastAPI**는 이를 처리하고, 함수에 올바른 데이터를 제공하며, *경로 동작*으로 올바른 스키마를 검증하고 문서화 합니다. |
|||
|
|||
또한, 단일 값을 본문의 일부로 받도록 선언할 수 있습니다. |
|||
|
|||
그리고 **FastAPI**는 단 한개의 매개변수가 선언 되더라도, 본문 내의 키로 삽입 시킬 수 있습니다. |
@ -0,0 +1,243 @@ |
|||
# 본문 - 중첩 모델 |
|||
|
|||
**FastAPI**를 이용하면 (Pydantic 덕분에) 단독으로 깊이 중첩된 모델을 정의, 검증, 문서화하며 사용할 수 있습니다. |
|||
## 리스트 필드 |
|||
|
|||
어트리뷰트를 서브타입으로 정의할 수 있습니다. 예를 들어 파이썬 `list`는: |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/body_nested_models/tutorial001.py!} |
|||
``` |
|||
|
|||
이는 `tags`를 항목 리스트로 만듭니다. 각 항목의 타입을 선언하지 않더라도요. |
|||
|
|||
## 타입 매개변수가 있는 리스트 필드 |
|||
|
|||
하지만 파이썬은 내부의 타입이나 "타입 매개변수"를 선언할 수 있는 특정 방법이 있습니다: |
|||
|
|||
### typing의 `List` 임포트 |
|||
|
|||
먼저, 파이썬 표준 `typing` 모듈에서 `List`를 임포트합니다: |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
### 타입 매개변수로 `List` 선언 |
|||
|
|||
`list`, `dict`, `tuple`과 같은 타입 매개변수(내부 타입)를 갖는 타입을 선언하려면: |
|||
|
|||
* `typing` 모듈에서 임포트 |
|||
* 대괄호를 사용하여 "타입 매개변수"로 내부 타입 전달: `[` 및 `]` |
|||
|
|||
```Python |
|||
from typing import List |
|||
|
|||
my_list: List[str] |
|||
``` |
|||
|
|||
이 모든 것은 타입 선언을 위한 표준 파이썬 문법입니다. |
|||
|
|||
내부 타입을 갖는 모델 어트리뷰트에 대해 동일한 표준 문법을 사용하세요. |
|||
|
|||
마찬가지로 예제에서 `tags`를 구체적으로 "문자열의 리스트"로 만들 수 있습니다: |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
## 집합 타입 |
|||
|
|||
그런데 생각해보니 태그는 반복되면 안 돼고, 고유한(Unique) 문자열이어야 할 것 같습니다. |
|||
|
|||
그리고 파이썬은 집합을 위한 특별한 데이터 타입 `set`이 있습니다. |
|||
|
|||
그렇다면 `Set`을 임포트 하고 `tags`를 `str`의 `set`으로 선언할 수 있습니다: |
|||
|
|||
```Python hl_lines="1 14" |
|||
{!../../../docs_src/body_nested_models/tutorial003.py!} |
|||
``` |
|||
|
|||
덕분에 중복 데이터가 있는 요청을 수신하더라도 고유한 항목들의 집합으로 변환됩니다. |
|||
|
|||
그리고 해당 데이터를 출력 할 때마다 소스에 중복이 있더라도 고유한 항목들의 집합으로 출력됩니다. |
|||
|
|||
또한 그에 따라 주석이 생기고 문서화됩니다. |
|||
|
|||
## 중첩 모델 |
|||
|
|||
Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다. |
|||
|
|||
그런데 해당 타입 자체로 또다른 Pydantic 모델의 타입이 될 수 있습니다. |
|||
|
|||
그러므로 특정한 어트리뷰트의 이름, 타입, 검증을 사용하여 깊게 중첩된 JSON "객체"를 선언할 수 있습니다. |
|||
|
|||
모든 것이 단독으로 중첩됩니다. |
|||
|
|||
### 서브모델 정의 |
|||
|
|||
예를 들어, `Image` 모델을 선언할 수 있습니다: |
|||
|
|||
```Python hl_lines="9-11" |
|||
{!../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
### 서브모듈을 타입으로 사용 |
|||
|
|||
그리고 어트리뷰트의 타입으로 사용할 수 있습니다: |
|||
|
|||
```Python hl_lines="20" |
|||
{!../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
이는 **FastAPI**가 다음과 유사한 본문을 기대한다는 것을 의미합니다: |
|||
|
|||
```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" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
다시 한번, **FastAPI**를 사용하여 해당 선언을 함으로써 얻는 것은: |
|||
|
|||
* 중첩 모델도 편집기 지원(자동완성 등) |
|||
* 데이터 변환 |
|||
* 데이터 검증 |
|||
* 자동 문서화 |
|||
|
|||
## 특별한 타입과 검증 |
|||
|
|||
`str`, `int`, `float` 등과 같은 단일 타입과는 별개로, `str`을 상속하는 더 복잡한 단일 타입을 사용할 수 있습니다. |
|||
|
|||
모든 옵션을 보려면, <a href="https://pydantic-docs.helpmanual.io/usage/types/" class="external-link" target="_blank">Pydantic's exotic types</a> 문서를 확인하세요. 다음 장에서 몇가지 예제를 볼 수 있습니다. |
|||
|
|||
예를 들어 `Image` 모델 안에 `url` 필드를 `str` 대신 Pydantic의 `HttpUrl`로 선언할 수 있습니다: |
|||
|
|||
```Python hl_lines="4 10" |
|||
{!../../../docs_src/body_nested_models/tutorial005.py!} |
|||
``` |
|||
|
|||
이 문자열이 유효한 URL인지 검사하고 JSON 스키마/OpenAPI로 문서화 됩니다. |
|||
|
|||
## 서브모델 리스트를 갖는 어트리뷰트 |
|||
|
|||
`list`, `set` 등의 서브타입으로 Pydantic 모델을 사용할 수도 있습니다: |
|||
|
|||
```Python hl_lines="20" |
|||
{!../../../docs_src/body_nested_models/tutorial006.py!} |
|||
``` |
|||
|
|||
아래와 같은 JSON 본문으로 예상(변환, 검증, 문서화 등을)합니다: |
|||
|
|||
```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 "정보" |
|||
`images` 키가 어떻게 이미지 객체 리스트를 갖는지 주목하세요. |
|||
|
|||
## 깊게 중첩된 모델 |
|||
|
|||
단독으로 깊게 중첩된 모델을 정의할 수 있습니다: |
|||
|
|||
```Python hl_lines="9 14 20 23 27" |
|||
{!../../../docs_src/body_nested_models/tutorial007.py!} |
|||
``` |
|||
|
|||
!!! info "정보" |
|||
`Offer`가 선택사항 `Image` 리스트를 차례로 갖는 `Item` 리스트를 어떻게 가지고 있는지 주목하세요 |
|||
|
|||
## 순수 리스트의 본문 |
|||
|
|||
예상되는 JSON 본문의 최상위 값이 JSON `array`(파이썬 `list`)면, Pydantic 모델에서와 마찬가지로 함수의 매개변수에서 타입을 선언할 수 있습니다: |
|||
|
|||
```Python |
|||
images: List[Image] |
|||
``` |
|||
|
|||
이를 아래처럼: |
|||
|
|||
```Python hl_lines="15" |
|||
{!../../../docs_src/body_nested_models/tutorial008.py!} |
|||
``` |
|||
|
|||
## 어디서나 편집기 지원 |
|||
|
|||
그리고 어디서나 편집기 지원을 받을수 있습니다. |
|||
|
|||
리스트 내부 항목의 경우에도: |
|||
|
|||
<img src="/img/tutorial/body-nested-models/image01.png"> |
|||
|
|||
Pydantic 모델 대신에 `dict`를 직접 사용하여 작업할 경우, 이러한 편집기 지원을 받을수 없습니다. |
|||
|
|||
하지만 수신한 딕셔너리가 자동으로 변환되고 출력도 자동으로 JSON으로 변환되므로 걱정할 필요는 없습니다. |
|||
|
|||
## 단독 `dict`의 본문 |
|||
|
|||
일부 타입의 키와 다른 타입의 값을 사용하여 `dict`로 본문을 선언할 수 있습니다. |
|||
|
|||
(Pydantic을 사용한 경우처럼) 유효한 필드/어트리뷰트 이름이 무엇인지 알 필요가 없습니다. |
|||
|
|||
아직 모르는 키를 받으려는 경우 유용합니다. |
|||
|
|||
--- |
|||
|
|||
다른 유용한 경우는 다른 타입의 키를 가질 때입니다. 예. `int`. |
|||
|
|||
여기서 그 경우를 볼 것입니다. |
|||
|
|||
이 경우, `float` 값을 가진 `int` 키가 있는 모든 `dict`를 받아들입니다: |
|||
|
|||
```Python hl_lines="15" |
|||
{!../../../docs_src/body_nested_models/tutorial009.py!} |
|||
``` |
|||
|
|||
!!! tip "팁" |
|||
JSON은 오직 `str`형 키만 지원한다는 것을 염두에 두세요. |
|||
|
|||
하지만 Pydantic은 자동 데이터 변환이 있습니다. |
|||
|
|||
즉, API 클라이언트가 문자열을 키로 보내더라도 해당 문자열이 순수한 정수를 포함하는한 Pydantic은 이를 변환하고 검증합니다. |
|||
|
|||
그러므로 `weights`로 받은 `dict`는 실제로 `int` 키와 `float` 값을 가집니다. |
|||
|
|||
## 요약 |
|||
|
|||
**FastAPI**를 사용하면 Pydantic 모델이 제공하는 최대 유연성을 확보하면서 코드를 간단하고 짧게, 그리고 우아하게 유지할 수 있습니다. |
|||
|
|||
물론 아래의 이점도 있습니다: |
|||
|
|||
* 편집기 지원 (자동완성이 어디서나!) |
|||
* 데이터 변환 (일명 파싱/직렬화) |
|||
* 데이터 검증 |
|||
* 스키마 문서화 |
|||
* 자동 문서 |
@ -0,0 +1,97 @@ |
|||
# 경로 동작 설정 |
|||
|
|||
*경로 작동 데코레이터*를 설정하기 위해서 전달할수 있는 몇 가지 매개변수가 있습니다. |
|||
|
|||
!!! warning "경고" |
|||
아래 매개변수들은 *경로 작동 함수*가 아닌 *경로 작동 데코레이터*에 직접 전달된다는 사실을 기억하십시오. |
|||
|
|||
## 응답 상태 코드 |
|||
|
|||
*경로 작동*의 응답에 사용될 (HTTP) `status_code`를 정의할수 있습니다. |
|||
|
|||
`404`와 같은 `int`형 코드를 직접 전달할수 있습니다. |
|||
|
|||
하지만 각 코드의 의미를 모른다면, `status`에 있는 단축 상수들을 사용할수 있습니다: |
|||
|
|||
```Python hl_lines="3 17" |
|||
{!../../../docs_src/path_operation_configuration/tutorial001.py!} |
|||
``` |
|||
|
|||
각 상태 코드들은 응답에 사용되며, OpenAPI 스키마에 추가됩니다. |
|||
|
|||
!!! note "기술적 세부사항" |
|||
다음과 같이 임포트하셔도 좋습니다. `from starlette import status`. |
|||
|
|||
**FastAPI**는 개발자 여러분의 편의를 위해서 `starlette.status`와 동일한 `fastapi.status`를 제공합니다. 하지만 Starlette에서 직접 온 것입니다. |
|||
|
|||
## 태그 |
|||
|
|||
(보통 단일 `str`인) `str`로 구성된 `list`와 함께 매개변수 `tags`를 전달하여, `경로 작동`에 태그를 추가할 수 있습니다: |
|||
|
|||
```Python hl_lines="17 22 27" |
|||
{!../../../docs_src/path_operation_configuration/tutorial002.py!} |
|||
``` |
|||
|
|||
전달된 태그들은 OpenAPI의 스키마에 추가되며, 자동 문서 인터페이스에서 사용됩니다: |
|||
|
|||
<img src="/img/tutorial/path-operation-configuration/image01.png"> |
|||
|
|||
## 요약과 기술 |
|||
|
|||
`summary`와 `description`을 추가할 수 있습니다: |
|||
|
|||
```Python hl_lines="20-21" |
|||
{!../../../docs_src/path_operation_configuration/tutorial003.py!} |
|||
``` |
|||
|
|||
## 독스트링으로 만든 기술 |
|||
|
|||
설명은 보통 길어지고 여러 줄에 걸쳐있기 때문에, *경로 작동* 기술을 함수 <abbr title="함수안에 있는 첫번째 표현식으로, 문서로 사용될 여러 줄에 걸친 (변수에 할당되지 않은) 문자열"> 독스트링</abbr> 에 선언할 수 있습니다, 이를 **FastAPI**가 독스트링으로부터 읽습니다. |
|||
|
|||
<a href="https://ko.wikipedia.org/wiki/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4" class="external-link" target="_blank">마크다운</a> 문법으로 독스트링을 작성할 수 있습니다, 작성된 마크다운 형식의 독스트링은 (마크다운의 들여쓰기를 고려하여) 올바르게 화면에 출력됩니다. |
|||
|
|||
```Python hl_lines="19-27" |
|||
{!../../../docs_src/path_operation_configuration/tutorial004.py!} |
|||
``` |
|||
|
|||
이는 대화형 문서에서 사용됩니다: |
|||
|
|||
<img src="/img/tutorial/path-operation-configuration/image02.png"> |
|||
|
|||
## 응답 기술 |
|||
|
|||
`response_description` 매개변수로 응답에 관한 설명을 명시할 수 있습니다: |
|||
|
|||
```Python hl_lines="21" |
|||
{!../../../docs_src/path_operation_configuration/tutorial005.py!} |
|||
``` |
|||
|
|||
!!! info "정보" |
|||
`response_description`은 구체적으로 응답을 지칭하며, `description`은 일반적인 *경로 작동*을 지칭합니다. |
|||
|
|||
!!! check "확인" |
|||
OpenAPI는 각 *경로 작동*이 응답에 관한 설명을 요구할 것을 명시합니다. |
|||
|
|||
따라서, 응답에 관한 설명이 없을경우, **FastAPI**가 자동으로 "성공 응답" 중 하나를 생성합니다. |
|||
|
|||
<img src="/img/tutorial/path-operation-configuration/image03.png"> |
|||
|
|||
## 단일 *경로 작동* 지원중단 |
|||
|
|||
단일 *경로 작동*을 없애지 않고 <abbr title="구식, 사용하지 않는것이 권장됨">지원중단</abbr>을 해야한다면, `deprecated` 매개변수를 전달하면 됩니다. |
|||
|
|||
```Python hl_lines="16" |
|||
{!../../../docs_src/path_operation_configuration/tutorial006.py!} |
|||
``` |
|||
|
|||
대화형 문서에 지원중단이라고 표시됩니다. |
|||
|
|||
<img src="/img/tutorial/path-operation-configuration/image04.png"> |
|||
|
|||
지원중단된 경우와 지원중단 되지 않은 경우에 대한 *경로 작동*이 어떻게 보이는 지 확인하십시오. |
|||
|
|||
<img src="/img/tutorial/path-operation-configuration/image05.png"> |
|||
|
|||
## 정리 |
|||
|
|||
*경로 작동 데코레이터*에 매개변수(들)를 전달함으로 *경로 작동*을 설정하고 메타데이터를 추가할수 있습니다. |
@ -0,0 +1,303 @@ |
|||
# 쿼리 매개변수와 문자열 검증 |
|||
|
|||
**FastAPI**를 사용하면 매개변수에 대한 추가 정보 및 검증을 선언할 수 있습니다. |
|||
|
|||
이 응용 프로그램을 예로 들어보겠습니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params_str_validations/tutorial001.py!} |
|||
``` |
|||
|
|||
쿼리 매개변수 `q`는 `Optional[str]` 자료형입니다. 즉, `str` 자료형이지만 `None` 역시 될 수 있음을 뜻하고, 실제로 기본값은 `None`이기 때문에 FastAPI는 이 매개변수가 필수가 아니라는 것을 압니다. |
|||
|
|||
!!! note "참고" |
|||
FastAPI는 `q`의 기본값이 `= None`이기 때문에 필수가 아님을 압니다. |
|||
|
|||
`Optional[str]`에 있는 `Optional`은 FastAPI가 사용하는게 아니지만, 편집기에게 더 나은 지원과 오류 탐지를 제공하게 해줍니다. |
|||
|
|||
## 추가 검증 |
|||
|
|||
`q`가 선택적이지만 값이 주어질 때마다 **값이 50 글자를 초과하지 않게** 강제하려 합니다. |
|||
|
|||
### `Query` 임포트 |
|||
|
|||
이를 위해 먼저 `fastapi`에서 `Query`를 임포트합니다: |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/query_params_str_validations/tutorial002.py!} |
|||
``` |
|||
|
|||
## 기본값으로 `Query` 사용 |
|||
|
|||
이제 `Query`를 매개변수의 기본값으로 사용하여 `max_length` 매개변수를 50으로 설정합니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params_str_validations/tutorial002.py!} |
|||
``` |
|||
|
|||
기본값 `None`을 `Query(None)`으로 바꿔야 하므로, `Query`의 첫 번째 매개변수는 기본값을 정의하는 것과 같은 목적으로 사용됩니다. |
|||
|
|||
그러므로: |
|||
|
|||
```Python |
|||
q: Optional[str] = Query(None) |
|||
``` |
|||
|
|||
...위 코드는 아래와 동일하게 매개변수를 선택적으로 만듭니다: |
|||
|
|||
```Python |
|||
q: Optional[str] = None |
|||
``` |
|||
|
|||
하지만 명시적으로 쿼리 매개변수를 선언합니다. |
|||
|
|||
!!! info "정보" |
|||
FastAPI는 다음 부분에 관심이 있습니다: |
|||
|
|||
```Python |
|||
= None |
|||
``` |
|||
|
|||
또는: |
|||
|
|||
```Python |
|||
= Query(None) |
|||
``` |
|||
|
|||
그리고 `None`을 사용하여 쿼라 매개변수가 필수적이지 않다는 것을 파악합니다. |
|||
|
|||
`Optional` 부분은 편집기에게 더 나은 지원을 제공하기 위해서만 사용됩니다. |
|||
|
|||
또한 `Query`로 더 많은 매개변수를 전달할 수 있습니다. 지금의 경우 문자열에 적용되는 `max_length` 매개변수입니다: |
|||
|
|||
```Python |
|||
q: str = Query(None, max_length=50) |
|||
``` |
|||
|
|||
이는 데이터를 검증할 것이고, 데이터가 유효하지 않다면 명백한 오류를 보여주며, OpenAPI 스키마 *경로 동작*에 매개변수를 문서화 합니다. |
|||
|
|||
## 검증 추가 |
|||
|
|||
매개변수 `min_length` 또한 추가할 수 있습니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params_str_validations/tutorial003.py!} |
|||
``` |
|||
|
|||
## 정규식 추가 |
|||
|
|||
매개변수와 일치해야 하는 <abbr title="정규표현식(regular expression), regex 또는 regexp는 문자열 조회 패턴을 정의하는 문자들의 순열입니다">정규표현식</abbr>을 정의할 수 있습니다: |
|||
|
|||
```Python hl_lines="10" |
|||
{!../../../docs_src/query_params_str_validations/tutorial004.py!} |
|||
``` |
|||
|
|||
이 특정 정규표현식은 전달 받은 매개변수 값을 검사합니다: |
|||
|
|||
* `^`: 이전에 문자가 없고 뒤따르는 문자로 시작합니다. |
|||
* `fixedquery`: 정확히 `fixedquery` 값을 갖습니다. |
|||
* `$`: 여기서 끝나고 `fixedquery` 이후로 아무 문자도 갖지 않습니다. |
|||
|
|||
**"정규표현식"** 개념에 대해 상실감을 느꼈다면 걱정하지 않아도 됩니다. 많은 사람에게 어려운 주제입니다. 아직은 정규표현식 없이도 많은 작업들을 할 수 있습니다. |
|||
|
|||
하지만 언제든지 가서 배울수 있고, **FastAPI**에서 직접 사용할 수 있다는 사실을 알고 있어야 합니다. |
|||
|
|||
## 기본값 |
|||
|
|||
기본값으로 사용하는 첫 번째 인자로 `None`을 전달하듯이, 다른 값을 전달할 수 있습니다. |
|||
|
|||
`min_length`가 `3`이고, 기본값이 `"fixedquery"`인 쿼리 매개변수 `q`를 선언해봅시다: |
|||
|
|||
```Python hl_lines="7" |
|||
{!../../../docs_src/query_params_str_validations/tutorial005.py!} |
|||
``` |
|||
|
|||
!!! note "참고" |
|||
기본값을 갖는 것만으로 매개변수는 선택적이 됩니다. |
|||
|
|||
## 필수로 만들기 |
|||
|
|||
더 많은 검증이나 메타데이터를 선언할 필요가 없는 경우, 다음과 같이 기본값을 선언하지 않고 쿼리 매개변수 `q`를 필수로 만들 수 있습니다: |
|||
|
|||
```Python |
|||
q: str |
|||
``` |
|||
|
|||
아래 대신: |
|||
|
|||
```Python |
|||
q: Optional[str] = None |
|||
``` |
|||
|
|||
그러나 이제 다음과 같이 `Query`로 선언합니다: |
|||
|
|||
```Python |
|||
q: Optional[str] = Query(None, min_length=3) |
|||
``` |
|||
|
|||
그래서 `Query`를 필수값으로 만들어야 할 때면, 첫 번째 인자로 `...`를 사용할 수 있습니다: |
|||
|
|||
```Python hl_lines="7" |
|||
{!../../../docs_src/query_params_str_validations/tutorial006.py!} |
|||
``` |
|||
|
|||
!!! info "정보" |
|||
이전에 `...`를 본적이 없다면: 특별한 단일값으로, <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">파이썬의 일부이며 "Ellipsis"라 부릅니다</a>. |
|||
|
|||
이렇게 하면 **FastAPI**가 이 매개변수는 필수임을 알 수 있습니다. |
|||
|
|||
## 쿼리 매개변수 리스트 / 다중값 |
|||
|
|||
쿼리 매개변수를 `Query`와 함께 명시적으로 선언할 때, 값들의 리스트나 다른 방법으로 여러 값을 받도록 선언 할 수도 있습니다. |
|||
|
|||
예를 들어, URL에서 여러번 나오는 `q` 쿼리 매개변수를 선언하려면 다음과 같이 작성할 수 있습니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params_str_validations/tutorial011.py!} |
|||
``` |
|||
|
|||
아래와 같은 URL을 사용합니다: |
|||
|
|||
``` |
|||
http://localhost:8000/items/?q=foo&q=bar |
|||
``` |
|||
|
|||
여러 `q` *쿼리 매개변수* 값들을 (`foo` 및 `bar`) 파이썬 `list`로 *경로 작동 함수* 내 *함수 매개변수* `q`로 전달 받습니다. |
|||
|
|||
따라서 해당 URL에 대한 응답은 다음과 같습니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"q": [ |
|||
"foo", |
|||
"bar" |
|||
] |
|||
} |
|||
``` |
|||
|
|||
!!! tip "팁" |
|||
위의 예와 같이 `list` 자료형으로 쿼리 매개변수를 선언하려면 `Query`를 명시적으로 사용해야 합니다. 그렇지 않으면 요청 본문으로 해석됩니다. |
|||
|
|||
대화형 API 문서는 여러 값을 허용하도록 수정 됩니다: |
|||
|
|||
<img src="/img/tutorial/query-params-str-validations/image02.png"> |
|||
|
|||
### 쿼리 매개변수 리스트 / 기본값을 사용하는 다중값 |
|||
|
|||
그리고 제공된 값이 없으면 기본 `list` 값을 정의할 수도 있습니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params_str_validations/tutorial012.py!} |
|||
``` |
|||
|
|||
아래로 이동한다면: |
|||
|
|||
``` |
|||
http://localhost:8000/items/ |
|||
``` |
|||
|
|||
`q`의 기본값은: `["foo", "bar"]`이며 응답은 다음이 됩니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"q": [ |
|||
"foo", |
|||
"bar" |
|||
] |
|||
} |
|||
``` |
|||
|
|||
#### `list` 사용하기 |
|||
|
|||
`List[str]` 대신 `list`를 직접 사용할 수도 있습니다: |
|||
|
|||
```Python hl_lines="7" |
|||
{!../../../docs_src/query_params_str_validations/tutorial013.py!} |
|||
``` |
|||
|
|||
!!! note "참고" |
|||
이 경우 FastAPI는 리스트의 내용을 검사하지 않음을 명심하기 바랍니다. |
|||
|
|||
예를 들어, `List[int]`는 리스트 내용이 정수인지 검사(및 문서화)합니다. 하지만 `list` 단독일 경우는 아닙니다. |
|||
|
|||
## 더 많은 메타데이터 선언 |
|||
|
|||
매개변수에 대한 정보를 추가할 수 있습니다. |
|||
|
|||
해당 정보는 생성된 OpenAPI에 포함되고 문서 사용자 인터페이스 및 외부 도구에서 사용됩니다. |
|||
|
|||
!!! note "참고" |
|||
도구에 따라 OpenAPI 지원 수준이 다를 수 있음을 명심하기 바랍니다. |
|||
|
|||
일부는 아직 선언된 추가 정보를 모두 표시하지 않을 수 있지만, 대부분의 경우 누락된 기능은 이미 개발 계획이 있습니다. |
|||
|
|||
`title`을 추가할 수 있습니다: |
|||
|
|||
```Python hl_lines="10" |
|||
{!../../../docs_src/query_params_str_validations/tutorial007.py!} |
|||
``` |
|||
|
|||
그리고 `description`도 추가할 수 있습니다: |
|||
|
|||
```Python hl_lines="13" |
|||
{!../../../docs_src/query_params_str_validations/tutorial008.py!} |
|||
``` |
|||
|
|||
## 별칭 매개변수 |
|||
|
|||
매개변수가 `item-query`이길 원한다고 가정해 봅시다. |
|||
|
|||
마치 다음과 같습니다: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/?item-query=foobaritems |
|||
``` |
|||
|
|||
그러나 `item-query`은 유효한 파이썬 변수 이름이 아닙니다. |
|||
|
|||
가장 가까운 것은 `item_query`일 겁니다. |
|||
|
|||
하지만 정확히`item-query`이길 원합니다... |
|||
|
|||
이럴 경우 `alias`를 선언할 수 있으며, 해당 별칭은 매개변수 값을 찾는 데 사용됩니다: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params_str_validations/tutorial009.py!} |
|||
``` |
|||
|
|||
## 매개변수 사용하지 않게 하기 |
|||
|
|||
이제는 더이상 이 매개변수를 마음에 들어하지 않는다고 가정해 봅시다. |
|||
|
|||
이 매개변수를 사용하는 클라이언트가 있기 때문에 한동안은 남겨둬야 하지만, <abbr title="구식이며, 사용하지 않는 것을 추천">사용되지 않는다(deprecated)</abbr>고 확실하게 문서에서 보여주고 싶습니다. |
|||
|
|||
그렇다면 `deprecated=True` 매개변수를 `Query`로 전달합니다: |
|||
|
|||
```Python hl_lines="18" |
|||
{!../../../docs_src/query_params_str_validations/tutorial010.py!} |
|||
``` |
|||
|
|||
문서가 아래와 같이 보일겁니다: |
|||
|
|||
<img src="/img/tutorial/query-params-str-validations/image01.png"> |
|||
|
|||
## 요약 |
|||
|
|||
매개변수에 검증과 메타데이터를 추가 선언할 수 있습니다. |
|||
|
|||
제네릭 검증과 메타데이터: |
|||
|
|||
* `alias` |
|||
* `title` |
|||
* `description` |
|||
* `deprecated` |
|||
|
|||
특정 문자열 검증: |
|||
|
|||
* `min_length` |
|||
* `max_length` |
|||
* `regex` |
|||
|
|||
예제에서 `str` 값의 검증을 어떻게 추가하는지 살펴보았습니다. |
|||
|
|||
숫자와 같은 다른 자료형에 대한 검증을 어떻게 선언하는지 확인하려면 다음 장을 확인하기 바랍니다. |
@ -0,0 +1,210 @@ |
|||
# 응답 모델 |
|||
|
|||
어떤 *경로 동작*이든 매개변수 `response_model`를 사용하여 응답을 위한 모델을 선언할 수 있습니다: |
|||
|
|||
* `@app.get()` |
|||
* `@app.post()` |
|||
* `@app.put()` |
|||
* `@app.delete()` |
|||
* 기타. |
|||
|
|||
```Python hl_lines="17" |
|||
{!../../../docs_src/response_model/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "참고" |
|||
`response_model`은 "데코레이터" 메소드(`get`, `post`, 등)의 매개변수입니다. 모든 매개변수들과 본문(body)처럼 *경로 동작 함수*가 아닙니다. |
|||
|
|||
Pydantic 모델 어트리뷰트를 선언한 것과 동일한 타입을 수신하므로 Pydantic 모델이 될 수 있지만, `List[Item]`과 같이 Pydantic 모델들의 `list`일 수도 있습니다. |
|||
|
|||
FastAPI는 이 `response_model`를 사용하여: |
|||
|
|||
* 출력 데이터를 타입 선언으로 변환. |
|||
* 데이터 검증. |
|||
* OpenAPI *경로 동작*의 응답에 JSON 스키마 추가. |
|||
* 자동 생성 문서 시스템에 사용. |
|||
|
|||
하지만 가장 중요한 것은: |
|||
|
|||
* 해당 모델의 출력 데이터 제한. 이것이 얼마나 중요한지 아래에서 볼 것입니다. |
|||
|
|||
!!! note "기술 세부사항" |
|||
응답 모델은 함수의 타입 어노테이션 대신 이 매개변수로 선언하는데, 경로 함수가 실제 응답 모델을 반환하지 않고 `dict`, 데이터베이스 객체나 기타 다른 모델을 `response_model`을 사용하여 필드 제한과 직렬화를 수행하고 반환할 수 있기 때문입니다 |
|||
|
|||
## 동일한 입력 데이터 반환 |
|||
|
|||
여기서 우리는 평문 비밀번호를 포함하는 `UserIn` 모델을 선언합니다: |
|||
|
|||
```Python hl_lines="9 11" |
|||
{!../../../docs_src/response_model/tutorial002.py!} |
|||
``` |
|||
|
|||
그리고 이 모델을 사용하여 입력을 선언하고 같은 모델로 출력을 선언합니다: |
|||
|
|||
```Python hl_lines="17-18" |
|||
{!../../../docs_src/response_model/tutorial002.py!} |
|||
``` |
|||
|
|||
이제 브라우저가 비밀번호로 사용자를 만들 때마다 API는 응답으로 동일한 비밀번호를 반환합니다. |
|||
|
|||
이 경우, 사용자가 스스로 비밀번호를 발신했기 때문에 문제가 되지 않을 수 있습니다. |
|||
|
|||
그러나 동일한 모델을 다른 *경로 동작*에서 사용할 경우, 모든 클라이언트에게 사용자의 비밀번호를 발신할 수 있습니다. |
|||
|
|||
!!! danger "위험" |
|||
절대로 사용자의 평문 비밀번호를 저장하거나 응답으로 발신하지 마십시오. |
|||
|
|||
## 출력 모델 추가 |
|||
|
|||
대신 평문 비밀번호로 입력 모델을 만들고 해당 비밀번호 없이 출력 모델을 만들 수 있습니다: |
|||
|
|||
```Python hl_lines="9 11 16" |
|||
{!../../../docs_src/response_model/tutorial003.py!} |
|||
``` |
|||
|
|||
여기서 *경로 동작 함수*가 비밀번호를 포함하는 동일한 입력 사용자를 반환할지라도: |
|||
|
|||
```Python hl_lines="24" |
|||
{!../../../docs_src/response_model/tutorial003.py!} |
|||
``` |
|||
|
|||
...`response_model`을 `UserOut` 모델로 선언했기 때문에 비밀번호를 포함하지 않습니다: |
|||
|
|||
```Python hl_lines="22" |
|||
{!../../../docs_src/response_model/tutorial003.py!} |
|||
``` |
|||
|
|||
따라서 **FastAPI**는 출력 모델에서 선언하지 않은 모든 데이터를 (Pydantic을 사용하여) 필터링합니다. |
|||
|
|||
## 문서에서 보기 |
|||
|
|||
자동 생성 문서를 보면 입력 모델과 출력 모델이 각자의 JSON 스키마를 가지고 있음을 확인할 수 있습니다: |
|||
|
|||
<img src="/img/tutorial/response-model/image01.png"> |
|||
|
|||
그리고 두 모델 모두 대화형 API 문서에 사용됩니다: |
|||
|
|||
<img src="/img/tutorial/response-model/image02.png"> |
|||
|
|||
## 응답 모델 인코딩 매개변수 |
|||
|
|||
응답 모델은 아래와 같이 기본값을 가질 수 있습니다: |
|||
|
|||
```Python hl_lines="11 13-14" |
|||
{!../../../docs_src/response_model/tutorial004.py!} |
|||
``` |
|||
|
|||
* `description: Optional[str] = None`은 기본값으로 `None`을 갖습니다. |
|||
* `tax: float = 10.5`는 기본값으로 `10.5`를 갖습니다. |
|||
* `tags: List[str] = []` 빈 리스트의 기본값으로: `[]`. |
|||
|
|||
그러나 실제로 저장되지 않았을 경우 결과에서 값을 생략하고 싶을 수 있습니다. |
|||
|
|||
예를 들어, NoSQL 데이터베이스에 많은 선택적 속성이 있는 모델이 있지만, 기본값으로 가득 찬 매우 긴 JSON 응답을 보내고 싶지 않습니다. |
|||
|
|||
### `response_model_exclude_unset` 매개변수 사용 |
|||
|
|||
*경로 동작 데코레이터* 매개변수를 `response_model_exclude_unset=True`로 설정 할 수 있습니다: |
|||
|
|||
```Python hl_lines="24" |
|||
{!../../../docs_src/response_model/tutorial004.py!} |
|||
``` |
|||
|
|||
이러한 기본값은 응답에 포함되지 않고 실제로 설정된 값만 포함됩니다. |
|||
|
|||
따라서 해당 *경로 동작*에 ID가 `foo`인 항목(items)을 요청으로 보내면 (기본값을 제외한) 응답은 다음과 같습니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"price": 50.2 |
|||
} |
|||
``` |
|||
|
|||
!!! info "정보" |
|||
FastAPI는 이를 위해 Pydantic 모델의 `.dict()`의 <a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank"> `exclude_unset` 매개변수</a>를 사용합니다. |
|||
|
|||
!!! info "정보" |
|||
아래 또한 사용할 수 있습니다: |
|||
|
|||
* `response_model_exclude_defaults=True` |
|||
* `response_model_exclude_none=True` |
|||
|
|||
<a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank">Pydantic 문서</a>에서 `exclude_defaults` 및 `exclude_none`에 대해 설명한 대로 사용할 수 있습니다. |
|||
|
|||
#### 기본값이 있는 필드를 갖는 값의 데이터 |
|||
|
|||
하지만 모델의 필드가 기본값이 있어도 ID가 `bar`인 항목(items)처럼 데이터가 값을 갖는다면: |
|||
|
|||
```Python hl_lines="3 5" |
|||
{ |
|||
"name": "Bar", |
|||
"description": "The bartenders", |
|||
"price": 62, |
|||
"tax": 20.2 |
|||
} |
|||
``` |
|||
|
|||
응답에 해당 값들이 포함됩니다. |
|||
|
|||
#### 기본값과 동일한 값을 갖는 데이터 |
|||
|
|||
If the data has the same values as the default ones, like the item with ID `baz`: |
|||
ID가 `baz`인 항목(items)처럼 기본값과 동일한 값을 갖는다면: |
|||
|
|||
```Python hl_lines="3 5-6" |
|||
{ |
|||
"name": "Baz", |
|||
"description": None, |
|||
"price": 50.2, |
|||
"tax": 10.5, |
|||
"tags": [] |
|||
} |
|||
``` |
|||
|
|||
`description`, `tax` 그리고 `tags`가 기본값과 같더라도 (기본값에서 가져오는 대신) 값들이 명시적으로 설정되었다는 것을 인지할 정도로 FastAPI는 충분히 똑똑합니다(사실, Pydantic이 충분히 똑똑합니다). |
|||
|
|||
따라서 JSON 스키마에 포함됩니다. |
|||
|
|||
!!! tip "팁" |
|||
`None` 뿐만 아니라 다른 어떤 것도 기본값이 될 수 있습니다. |
|||
|
|||
리스트(`[]`), `float`인 `10.5` 등이 될 수 있습니다. |
|||
|
|||
### `response_model_include` 및 `response_model_exclude` |
|||
|
|||
*경로 동작 데코레이터* 매개변수 `response_model_include` 및 `response_model_exclude`를 사용할 수 있습니다. |
|||
|
|||
이들은 포함(나머지 생략)하거나 제외(나머지 포함) 할 어트리뷰트의 이름과 `str`의 `set`을 받습니다. |
|||
|
|||
Pydantic 모델이 하나만 있고 출력에서 일부 데이터를 제거하려는 경우 빠른 지름길로 사용할 수 있습니다. |
|||
|
|||
!!! tip "팁" |
|||
하지만 이러한 매개변수 대신 여러 클래스를 사용하여 위 아이디어를 사용하는 것을 추천합니다. |
|||
|
|||
이는 일부 어트리뷰트를 생략하기 위해 `response_model_include` 또는 `response_model_exclude`를 사용하더라도 앱의 OpenAPI(및 문서)가 생성한 JSON 스키마가 여전히 전체 모델에 대한 스키마이기 때문입니다. |
|||
|
|||
비슷하게 작동하는 `response_model_by_alias` 역시 마찬가지로 적용됩니다. |
|||
|
|||
```Python hl_lines="31 37" |
|||
{!../../../docs_src/response_model/tutorial005.py!} |
|||
``` |
|||
|
|||
!!! tip "팁" |
|||
문법 `{"name", "description"}`은 두 값을 갖는 `set`을 만듭니다. |
|||
|
|||
이는 `set(["name", "description"])`과 동일합니다. |
|||
|
|||
#### `set` 대신 `list` 사용하기 |
|||
|
|||
`list` 또는 `tuple` 대신 `set`을 사용하는 법을 잊었더라도, FastAPI는 `set`으로 변환하고 정상적으로 작동합니다: |
|||
|
|||
```Python hl_lines="31 37" |
|||
{!../../../docs_src/response_model/tutorial006.py!} |
|||
``` |
|||
|
|||
## 요약 |
|||
|
|||
응답 모델을 정의하고 개인정보가 필터되는 것을 보장하기 위해 *경로 동작 데코레이터*의 매개변수 `response_model`을 사용하세요. |
|||
|
|||
명시적으로 설정된 값만 반환하려면 `response_model_exclude_unset`을 사용하세요. |
@ -0,0 +1,40 @@ |
|||
# 정적 파일 |
|||
|
|||
'StaticFiles'를 사용하여 디렉토리에서 정적 파일을 자동으로 제공할 수 있습니다. |
|||
|
|||
## `StaticFiles` 사용 |
|||
|
|||
* `StaticFiles` 임포트합니다. |
|||
* 특정 경로에 `StaticFiles()` 인스턴스를 "마운트" 합니다. |
|||
|
|||
```Python hl_lines="2 6" |
|||
{!../../../docs_src/static_files/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "기술적 세부사항" |
|||
`from starlette.staticfiles import StaticFiles` 를 사용할 수도 있습니다. |
|||
|
|||
**FastAPI**는 단지 개발자인, 당신에게 편의를 제공하기 위해 `fastapi.static files` 와 동일한 `starlett.static files`를 제공합니다. 하지만 사실 이것은 Starlett에서 직접 온 것입니다. |
|||
|
|||
### "마운팅" 이란 |
|||
|
|||
"마운팅"은 특정 경로에 완전히 "독립적인" 애플리케이션을 추가하는 것을 의미하는데, 그 후 모든 하위 경로에 대해서도 적용됩니다. |
|||
|
|||
마운트된 응용 프로그램은 완전히 독립적이기 때문에 `APIRouter`를 사용하는 것과는 다릅니다. OpenAPI 및 응용 프로그램의 문서는 마운트된 응용 프로그램 등에서 어떤 것도 포함하지 않습니다. |
|||
|
|||
자세한 내용은 **숙련된 사용자 안내서**에서 확인할 수 있습니다. |
|||
|
|||
## 세부사항 |
|||
|
|||
첫 번째 `"/static"`은 이 "하위 응용 프로그램"이 "마운트"될 하위 경로를 가리킵니다. 따라서 `"/static"`으로 시작하는 모든 경로는 `"/static"`으로 처리됩니다. |
|||
|
|||
`'directory="static"`은 정적 파일이 들어 있는 디렉토리의 이름을 나타냅니다. |
|||
|
|||
`name="static"`은 **FastAPI**에서 내부적으로 사용할 수 있는 이름을 제공합니다. |
|||
|
|||
이 모든 매개변수는 "`static`"과 다를 수 있으며, 사용자 응용 프로그램의 요구 사항 및 구체적인 세부 정보에 따라 매개변수를 조정할 수 있습니다. |
|||
|
|||
|
|||
## 추가 정보 |
|||
|
|||
자세한 내용과 선택 사항을 보려면 <a href="https://www.starlette.io/staticfiles/" class="external-link" target="_blank">Starlette의 정적 파일에 관한 문서</a>를 확인하십시오. |
@ -0,0 +1,401 @@ |
|||
# Concurrency ve async / await |
|||
|
|||
*path operasyon fonksiyonu* için `async def `sözdizimi, asenkron kod, eşzamanlılık ve paralellik hakkında bazı ayrıntılar. |
|||
|
|||
## Aceleniz mi var? |
|||
|
|||
<abbr title="too long; didn't read"><strong>TL;DR:</strong></abbr> |
|||
|
|||
Eğer `await` ile çağrılması gerektiğini belirten üçüncü taraf kütüphaneleri kullanıyorsanız, örneğin: |
|||
|
|||
```Python |
|||
results = await some_library() |
|||
``` |
|||
|
|||
O zaman *path operasyon fonksiyonunu* `async def` ile tanımlayın örneğin: |
|||
|
|||
```Python hl_lines="2" |
|||
@app.get('/') |
|||
async def read_results(): |
|||
results = await some_library() |
|||
return results |
|||
``` |
|||
|
|||
!!! not |
|||
Sadece `async def` ile tanımlanan fonksiyonlar içinde `await` kullanabilirsiniz. |
|||
|
|||
--- |
|||
|
|||
Eğer bir veritabanı, bir API, dosya sistemi vb. ile iletişim kuran bir üçüncü taraf bir kütüphane kullanıyorsanız ve `await` kullanımını desteklemiyorsa, (bu şu anda çoğu veritabanı kütüphanesi için geçerli bir durumdur), o zaman *path operasyon fonksiyonunuzu* `def` kullanarak normal bir şekilde tanımlayın, örneğin: |
|||
|
|||
```Python hl_lines="2" |
|||
@app.get('/') |
|||
def results(): |
|||
results = some_library() |
|||
return results |
|||
``` |
|||
|
|||
--- |
|||
|
|||
Eğer uygulamanız (bir şekilde) başka bir şeyle iletişim kurmak ve onun cevap vermesini beklemek zorunda değilse, `async def` kullanın. |
|||
|
|||
--- |
|||
|
|||
Sadece bilmiyorsanız, normal `def` kullanın. |
|||
|
|||
--- |
|||
|
|||
**Not**: *path operasyon fonksiyonlarınızda* `def` ve `async def`'i ihtiyaç duyduğunuz gibi karıştırabilir ve her birini sizin için en iyi seçeneği kullanarak tanımlayabilirsiniz. FastAPI onlarla doğru olanı yapacaktır. |
|||
|
|||
Her neyse, yukarıdaki durumlardan herhangi birinde, FastAPI yine de asenkron olarak çalışacak ve son derece hızlı olacaktır. |
|||
|
|||
Ancak yukarıdaki adımları takip ederek, bazı performans optimizasyonları yapılabilecektir. |
|||
|
|||
## Teknik Detaylar |
|||
|
|||
Python'un modern versiyonlarında **`async` ve `await`** sözdizimi ile **"coroutines"** kullanan **"asenkron kod"** desteğine sahiptir. |
|||
|
|||
Bu ifadeyi aşağıdaki bölümlerde daha da ayrıntılı açıklayalım: |
|||
|
|||
* **Asenkron kod** |
|||
* **`async` ve `await`** |
|||
* **Coroutines** |
|||
|
|||
## Asenkron kod |
|||
|
|||
Asenkron kod programlama dilinin 💬 bilgisayara / programa 🤖 kodun bir noktasında, *başka bir kodun* bir yerde bitmesini 🤖 beklemesi gerektiğini söylemenin bir yoludur. Bu *başka koda* "slow-file" denir 📝. |
|||
|
|||
Böylece, bu süreçte bilgisayar "slow-file" 📝 tamamlanırken gidip başka işler yapabilir. |
|||
|
|||
Sonra bilgisayar / program 🤖 her fırsatı olduğunda o noktada yaptığı tüm işleri 🤖 bitirene kadar geri dönücek. Ve 🤖 yapması gerekeni yaparak, beklediği görevlerden herhangi birinin bitip bitmediğini görecek. |
|||
|
|||
Ardından, 🤖 bitirmek için ilk görevi alır ("slow-file" 📝) ve onunla ne yapması gerekiyorsa onu devam ettirir. |
|||
|
|||
Bu "başka bir şey için bekle" normalde, aşağıdakileri beklemek gibi (işlemcinin ve RAM belleğinin hızına kıyasla) nispeten "yavaş" olan <abbr title="Input ve Output (Giriş ve Çıkış)">I/O</abbr> işlemlerine atıfta bulunur: |
|||
|
|||
* istemci tarafından ağ üzerinden veri göndermek |
|||
* ağ üzerinden istemciye gönderilen veriler |
|||
* sistem tarafından okunacak ve programınıza verilecek bir dosya içeriği |
|||
* programınızın diske yazılmak üzere sisteme verdiği dosya içerikleri |
|||
* uzak bir API işlemi |
|||
* bir veritabanı bitirme işlemi |
|||
* sonuçları döndürmek için bir veritabanı sorgusu |
|||
* vb. |
|||
|
|||
Yürütme süresi çoğunlukla <abbr title="Input ve Output (Giriş ve Çıkış)">I/O</abbr> işlemleri beklenerek tüketildiğinden bunlara "I/O bağlantılı" işlemler denir. |
|||
|
|||
Buna "asenkron" denir, çünkü bilgisayar/program yavaş görevle "senkronize" olmak zorunda değildir, görevin tam olarak biteceği anı bekler, hiçbir şey yapmadan, görev sonucunu alabilmek ve çalışmaya devam edebilmek için . |
|||
|
|||
Bunun yerine, "asenkron" bir sistem olarak, bir kez bittiğinde, bilgisayarın / programın yapması gerekeni bitirmesi için biraz (birkaç mikrosaniye) sırada bekleyebilir ve ardından sonuçları almak için geri gelebilir ve onlarla çalışmaya devam edebilir. |
|||
|
|||
"Senkron" ("asenkron"un aksine) için genellikle "sıralı" terimini de kullanırlar, çünkü bilgisayar/program, bu adımlar beklemeyi içerse bile, farklı bir göreve geçmeden önce tüm adımları sırayla izler. |
|||
|
|||
|
|||
### Eşzamanlılık (Concurrency) ve Burgerler |
|||
|
|||
|
|||
Yukarıda açıklanan bu **asenkron** kod fikrine bazen **"eşzamanlılık"** da denir. **"Paralellikten"** farklıdır. |
|||
|
|||
**Eşzamanlılık** ve **paralellik**, "aynı anda az ya da çok olan farklı işler" ile ilgilidir. |
|||
|
|||
Ancak *eşzamanlılık* ve *paralellik* arasındaki ayrıntılar oldukça farklıdır. |
|||
|
|||
|
|||
Farkı görmek için burgerlerle ilgili aşağıdaki hikayeyi hayal edin: |
|||
|
|||
### Eşzamanlı Burgerler |
|||
|
|||
<!-- Cinsiyetten bağımsız olan aşçı emojisi "🧑🍳" tarayıcılarda yeterince iyi görüntülenmiyor. Bu yüzden erken "👨🍳" ve kadın "👩🍳" aşçıları karışık bir şekilde kullanıcağım. --> |
|||
|
|||
Aşkınla beraber 😍 dışarı hamburger yemeye çıktınız 🍔, kasiyer 💁 öndeki insanlardan sipariş alırken siz sıraya girdiniz. |
|||
|
|||
Sıra sizde ve sen aşkın 😍 ve kendin için 2 çılgın hamburger 🍔 söylüyorsun. |
|||
|
|||
Ödemeyi yaptın 💸. |
|||
|
|||
Kasiyer 💁 mutfakdaki aşçıya 👨🍳 hamburgerleri 🍔 hazırlaması gerektiğini söyler ve aşçı bunu bilir (o an önceki müşterilerin siparişlerini hazırlıyor olsa bile). |
|||
|
|||
Kasiyer 💁 size bir sıra numarası verir. |
|||
|
|||
Beklerken askınla 😍 bir masaya oturur ve uzun bir süre konuşursunuz(Burgerleriniz çok çılgın olduğundan ve hazırlanması biraz zaman alıyor ✨🍔✨). |
|||
|
|||
Hamburgeri beklerkenki zamanı 🍔, aşkının ne kadar zeki ve tatlı olduğuna hayran kalarak harcayabilirsin ✨😍✨. |
|||
|
|||
Aşkınla 😍 konuşurken arada sıranın size gelip gelmediğini kontrol ediyorsun. |
|||
|
|||
Nihayet sıra size geldi. Tezgaha gidip hamburgerleri 🍔kapıp masaya geri dönüyorsun. |
|||
|
|||
Aşkınla hamburgerlerinizi yiyor 🍔 ve iyi vakit geçiriyorsunuz ✨. |
|||
|
|||
--- |
|||
|
|||
Bu hikayedeki bilgisayar / program 🤖 olduğunuzu hayal edin. |
|||
|
|||
Sırada beklerken boştasın 😴, sıranı beklerken herhangi bir "üretim" yapmıyorsun. Ama bu sıra hızlı çünkü kasiyer sadece siparişleri alıyor (onları hazırlamıyor), burada bir sıknıtı yok. |
|||
|
|||
Sonra sıra size geldiğinde gerçekten "üretken" işler yapabilirsiniz 🤓, menüyü oku, ne istediğine larar ver, aşkının seçimini al 😍, öde 💸, doğru kartı çıkart, ödemeyi kontrol et, faturayı kontrol et, siparişin doğru olup olmadığını kontrol et, vb. |
|||
|
|||
Ama hamburgerler 🍔 hazır olmamasına rağmen Kasiyer 💁 ile işiniz "duraklıyor" ⏸, çünkü hamburgerlerin hazır olmasını bekliyoruz 🕙. |
|||
|
|||
Ama tezgahtan uzaklaşıp sıranız gelene kadarmasanıza dönebilir 🔀 ve dikkatinizi aşkınıza 😍 verebilirsiniz vr bunun üzerine "çalışabilirsiniz" ⏯ 🤓. Artık "üretken" birşey yapıyorsunuz 🤓, sevgilinle 😍 flört eder gibi. |
|||
|
|||
Kasiyer 💁 "Hamburgerler hazır !" 🍔 dediğinde ve görüntülenen numara sizin numaranız olduğunda hemen koşup hamburgerlerinizi almaya çalışmıyorsunuz. Biliyorsunuzki kimse sizin hamburgerlerinizi 🍔 çalmayacak çünkü sıra sizin. |
|||
|
|||
Yani Aşkınızın😍 hikayeyi bitirmesini bekliyorsunuz (çalışmayı bitir ⏯ / görev işleniyor.. 🤓), nazikçe gülümseyin ve hamburger yemeye gittiğinizi söyleyin ⏸. |
|||
|
|||
Ardından tezgaha 🔀, şimdi biten ilk göreve ⏯ gidin, Hamburgerleri 🍔 alın, teşekkür edin ve masaya götürün. sayacın bu adımı tamamlanır ⏹. Bu da yeni bir görev olan "hamburgerleri ye" 🔀 ⏯ görevini başlatırken "hamburgerleri al" ⏹ görevini bitirir. |
|||
|
|||
### Parallel Hamburgerler |
|||
|
|||
Şimdi bunların "Eşzamanlı Hamburger" değil, "Paralel Hamburger" olduğunu düşünelim. |
|||
|
|||
Hamburger 🍔 almak için 😍 aşkınla Paralel fast food'a gidiyorsun. |
|||
|
|||
Birden fazla kasiyer varken (varsayalım 8) sıraya girdiniz👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳 ve sıranız gelene kadar bekliyorsunuz. |
|||
|
|||
Sizden önceki herkez ayrılmadan önce hamburgerlerinin 🍔 hazır olmasını bekliyor 🕙. Çünkü kasiyerlerin her biri bir hamburger hazırlanmadan önce bir sonraki siparişe geçmiiyor. |
|||
|
|||
Sonunda senin sıran, aşkın 😍 ve kendin için 2 hamburger 🍔 siparişi verdiniz. |
|||
|
|||
Ödemeyi yaptınız 💸. |
|||
|
|||
Kasiyer mutfağa gider 👨🍳. |
|||
|
|||
Sırada bekliyorsunuz 🕙, kimse sizin burgerinizi 🍔 almaya çalışmıyor çünkü sıra sizin. |
|||
|
|||
Sen ve aşkın 😍 sıranızı korumak ve hamburgerleri almakla o kadar meşgulsünüz ki birbirinize vakit 🕙 ayıramıyorsunuz 😞. |
|||
|
|||
İşte bu "senkron" çalışmadır. Kasiyer/aşçı 👨🍳ile senkron hareket ediyorsunuz. Bu yüzden beklemek 🕙 ve kasiyer/aşçı burgeri 🍔bitirip size getirdiğinde orda olmak zorundasınız yoksa başka biri alabilir. |
|||
|
|||
Sonra kasiyeri/aşçı 👨🍳 nihayet hamburgerlerinizle 🍔, uzun bir süre sonra 🕙 tezgaha geri geliyor. |
|||
|
|||
Burgerlerinizi 🍔 al ve aşkınla masanıza doğru ilerle 😍. |
|||
|
|||
Sadece burgerini yiyorsun 🍔 ve bitti ⏹. |
|||
|
|||
Bekleyerek çok fazla zaman geçtiğinden 🕙 konuşmaya çok fazla vakit kalmadı 😞. |
|||
|
|||
--- |
|||
|
|||
Paralel burger senaryosunda ise, siz iki işlemcili birer robotsunuz 🤖 (sen ve sevgilin 😍), Beklıyorsunuz 🕙 hem konuşarak güzel vakit geçirirken ⏯ hem de sıranızı bekliyorsunuz 🕙. |
|||
|
|||
Mağazada ise 8 işlemci bulunuyor (Kasiyer/aşçı) 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳. Eşzamanlı burgerde yalnızca 2 kişi olabiliyordu (bir kasiyer ve bir aşçı) 💁 👨🍳. |
|||
|
|||
Ama yine de bu en iyisi değil 😞. |
|||
|
|||
--- |
|||
|
|||
Bu hikaye burgerler 🍔 için paralel. |
|||
|
|||
Bir gerçek hayat örneği verelim. Bir banka hayal edin. |
|||
|
|||
Bankaların çoğunda birkaç kasiyer 👨💼👨💼👨💼👨💼 ve uzun bir sıra var 🕙🕙🕙🕙🕙🕙🕙🕙. |
|||
|
|||
Tüm işi sırayla bir müşteri ile yapan tüm kasiyerler 👨💼⏯. |
|||
|
|||
Ve uzun süre kuyrukta beklemek 🕙 zorundasın yoksa sıranı kaybedersin. |
|||
|
|||
Muhtemelen ayak işlerı yaparken sevgilini 😍 bankaya 🏦 getirmezsin. |
|||
|
|||
### Burger Sonucu |
|||
|
|||
Bu "aşkınla fast food burgerleri" senaryosunda, çok fazla bekleme olduğu için 🕙, eşzamanlı bir sisteme sahip olmak çok daha mantıklı ⏸🔀⏯. |
|||
|
|||
Web uygulamalarının çoğu için durum böyledir. |
|||
|
|||
Pek çok kullanıcı var, ama sunucunuz pek de iyi olmayan bir bağlantı ile istek atmalarını bekliyor. |
|||
|
|||
Ve sonra yanıtların geri gelmesi için tekrar 🕙 bekliyor |
|||
|
|||
Bu "bekleme" 🕙 mikrosaniye cinsinden ölçülür, yine de, hepsini toplarsak çok fazla bekleme var. |
|||
|
|||
Bu nedenle, web API'leri için asenkron ⏸🔀⏯ kod kullanmak çok daha mantıklı. |
|||
|
|||
Mevcut popüler Python frameworklerinin çoğu (Flask ve Django gibi), Python'daki yeni asenkron özellikler mevcut olmadan önce yazıldı. Bu nedenle, dağıtılma biçimleri paralel yürütmeyi ve yenisi kadar güçlü olmayan eski bir eşzamansız yürütme biçimini destekler. |
|||
|
|||
Asenkron web (ASGI) özelliği, WebSockets için destek eklemek için Django'ya eklenmiş olsa da. |
|||
|
|||
Asenkron çalışabilme NodeJS in popüler olmasının sebebi (paralel olamasa bile) ve Go dilini güçlü yapan özelliktir. |
|||
|
|||
Ve bu **FastAPI** ile elde ettiğiniz performans düzeyiyle aynıdır. |
|||
|
|||
Aynı anda paralellik ve asenkronluğa sahip olabildiğiniz için, test edilen NodeJS çerçevelerinin çoğundan daha yüksek performans elde edersiniz ve C'ye daha yakın derlenmiş bir dil olan Go ile eşit bir performans elde edersiniz <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(bütün teşekkürler Starlette'e )</a>. |
|||
|
|||
### Eşzamanlılık paralellikten daha mı iyi? |
|||
|
|||
Hayır! Hikayenin ahlakı bu değil. |
|||
|
|||
Eşzamanlılık paralellikten farklıdır. Ve çok fazla bekleme içeren **belirli** senaryolarda daha iyidir. Bu nedenle, genellikle web uygulamaları için paralellikten çok daha iyidir. Ama her şey için değil. |
|||
|
|||
Yanı, bunu aklınızda oturtmak için aşağıdaki kısa hikayeyi hayal edin: |
|||
|
|||
> Büyük, kirli bir evi temizlemelisin. |
|||
|
|||
*Evet, tüm hikaye bu*. |
|||
|
|||
--- |
|||
|
|||
Beklemek yok 🕙. Hiçbir yerde. Sadece evin birden fazla yerinde yapılacak fazlasıyla iş var. |
|||
|
|||
You could have turns as in the burgers example, first the living room, then the kitchen, but as you are not waiting 🕙 for anything, just cleaning and cleaning, the turns wouldn't affect anything. |
|||
Hamburger örneğindeki gibi dönüşleriniz olabilir, önce oturma odası, sonra mutfak, ama hiçbir şey için 🕙 beklemediğinizden, sadece temizlik, temizlik ve temizlik, dönüşler hiçbir şeyi etkilemez. |
|||
|
|||
Sıralı veya sırasız (eşzamanlılık) bitirmek aynı zaman alır ve aynı miktarda işi yaparsınız. |
|||
|
|||
Ama bu durumda, 8 eski kasiyer/aşçı - yeni temizlikçiyi getirebilseydiniz 👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳👩🍳👨🍳 ve her birini (artı siz) evin bir bölgesini temizlemek için görevlendirseydiniz, ekstra yardımla tüm işleri **paralel** olarak yapabilir ve çok daha erken bitirebilirdiniz. |
|||
|
|||
Bu senaryoda, temizlikçilerin her biri (siz dahil) birer işlemci olacak ve üzerine düşeni yapacaktır. |
|||
|
|||
Yürütme süresinin çoğu (beklemek yerine) iş yapıldığından ve bilgisayardaki iş bir <abbr title="Central Processing Unit">CPU</abbr> tarafından yapıldığından, bu sorunlara "CPU bound" diyorlar". |
|||
|
|||
--- |
|||
|
|||
CPU'ya bağlı işlemlerin yaygın örnekleri, karmaşık matematik işlemleri gerektiren işlerdir. |
|||
|
|||
Örneğin: |
|||
|
|||
* **Ses** veya **görüntü işleme**. |
|||
* **Bilgisayar görüsü**: bir görüntü milyonlarca pikselden oluşur, her pikselin 3 değeri / rengi vardır, bu pikseller üzerinde aynı anda bir şeyler hesaplamayı gerektiren işleme. |
|||
* **Makine Öğrenimi**: Çok sayıda "matris" ve "vektör" çarpımı gerektirir. Sayıları olan ve hepsini aynı anda çarpan büyük bir elektronik tablo düşünün. |
|||
* **Derin Öğrenme**: Bu, Makine Öğreniminin bir alt alanıdır, dolayısıyla aynısı geçerlidir. Sadece çarpılacak tek bir sayı tablosu değil, büyük bir sayı kümesi vardır ve çoğu durumda bu modelleri oluşturmak ve/veya kullanmak için özel işlemciler kullanırsınız. |
|||
|
|||
### Eşzamanlılık + Paralellik: Web + Makine Öğrenimi |
|||
|
|||
**FastAPI** ile web geliştirme için çok yaygın olan eşzamanlılıktan yararlanabilirsiniz (NodeJS'in aynı çekiciliği). |
|||
|
|||
Ancak, Makine Öğrenimi sistemlerindekile gibi **CPU'ya bağlı** iş yükleri için paralellik ve çoklu işlemenin (birden çok işlemin paralel olarak çalışması) avantajlarından da yararlanabilirsiniz. |
|||
|
|||
Buna ek olarak Python'un **Veri Bilimi**, Makine Öğrenimi ve özellikle Derin Öğrenme için ana dil olduğu gerçeği, FastAPI'yi Veri Bilimi / Makine Öğrenimi web API'leri ve uygulamaları için çok iyi bir seçenek haline getirir. |
|||
|
|||
Production'da nasıl oldugunu görmek için şu bölüme bakın [Deployment](deployment/index.md){.internal-link target=_blank}. |
|||
|
|||
## `async` ve `await` |
|||
|
|||
Python'un modern sürümleri, asenkron kodu tanımlamanın çok sezgisel bir yoluna sahiptir. Bu, normal "sequentıal" (sıralı) kod gibi görünmesini ve doğru anlarda sizin için "awaıt" ile bekleme yapmasını sağlar. |
|||
|
|||
Sonuçları vermeden önce beklemeyi gerektirecek ve yeni Python özelliklerini destekleyen bir işlem olduğunda aşağıdaki gibi kodlayabilirsiniz: |
|||
|
|||
```Python |
|||
burgers = await get_burgers(2) |
|||
``` |
|||
|
|||
Buradaki `await` anahtari Python'a, sonuçları `burgers` degiskenine atamadan önce `get_burgers(2)` kodunun işini bitirmesini 🕙 beklemesi gerektiğini söyler. Bununla Python, bu ara zamanda başka bir şey 🔀 ⏯ yapabileceğini bilecektir (başka bir istek almak gibi). |
|||
|
|||
`await`kodunun çalışması için, eşzamansızlığı destekleyen bir fonksiyonun içinde olması gerekir. Bunu da yapmak için fonksiyonu `async def` ile tanımlamamız yeterlidir: |
|||
|
|||
```Python hl_lines="1" |
|||
async def get_burgers(number: int): |
|||
# burgerleri oluşturmak için asenkron birkaç iş |
|||
return burgers |
|||
``` |
|||
|
|||
...`def` yerine: |
|||
|
|||
```Python hl_lines="2" |
|||
# bu kod asenkron değil |
|||
def get_sequential_burgers(number: int): |
|||
# burgerleri oluşturmak için senkron bırkaç iş |
|||
return burgers |
|||
``` |
|||
|
|||
`async def` ile Python, bu fonksıyonun içinde, `await` ifadelerinin farkında olması gerektiğini ve çalışma zamanı gelmeden önce bu işlevin yürütülmesini "duraklatabileceğini" ve başka bir şey yapabileceğini 🔀 bilir. |
|||
|
|||
`async def` fonksiyonunu çağırmak istediğinizde, onu "awaıt" ıle kullanmanız gerekir. Yani, bu işe yaramaz: |
|||
|
|||
```Python |
|||
# Bu işe yaramaz, çünkü get_burgers, şu şekilde tanımlandı: async def |
|||
burgers = get_burgers(2) |
|||
``` |
|||
|
|||
--- |
|||
|
|||
Bu nedenle, size onu `await` ile çağırabileceğinizi söyleyen bir kitaplık kullanıyorsanız, onu `async def` ile tanımlanan *path fonksiyonu* içerisinde kullanmanız gerekir, örneğin: |
|||
|
|||
```Python hl_lines="2-3" |
|||
@app.get('/burgers') |
|||
async def read_burgers(): |
|||
burgers = await get_burgers(2) |
|||
return burgers |
|||
``` |
|||
|
|||
### Daha fazla teknik detay |
|||
|
|||
`await` in yalnızca `async def` ile tanımlanan fonksıyonların içinde kullanılabileceğini fark etmişsinizdir. |
|||
|
|||
Ama aynı zamanda, `async def` ile tanımlanan fonksiyonların "await" ile beklenmesi gerekir. Bu nedenle, "`async def` içeren fonksiyonlar yalnızca "`async def` ile tanımlanan fonksiyonların içinde çağrılabilir. |
|||
|
|||
|
|||
Yani yumurta mı tavukdan, tavuk mu yumurtadan gibi ilk `async` fonksiyonu nasıl çağırılır? |
|||
|
|||
**FastAPI** ile çalışıyorsanız bunun için endişelenmenize gerek yok, çünkü bu "ilk" fonksiyon sizin *path fonksiyonunuz* olacak ve FastAPI doğru olanı nasıl yapacağını bilecek. |
|||
|
|||
Ancak FastAPI olmadan `async` / `await` kullanmak istiyorsanız, <a href="https://docs.python.org/3/library/asyncio-task.html#coroutine" class="external-link" target="_blank">resmi Python belgelerini kontrol edin</a>. |
|||
|
|||
### Asenkron kodun diğer biçimleri |
|||
|
|||
Bu `async` ve `await` kullanimi oldukça yenidir. |
|||
|
|||
Ancak asenkron kodla çalışmayı çok daha kolay hale getirir. |
|||
|
|||
Aynı sözdizimi (hemen hemen aynı) son zamanlarda JavaScript'in modern sürümlerine de dahil edildi (Tarayıcı ve NodeJS'de). |
|||
|
|||
Ancak bundan önce, asenkron kodu işlemek oldukça karmaşık ve zordu. |
|||
|
|||
Python'un önceki sürümlerinde, threadlerı veya <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a> kullanıyor olabilirdin. Ancak kodu anlamak, hata ayıklamak ve düşünmek çok daha karmaşık olurdu. |
|||
|
|||
NodeJS / Browser JavaScript'in önceki sürümlerinde, "callback" kullanırdınız. Bu da <a href="http://callbackhell.com/" class="external-link" target="_blank">callbacks cehennemine</a> yol açar. |
|||
|
|||
## Coroutine'ler |
|||
|
|||
**Coroutine**, bir `async def` fonksiyonu tarafından döndürülen değer için çok süslü bir terimdir. Python bunun bir fonksiyon gibi bir noktada başlayıp biteceğini bilir, ancak içinde bir `await` olduğunda dahili olarak da duraklatılabilir ⏸. |
|||
|
|||
Ancak, `async` ve `await` ile asenkron kod kullanmanın tüm bu işlevselliği, çoğu zaman "Coroutine" kullanmak olarak adlandırılır. Go'nun ana özelliği olan "Goroutines" ile karşılaştırılabilir. |
|||
|
|||
## Sonuç |
|||
|
|||
Aynı ifadeyi yukarıdan görelim: |
|||
|
|||
> Python'ın modern sürümleri, **"async" ve "await"** sözdizimi ile birlikte **"coroutines"** adlı bir özelliği kullanan **"asenkron kod"** desteğine sahiptir. |
|||
|
|||
Şimdi daha mantıklı gelmeli. ✨ |
|||
|
|||
FastAPI'ye (Starlette aracılığıyla) güç veren ve bu kadar etkileyici bir performansa sahip olmasını sağlayan şey budur. |
|||
|
|||
## Çok Teknik Detaylar |
|||
|
|||
!!! warning |
|||
Muhtemelen burayı atlayabilirsiniz. |
|||
|
|||
Bunlar, **FastAPI**'nin altta nasıl çalıştığına dair çok teknik ayrıntılardır. |
|||
|
|||
Biraz teknik bilginiz varsa (co-routines, threads, blocking, vb)ve FastAPI'nin "async def" ile normal "def" arasındaki farkı nasıl işlediğini merak ediyorsanız, devam edin. |
|||
|
|||
### Path fonksiyonu |
|||
|
|||
"async def" yerine normal "def" ile bir *yol işlem işlevi* bildirdiğinizde, doğrudan çağrılmak yerine (sunucuyu bloke edeceğinden) daha sonra beklenen harici bir iş parçacığı havuzunda çalıştırılır. |
|||
|
|||
Yukarıda açıklanan şekilde çalışmayan başka bir asenkron framework'den geliyorsanız ve küçük bir performans kazancı (yaklaşık 100 nanosaniye) için "def" ile *path fonksiyonu* tanımlamaya alışkınsanız, **FastAPI**'de tam tersi olacağını unutmayın. Bu durumlarda, *path fonksiyonu* <abbr title="Input/Output: disk okuma veya yazma, ağ iletişimleri.">G/Ç</abbr> engelleyen durum oluşturmadıkça "async def" kullanmak daha iyidir. |
|||
|
|||
Yine de, her iki durumda da, **FastAPI**'nin önceki frameworkden [hala daha hızlı](/#performance){.internal-link target=_blank} (veya en azından karşılaştırılabilir) olma olasılığı vardır. |
|||
|
|||
### Bagımlılıklar |
|||
|
|||
Aynısı bağımlılıklar için de geçerlidir. Bir bağımlılık, "async def" yerine standart bir "def" işleviyse, harici iş parçacığı havuzunda çalıştırılır. |
|||
|
|||
### Alt-bağımlıklar |
|||
|
|||
Birbirini gerektiren (fonksiyonlarin parametreleri olarak) birden fazla bağımlılık ve alt bağımlılıklarınız olabilir, bazıları 'async def' ve bazıları normal 'def' ile oluşturulabilir. Yine de normal 'def' ile oluşturulanlar, "await" kulanilmadan harici bir iş parçacığında (iş parçacığı havuzundan) çağrılır. |
|||
|
|||
### Diğer yardımcı fonksiyonlar |
|||
|
|||
Doğrudan çağırdığınız diğer herhangi bir yardımcı fonksiyonu, normal "def" veya "async def" ile tanimlayabilirsiniz. FastAPI onu çağırma şeklinizi etkilemez. |
|||
|
|||
Bu, FastAPI'nin sizin için çağırdığı fonksiyonlarin tam tersidir: *path fonksiyonu* ve bağımlılıklar. |
|||
|
|||
Yardımcı program fonksiyonunuz 'def' ile normal bir işlevse, bir iş parçacığı havuzunda değil doğrudan (kodunuzda yazdığınız gibi) çağrılır, işlev 'async def' ile oluşturulmuşsa çağırıldığı yerde 'await' ile beklemelisiniz. |
|||
|
|||
--- |
|||
|
|||
Yeniden, bunlar, onları aramaya geldiğinizde muhtemelen işinize yarayacak çok teknik ayrıntılardır. |
|||
|
|||
Aksi takdirde, yukarıdaki bölümdeki yönergeleri iyi bilmelisiniz: <a href="#in-a-hurry">Aceleniz mi var?</a>. |
@ -0,0 +1,84 @@ |
|||
# Proje oluşturma - Şablonlar |
|||
|
|||
Başlamak için bir proje oluşturucu kullanabilirsiniz, çünkü sizin için önceden yapılmış birçok başlangıç kurulumu, güvenlik, veritabanı ve temel API endpoinlerini içerir. |
|||
|
|||
Bir proje oluşturucu, her zaman kendi ihtiyaçlarınıza göre güncellemeniz ve uyarlamanız gereken esnek bir kuruluma sahip olacaktır, ancak bu, projeniz için iyi bir başlangıç noktası olabilir. |
|||
|
|||
## Full Stack FastAPI PostgreSQL |
|||
|
|||
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> |
|||
|
|||
### Full Stack FastAPI PostgreSQL - Özellikler |
|||
|
|||
* Full **Docker** entegrasyonu (Docker based). |
|||
* Docker Swarm Mode ile deployment. |
|||
* **Docker Compose** entegrasyonu ve lokal geliştirme için optimizasyon. |
|||
* Uvicorn ve Gunicorn ile **Production ready** Python web server'ı. |
|||
* Python <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">**FastAPI**</a> backend: |
|||
* **Hızlı**: **NodeJS** ve **Go** ile eşit, çok yüksek performans (Starlette ve Pydantic'e teşekkürler). |
|||
* **Sezgisel**: Editor desteğı. <abbr title="auto-complete, IntelliSense gibi isimlerle de bilinir">Otomatik tamamlama</abbr>. Daha az debugging. |
|||
* **Kolay**: Kolay öğrenip kolay kullanmak için tasarlandı. Daha az döküman okuma daha çok iş. |
|||
* **Kısa**: Minimum kod tekrarı. Her parametre bildiriminde birden çok özellik. |
|||
* **Güçlü**: Production-ready. Otomatik interaktif dökümantasyon. |
|||
* **Standartlara dayalı**: API'ler için açık standartlara dayanır (ve tamamen uyumludur): <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> ve <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Şeması</a>. |
|||
* <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**Birçok diger özelliği**</a> dahili otomatik doğrulama, serialization, interaktif dokümantasyon, OAuth2 JWT token ile authentication, vb. |
|||
* **Güvenli şifreleme** . |
|||
* **JWT token** kimlik doğrulama. |
|||
* **SQLAlchemy** models (Flask dan bağımsızdır. Celery worker'ları ile kullanılabilir). |
|||
* Kullanıcılar için temel başlangıç modeli (gerektiği gibi değiştirin ve kaldırın). |
|||
* **Alembic** migration. |
|||
* **CORS** (Cross Origin Resource Sharing). |
|||
* **Celery** worker'ları ile backend içerisinden seçilen işleri çalıştırabilirsiniz. |
|||
* **Pytest**'e dayalı, Docker ile entegre REST backend testleri ile veritabanından bağımsız olarak tam API etkileşimini test edebilirsiniz. Docker'da çalıştığı için her seferinde sıfırdan yeni bir veri deposu oluşturabilir (böylece ElasticSearch, MongoDB, CouchDB veya ne istersen kullanabilirsin ve sadece API'nin çalışıp çalışmadığını test edebilirsin). |
|||
* Atom Hydrogen veya Visual Studio Code Jupyter gibi uzantılarla uzaktan veya Docker içi geliştirme için **Jupyter Çekirdekleri** ile kolay Python entegrasyonu. |
|||
* **Vue** ile frontend: |
|||
* Vue CLI ile oluşturulmuş. |
|||
* Dahili **JWT kimlik doğrulama**. |
|||
* Dahili Login. |
|||
* Login sonrası, Kontrol paneli. |
|||
* Kullanıcı oluşturma ve düzenleme kontrol paneli |
|||
* Kendi kendine kullanıcı sürümü. |
|||
* **Vuex**. |
|||
* **Vue-router**. |
|||
* **Vuetify** güzel material design kompanentleri için. |
|||
* **TypeScript**. |
|||
* **Nginx** tabanlı Docker sunucusu (Vue-router için yapılandırılmış). |
|||
* Docker ile multi-stage yapı, böylece kodu derlemeniz, kaydetmeniz veya işlemeniz gerekmez. |
|||
* Derleme zamanında Frontend testi (devre dışı bırakılabilir). |
|||
* Mümkün olduğu kadar modüler yapılmıştır, bu nedenle kutudan çıktığı gibi çalışır, ancak Vue CLI ile yeniden oluşturabilir veya ihtiyaç duyduğunuz şekilde oluşturabilir ve istediğinizi yeniden kullanabilirsiniz. |
|||
* **PGAdmin** PostgreSQL database admin tool'u, PHPMyAdmin ve MySQL ile kolayca değiştirilebilir. |
|||
* **Flower** ile Celery job'larını monitörleme. |
|||
* **Traefik** ile backend ve frontend arasında yük dengeleme, böylece her ikisini de aynı domain altında, path ile ayrılmış, ancak farklı kapsayıcılar tarafından sunulabilirsiniz. |
|||
* Let's Encrypt **HTTPS** sertifikalarının otomatik oluşturulması dahil olmak üzere Traefik entegrasyonu. |
|||
* GitLab **CI** (sürekli entegrasyon), backend ve frontend testi dahil. |
|||
|
|||
## 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> |
|||
|
|||
⚠️ **UYARI** ⚠️ |
|||
|
|||
Sıfırdan bir projeye başlıyorsanız alternatiflerine bakın. |
|||
|
|||
Örneğin, <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">Full Stack FastAPI PostgreSQL</a> daha iyi bir alternatif olabilir, aktif olarak geliştiriliyor ve kullanılıyor. Ve yeni özellik ve ilerlemelere sahip. |
|||
|
|||
İsterseniz Couchbase tabanlı generator'ı kullanmakta özgürsünüz, hala iyi çalışıyor olmalı ve onunla oluşturulmuş bir projeniz varsa bu da sorun değil (ve muhtemelen zaten ihtiyaçlarınıza göre güncellediniz). |
|||
|
|||
Bununla ilgili daha fazla bilgiyi repo belgelerinde okuyabilirsiniz. |
|||
|
|||
## Full Stack FastAPI MongoDB |
|||
|
|||
... müsaitliğime ve diğer faktörlere bağlı olarak daha sonra gelebilir. 😅 🎉 |
|||
|
|||
## Machine Learning modelleri, spaCy ve FastAPI |
|||
|
|||
GitHub: <a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a> |
|||
|
|||
### Machine Learning modelleri, spaCy ve FastAPI - Features |
|||
|
|||
* **spaCy** NER model entegrasyonu. |
|||
* **Azure Cognitive Search** yerleşik istek biçimi. |
|||
* Uvicorn ve Gunicorn ile **Production ready** Python web server'ı. |
|||
* Dahili **Azure DevOps** Kubernetes (AKS) CI/CD deployment. |
|||
* **Multilingual**, Proje kurulumu sırasında spaCy'nin yerleşik dillerinden birini kolayca seçin. |
|||
* **Esnetilebilir** diğer frameworkler (Pytorch, Tensorflow) ile de çalışır sadece spaCy değil. |
@ -0,0 +1,470 @@ |
|||
<p align="center"> |
|||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a> |
|||
</p> |
|||
<p align="center"> |
|||
<em>FastAPI 框架,高效能,易於學習,快速開發,適用於生產環境</em> |
|||
</p> |
|||
<p align="center"> |
|||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank"> |
|||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test"> |
|||
</a> |
|||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/fastapi" target="_blank"> |
|||
<img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/fastapi.svg" alt="Coverage"> |
|||
</a> |
|||
<a href="https://pypi.org/project/fastapi" target="_blank"> |
|||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version"> |
|||
</a> |
|||
<a href="https://pypi.org/project/fastapi" target="_blank"> |
|||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions"> |
|||
</a> |
|||
</p> |
|||
|
|||
--- |
|||
|
|||
**文件**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a> |
|||
|
|||
**程式碼**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a> |
|||
|
|||
--- |
|||
|
|||
FastAPI 是一個現代、快速(高效能)的 web 框架,用於 Python 3.8+ 並採用標準 Python 型別提示。 |
|||
|
|||
主要特點包含: |
|||
|
|||
- **快速**: 非常高的效能,可與 **NodeJS** 和 **Go** 效能相當 (歸功於 Starlette and Pydantic)。 [FastAPI 是最快的 Python web 框架之一](#performance)。 |
|||
- **極速開發**: 提高開發功能的速度約 200% 至 300%。 \* |
|||
- **更少的 Bug**: 減少約 40% 的人為(開發者)導致的錯誤。 \* |
|||
- **直覺**: 具有出色的編輯器支援,處處都有<abbr title="也被稱為自動完成、IntelliSense">自動補全</abbr>以減少偵錯時間。 |
|||
- **簡單**: 設計上易於使用和學習,大幅減少閱讀文件的時間。 |
|||
- **簡潔**: 最小化程式碼重複性。可以通過不同的參數聲明來實現更豐富的功能,和更少的錯誤。 |
|||
- **穩健**: 立即獲得生產級可用的程式碼,還有自動生成互動式文件。 |
|||
- **標準化**: 基於 (且完全相容於) OpenAPIs 的相關標準:<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a>(之前被稱為 Swagger)和<a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>。 |
|||
|
|||
<small>\* 基於內部開發團隊在建立生產應用程式時的測試預估。</small> |
|||
|
|||
## 贊助 |
|||
|
|||
<!-- sponsors --> |
|||
|
|||
{% if 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">其他贊助商</a> |
|||
|
|||
## 評價 |
|||
|
|||
"_[...] 近期大量的使用 **FastAPI**。 [...] 目前正在計畫在**微軟**團隊的**機器學習**服務中導入。其中一些正在整合到核心的 **Windows** 產品和一些 **Office** 產品。_" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div> |
|||
|
|||
--- |
|||
|
|||
"_我們使用 **FastAPI** 來建立產生**預測**結果的 **REST** 伺服器。 [for Ludwig]_" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div> |
|||
|
|||
--- |
|||
|
|||
"_**Netflix** 很榮幸地宣布開源**危機管理**協調框架: **Dispatch**! [是使用 **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> |
|||
|
|||
--- |
|||
|
|||
"_我對 **FastAPI** 興奮得不得了。它太有趣了!_" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div> |
|||
|
|||
--- |
|||
|
|||
"_老實說,你建造的東西看起來非常堅固和精緻。在很多方面,這就是我想要的,看到有人建造它真的很鼓舞人心。_" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div> |
|||
|
|||
--- |
|||
|
|||
"_如果您想學習一種用於構建 REST API 的**現代框架**,不能錯過 **FastAPI** [...] 它非常快速、且易於使用和學習 [...]_" |
|||
|
|||
"_我們的 **APIs** 已經改用 **FastAPI** [...] 我想你會喜歡它 [...]_" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> 創辦人 - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div> |
|||
|
|||
--- |
|||
|
|||
"_如果有人想要建立一個生產環境的 Python API,我強烈推薦 **FastAPI**,它**設計精美**,**使用簡單**且**高度可擴充**,它已成為我們 API 優先開發策略中的**關鍵組件**,並且驅動了許多自動化服務,例如我們的 Virtual TAC Engineer。_" |
|||
|
|||
<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**,命令列中的 FastAPI |
|||
|
|||
<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> |
|||
|
|||
如果你不是在開發網頁 API,而是正在開發一個在終端機中運行的<abbr title="Command Line Interface">命令列</abbr>應用程式,不妨嘗試 <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>。 |
|||
|
|||
**Typer** 是 FastAPI 的小兄弟。他立志成為命令列的 **FastAPI**。 ⌨️ 🚀 |
|||
|
|||
## 安裝需求 |
|||
|
|||
Python 3.8+ |
|||
|
|||
FastAPI 是站在以下巨人的肩膀上: |
|||
|
|||
- <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> 負責網頁的部分 |
|||
- <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> 負責資料的部分 |
|||
|
|||
## 安裝 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install fastapi |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
你同時也會需要 ASGI 伺服器用於生產環境,像是 <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> 或 <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "uvicorn[standard]" |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## 範例 |
|||
|
|||
### 建立 |
|||
|
|||
- 建立一個 python 檔案 `main.py`,並寫入以下程式碼: |
|||
|
|||
```Python |
|||
from typing import Union |
|||
|
|||
from fastapi import FastAPI |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
@app.get("/") |
|||
def read_root(): |
|||
return {"Hello": "World"} |
|||
|
|||
|
|||
@app.get("/items/{item_id}") |
|||
def read_item(item_id: int, q: Union[str, None] = None): |
|||
return {"item_id": item_id, "q": q} |
|||
``` |
|||
|
|||
<details markdown="1"> |
|||
<summary>或可以使用 <code>async def</code>...</summary> |
|||
|
|||
如果你的程式使用 `async` / `await`,請使用 `async def`: |
|||
|
|||
```Python hl_lines="9 14" |
|||
from typing import Union |
|||
|
|||
from fastapi import FastAPI |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
@app.get("/") |
|||
async def read_root(): |
|||
return {"Hello": "World"} |
|||
|
|||
|
|||
@app.get("/items/{item_id}") |
|||
async def read_item(item_id: int, q: Union[str, None] = None): |
|||
return {"item_id": item_id, "q": q} |
|||
``` |
|||
|
|||
**注意**: |
|||
|
|||
如果你不知道是否會用到,可以查看 _"In a hurry?"_ 章節中,關於 <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` 和 `await` 的部分</a>。 |
|||
|
|||
</details> |
|||
|
|||
### 運行 |
|||
|
|||
使用以下指令運行伺服器: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uvicorn main:app --reload |
|||
|
|||
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: Waiting for application startup. |
|||
INFO: Application startup complete. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
<details markdown="1"> |
|||
<summary>關於指令 <code>uvicorn main:app --reload</code>...</summary> |
|||
|
|||
該指令 `uvicorn main:app` 指的是: |
|||
|
|||
- `main`:`main.py` 檔案(一個 python 的 "模組")。 |
|||
- `app`:在 `main.py` 檔案中,使用 `app = FastAPI()` 建立的物件。 |
|||
- `--reload`:程式碼更改後會自動重新啟動,請僅在開發時使用此參數。 |
|||
|
|||
</details> |
|||
|
|||
### 檢查 |
|||
|
|||
使用瀏覽器開啟 <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>。 |
|||
|
|||
你將會看到以下的 JSON 回應: |
|||
|
|||
```JSON |
|||
{"item_id": 5, "q": "somequery"} |
|||
``` |
|||
|
|||
你已經建立了一個具有以下功能的 API: |
|||
|
|||
- 透過路徑 `/` 和 `/items/{item_id}` 接受 HTTP 請求。 |
|||
- 以上路經都接受 `GET` <em>請求</em>(也被稱為 HTTP _方法_)。 |
|||
- 路徑 `/items/{item_id}` 有一個 `int` 型別的 `item_id` 參數。 |
|||
- 路徑 `/items/{item_id}` 有一個 `str` 型別的查詢參數 `q`。 |
|||
|
|||
### 互動式 API 文件 |
|||
|
|||
使用瀏覽器開啟 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。 |
|||
|
|||
你會看到自動生成的互動式 API 文件(由 <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> 生成): |
|||
|
|||
 |
|||
|
|||
### ReDoc API 文件 |
|||
|
|||
使用瀏覽器開啟 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>。 |
|||
|
|||
你將看到 ReDoc 文件 (由 <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> 生成): |
|||
|
|||
 |
|||
|
|||
## 範例升級 |
|||
|
|||
現在繼續修改 `main.py` 檔案,來接收一個帶有 body 的 `PUT` 請求。 |
|||
|
|||
我們使用 Pydantic 來使用標準的 Python 型別聲明請求。 |
|||
|
|||
```Python hl_lines="4 9-12 25-27" |
|||
from typing import Union |
|||
|
|||
from fastapi import FastAPI |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class Item(BaseModel): |
|||
name: str |
|||
price: float |
|||
is_offer: Union[bool, None] = None |
|||
|
|||
|
|||
@app.get("/") |
|||
def read_root(): |
|||
return {"Hello": "World"} |
|||
|
|||
|
|||
@app.get("/items/{item_id}") |
|||
def read_item(item_id: int, q: Union[str, None] = None): |
|||
return {"item_id": item_id, "q": q} |
|||
|
|||
|
|||
@app.put("/items/{item_id}") |
|||
def update_item(item_id: int, item: Item): |
|||
return {"item_name": item.name, "item_id": item_id} |
|||
``` |
|||
|
|||
伺服器將自動重新載入(因為在上一步中,你向 `uvicorn` 指令添加了 `--reload` 的選項)。 |
|||
|
|||
### 互動式 API 文件升級 |
|||
|
|||
使用瀏覽器開啟 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。 |
|||
|
|||
- 互動式 API 文件會自動更新,並加入新的 body 請求: |
|||
|
|||
 |
|||
|
|||
- 點擊 "Try it out" 按鈕, 你可以填寫參數並直接與 API 互動: |
|||
|
|||
 |
|||
|
|||
- 然後點擊 "Execute" 按鈕,使用者介面將會向 API 發送請求,並將結果顯示在螢幕上: |
|||
|
|||
 |
|||
|
|||
### ReDoc API 文件升級 |
|||
|
|||
使用瀏覽器開啟 <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>。 |
|||
|
|||
- ReDoc API 文件會自動更新,並加入新的參數和 body 請求: |
|||
|
|||
 |
|||
|
|||
### 總結 |
|||
|
|||
總結來說, 你就像宣告函式的參數型別一樣,只宣告了一次請求參數和請求主體參數等型別。 |
|||
|
|||
你使用 Python 標準型別來完成聲明。 |
|||
|
|||
你不需要學習新的語法、類別、方法或函式庫等等。 |
|||
|
|||
只需要使用 **Python 3.8 以上的版本**。 |
|||
|
|||
舉個範例,比如宣告 int 的型別: |
|||
|
|||
```Python |
|||
item_id: int |
|||
``` |
|||
|
|||
或是一個更複雜的 `Item` 模型: |
|||
|
|||
```Python |
|||
item: Item |
|||
``` |
|||
|
|||
在進行一次宣告後,你將獲得: |
|||
|
|||
- 編輯器支援: |
|||
- 自動補全 |
|||
- 型別檢查 |
|||
- 資料驗證: |
|||
- 驗證失敗時自動生成清楚的錯誤訊息 |
|||
- 可驗證多層巢狀的 JSON 物件 |
|||
- <abbr title="也被稱為: 序列化或解析">轉換</abbr>輸入的資料: 轉換來自網路請求到 Python 資料型別。包含以下數據: |
|||
- JSON |
|||
- 路徑參數 |
|||
- 查詢參數 |
|||
- Cookies |
|||
- 請求標頭 |
|||
- 表單 |
|||
- 文件 |
|||
- <abbr title="也被稱為: 序列化或解析">轉換</abbr>輸出的資料: 轉換 Python 資料型別到網路傳輸的 JSON: |
|||
- 轉換 Python 型別 (`str`、 `int`、 `float`、 `bool`、 `list` 等) |
|||
- `datetime` 物件 |
|||
- `UUID` 物件 |
|||
- 數據模型 |
|||
- ...還有其他更多 |
|||
- 自動生成的 API 文件,包含 2 種不同的使用介面: |
|||
- Swagger UI |
|||
- ReDoc |
|||
|
|||
--- |
|||
|
|||
回到前面的的程式碼範例,**FastAPI** 還會: |
|||
|
|||
- 驗證 `GET` 和 `PUT` 請求路徑中是否包含 `item_id`。 |
|||
- 驗證 `GET` 和 `PUT` 請求中的 `item_id` 是否是 `int` 型別。 |
|||
- 如果驗證失敗,將會返回清楚有用的錯誤訊息。 |
|||
- 查看 `GET` 請求中是否有命名為 `q` 的查詢參數 (例如 `http://127.0.0.1:8000/items/foo?q=somequery`)。 |
|||
- 因為 `q` 參數被宣告為 `= None`,所以是選填的。 |
|||
- 如果沒有宣告 `None`,則此參數將會是必填 (例如 `PUT` 範例的請求 body)。 |
|||
- 對於 `PUT` 的請求 `/items/{item_id}`,將會讀取 body 為 JSON: |
|||
- 驗證是否有必填屬性 `name` 且型別是 `str`。 |
|||
- 驗證是否有必填屬性 `price` 且型別是 `float`。 |
|||
- 驗證是否有選填屬性 `is_offer` 且型別是 `bool`。 |
|||
- 以上驗證都適用於多層次巢狀 JSON 物件。 |
|||
- 自動轉換 JSON 格式。 |
|||
- 透過 OpenAPI 文件來記錄所有內容,可以被用於: |
|||
- 互動式文件系統。 |
|||
- 自動為多種程式語言生成用戶端的程式碼。 |
|||
- 提供兩種交互式文件介面。 |
|||
|
|||
--- |
|||
|
|||
雖然我們只敘述了表面的功能,但其實你已經理解了它是如何執行。 |
|||
|
|||
試著修改以下程式碼: |
|||
|
|||
```Python |
|||
return {"item_name": item.name, "item_id": item_id} |
|||
``` |
|||
|
|||
從: |
|||
|
|||
```Python |
|||
... "item_name": item.name ... |
|||
``` |
|||
|
|||
修改為: |
|||
|
|||
```Python |
|||
... "item_price": item.price ... |
|||
``` |
|||
|
|||
然後觀察你的編輯器,會自動補全並且還知道他們的型別: |
|||
|
|||
 |
|||
|
|||
有關更多功能的完整範例,可以參考 <a href="https://fastapi.tiangolo.com/tutorial/">教學 - 使用者指南</a>。 |
|||
|
|||
**劇透警告**: 教學 - 使用者指南內容有: |
|||
|
|||
- 對來自不同地方的**參數**進行宣告:像是 **headers**, **cookies**, **form 表單**以及**上傳檔案**。 |
|||
- 如何設定 **驗證限制** 像是 `maximum_length` or `regex`。 |
|||
- 簡單且非常容易使用的 **<abbr title="也被稱為元件、資源、提供者、服務或是注入">依賴注入</abbr>** 系統。 |
|||
- 安全性和身份驗證,包含提供支援 **OAuth2**、**JWT tokens** 和 **HTTP Basic** 驗證。 |
|||
- 更進階 (但同樣簡單) 的宣告 **多層次的巢狀 JSON 格式** (感謝 Pydantic)。 |
|||
- **GraphQL** 與 <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> 以及其他的相關函式庫進行整合。 |
|||
- 更多其他的功能 (感謝 Starlette) 像是: |
|||
- **WebSockets** |
|||
- 於 HTTPX 和 `pytest` 的非常簡單測試 |
|||
- **CORS** |
|||
- **Cookie Sessions** |
|||
- ...以及更多 |
|||
|
|||
## 效能 |
|||
|
|||
來自獨立機構 TechEmpower 的測試結果,顯示在 Uvicorn 執行下的 **FastAPI** 是 <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">最快的 Python 框架之一</a>, 僅次於 Starlette 和 Uvicorn 本身 (兩者是 FastAPI 的底層)。 (\*) |
|||
|
|||
想了解更多訊息,可以參考 <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">測試結果</a>。 |
|||
|
|||
## 可選的依賴套件 |
|||
|
|||
用於 Pydantic: |
|||
|
|||
- <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - 用於電子郵件驗證。 |
|||
- <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - 用於設定管理。 |
|||
- <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - 用於與 Pydantic 一起使用的額外型別。 |
|||
|
|||
用於 Starlette: |
|||
|
|||
- <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - 使用 `TestClient`時必須安裝。 |
|||
- <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - 使用預設的模板配置時必須安裝。 |
|||
- <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - 需要使用 `request.form()` 對表單進行<abbr title="轉換來自表單的 HTTP 請求到 Python 資料型別"> "解析" </abbr>時安裝。 |
|||
- <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - 需要使用 `SessionMiddleware` 支援時安裝。 |
|||
- <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - 用於支援 Starlette 的 `SchemaGenerator` (如果你使用 FastAPI,可能不需要它)。 |
|||
- <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - 使用 `UJSONResponse` 時必須安裝。 |
|||
|
|||
用於 FastAPI / Starlette: |
|||
|
|||
- <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - 用於加載和運行應用程式的服務器。 |
|||
- <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - 使用 `ORJSONResponse`時必須安裝。 |
|||
|
|||
你可以使用 `pip install "fastapi[all]"` 來安裝這些所有依賴套件。 |
|||
|
|||
## 授權 |
|||
|
|||
該項目遵循 MIT 許可協議。 |
@ -0,0 +1 @@ |
|||
INHERIT: ../en/mkdocs.yml |
Loading…
Reference in new issue