committed by
GitHub
77 changed files with 8029 additions and 597 deletions
@ -0,0 +1,469 @@ |
|||
<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 framework, yüksək məshuldarlı, öyrənməsi asan, çevik kodlama, istifadəyə hazırdır</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="Əhatə"> |
|||
</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="Paket versiyası"> |
|||
</a> |
|||
<a href="https://pypi.org/project/fastapi" target="_blank"> |
|||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Dəstəklənən Python versiyaları"> |
|||
</a> |
|||
</p> |
|||
|
|||
--- |
|||
|
|||
**Sənədlər**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a> |
|||
|
|||
**Qaynaq Kodu**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a> |
|||
|
|||
--- |
|||
|
|||
FastAPI Python 3.8+ ilə API yaratmaq üçün standart Python <abbr title="Tip Məsləhətləri: Type Hints">tip məsləhətlərinə</abbr> əsaslanan, müasir, sürətli (yüksək performanslı) framework-dür. |
|||
|
|||
Əsas xüsusiyyətləri bunlardır: |
|||
|
|||
* **Sürətli**: Çox yüksək performans, **NodeJS** və **Go** səviyyəsində (Starlette və Pydantic-ə təşəkkürlər). [Ən sürətli Python frameworklərindən biridir](#performans). |
|||
* **Çevik kodlama**: Funksiyanallıqları inkişaf etdirmək sürətini təxminən 200%-dən 300%-ə qədər artırın. * |
|||
* **Daha az xəta**: İnsan (developer) tərəfindən törədilən səhvlərin təxminən 40% -ni azaldın. * |
|||
* **İntuitiv**: Əla redaktor dəstəyi. Hər yerdə <abbr title="auto-complete, autocompletion, IntelliSense olaraq da bilinir">otomatik tamamlama</abbr>. Xətaları müəyyənləşdirməyə daha az vaxt sərf edəcəksiniz. |
|||
* **Asan**: İstifadəsi və öyrənilməsi asan olması üçün nəzərdə tutulmuşdur. Sənədləri oxumaq üçün daha az vaxt ayıracaqsınız. |
|||
* **Qısa**: Kod təkrarlanmasını minimuma endirin. Hər bir parametr tərifində birdən çox xüsusiyyət ilə və daha az səhvlə qarşılaşacaqsınız. |
|||
* **Güclü**: Avtomatik və interaktiv sənədlərlə birlikdə istifadəyə hazır kod əldə edə bilərsiniz. |
|||
* **Standartlara əsaslanan**: API-lar üçün açıq standartlara əsaslanır (və tam uyğun gəlir): <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (əvvəlki adı ilə Swagger) və <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>. |
|||
|
|||
<small>* Bu fikirlər daxili development komandasının hazırladıqları məhsulların sınaqlarına əsaslanır.</small> |
|||
|
|||
## Sponsorlar |
|||
|
|||
<!-- 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/az/fastapi-people/#sponsors" class="external-link" target="_blank">Digər sponsorlar</a> |
|||
|
|||
## Rəylər |
|||
|
|||
"_[...] Son günlərdə **FastAPI**-ı çox istifadə edirəm. [...] Əslində onu komandamın bütün **Microsoftda ML sevislərində** istifadə etməyi planlayıram. Onların bəziləri **windows**-un əsas məhsuluna və bəzi **Office** məhsullarına inteqrasiya olunurlar._" |
|||
|
|||
<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** kitabxanasını **Proqnozlar** əldə etmək üçün sorğulana bilən **REST** serverini yaratmaqda istifadə etdik._" |
|||
|
|||
<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** **böhran idarəçiliyi** orkestrləşmə framework-nün açıq qaynaqlı buraxılışını elan etməkdən məmnundur: **Dispatch**! [**FastAPI** ilə quruldu]_" |
|||
|
|||
<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** üçün həyəcanlıyam. Çox əyləncəlidir!_" |
|||
|
|||
<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> |
|||
|
|||
--- |
|||
|
|||
"_Düzünü desəm, sizin qurduğunuz şey həqiqətən möhkəm və peşəkar görünür. Bir çox cəhətdən **Hug**-un olmasını istədiyim kimdir - kiminsə belə bir şey qurduğunu görmək həqiqətən ruhlandırıcıdır._" |
|||
|
|||
<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> |
|||
|
|||
--- |
|||
|
|||
"_Əgər REST API-lər yaratmaq üçün **müasir framework** öyrənmək istəyirsinizsə, **FastAPI**-a baxın [...] Sürətli, istifadəsi və öyrənməsi asandır. [...]_" |
|||
|
|||
"_**API** xidmətlərimizi **FastAPI**-a köçürdük [...] Sizin də bəyənəcəyinizi düşünürük._" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <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 ilə istifadəyə hazır API qurmaq istəyən hər kəsə **FastAPI**-ı tövsiyə edirəm. **Möhtəşəm şəkildə dizayn edilmiş**, **istifadəsi asan** və **yüksək dərəcədə genişlənə bilən**-dir, API əsaslı inkişaf strategiyamızın **əsas komponentinə** çevrilib və Virtual TAC Engineer kimi bir çox avtomatlaşdırma və servisləri idarə edir._" |
|||
|
|||
<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**, CLI-ların 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> |
|||
|
|||
Əgər siz veb API əvəzinə terminalda istifadə ediləcək <abbr title="Command Line Interface">CLI</abbr> proqramı qurursunuzsa, <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>-a baxa bilərsiniz. |
|||
|
|||
**Typer** FastAPI-ın kiçik qardaşıdır. Və o, CLI-lərin **FastAPI**-ı olmaq üçün nəzərdə tutulub. ⌨️ 🚀 |
|||
|
|||
## Tələblər |
|||
|
|||
Python 3.8+ |
|||
|
|||
FastAPI nəhənglərin çiyinlərində dayanır: |
|||
|
|||
* Web tərəfi üçün <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a>. |
|||
* Data tərəfi üçün <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a>. |
|||
|
|||
## Quraşdırma |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install fastapi |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Tətbiqimizi əlçatan etmək üçün bizə <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> və ya <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a> kimi ASGI server lazımdır. |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "uvicorn[standard]" |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## Nümunə |
|||
|
|||
### Kodu yaradaq |
|||
|
|||
* `main.py` adlı fayl yaradaq və ona aşağıdakı kodu yerləşdirək: |
|||
|
|||
```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>Və ya <code>async def</code>...</summary> |
|||
|
|||
Əgər kodunuzda `async` və ya `await` vardırsa `async def` istifadə edə bilərik: |
|||
|
|||
```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} |
|||
``` |
|||
|
|||
**Qeyd**: |
|||
|
|||
Əgər bu mövzu haqqında məlumatınız yoxdursa <a href="https://fastapi.tiangolo.com/az/async/#in-a-hurry" target="_blank">`async` və `await` sənədindəki</a> _"Tələsirsən?"_ bölməsinə baxa bilərsiniz. |
|||
|
|||
</details> |
|||
|
|||
### Kodu işə salaq |
|||
|
|||
Serveri aşağıdakı əmr ilə işə salaq: |
|||
|
|||
<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> əmri haqqında...</summary> |
|||
|
|||
`uvicorn main:app` əmri aşağıdakılara instinad edir: |
|||
|
|||
* `main`: `main.py` faylı (yəni Python "modulu"). |
|||
* `app`: `main.py` faylında `app = FastAPI()` sətrində yaratdığımız `FastAPI` obyektidir. |
|||
* `--reload`: kod dəyişikliyindən sonra avtomatik olaraq serveri yenidən işə salır. Bu parametrdən yalnız development mərhələsində istifadə etməliyik. |
|||
|
|||
</details> |
|||
|
|||
### İndi yoxlayaq |
|||
|
|||
Bu linki brauzerimizdə açaq <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>. |
|||
|
|||
Aşağıdakı kimi bir JSON cavabı görəcəksiniz: |
|||
|
|||
```JSON |
|||
{"item_id": 5, "q": "somequery"} |
|||
``` |
|||
|
|||
Siz artıq bir API yaratmısınız, hansı ki: |
|||
|
|||
* `/` və `/items/{item_id}` <abbr title="Yol: Path ">_yollarında_</abbr> HTTP sorğularını qəbul edir. |
|||
* Hər iki _yolda_ `GET` <em>əməliyyatlarını</em> (həmçinin HTTP _metodları_ kimi bilinir) aparır. |
|||
* `/items/{item_id}` _yolu_ `item_id` adlı `int` qiyməti almalı olan _yol parametrinə_ sahibdir. |
|||
* `/items/{item_id}` _yolunun_ `q` adlı yol parametri var və bu parametr istəyə bağlı olsa da, `str` qiymətini almalıdır. |
|||
|
|||
### İnteraktiv API Sənədləri |
|||
|
|||
İndi <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> ünvanına daxil olun. |
|||
|
|||
Avtomatik interaktiv API sənədlərini görəcəksiniz (<a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> tərəfindən təmin edilir): |
|||
|
|||
 |
|||
|
|||
### Alternativ API sənədləri |
|||
|
|||
İndi isə <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> ünvanına daxil olun. |
|||
|
|||
<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> tərəfindən təqdim edilən avtomatik sənədləri görəcəksiniz: |
|||
|
|||
 |
|||
|
|||
## Nümunəni Yeniləyək |
|||
|
|||
İndi gəlin `main.py` faylını `PUT` sorğusu ilə birlikdə <abbr title="Gövdə: Body ">gövdə</abbr> qəbul edəcək şəkildə dəyişdirək. |
|||
|
|||
Pydantic sayəsində standart Python tiplərindən istifadə edərək <abbr title="Gövdə: Body ">gövdə</abbr>ni müəyyən edək. |
|||
|
|||
```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} |
|||
``` |
|||
Server avtomatik olaraq yenidən işə salınmalı idi (çünki biz yuxarıda `uvicorn` əmri ilə `--reload` parametrindən istifadə etmişik). |
|||
|
|||
### İnteraktiv API sənədlərindəki dəyişikliyə baxaq |
|||
|
|||
Yenidən <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> ünvanına daxil olun. |
|||
|
|||
* İnteraktiv API sənədləri yeni gövdə də daxil olmaq ilə avtomatik olaraq yenilənəcək: |
|||
|
|||
 |
|||
|
|||
* "Try it out" düyməsini klikləyin, bu, parametrləri doldurmağa və API ilə birbaşa əlaqə saxlamağa imkan verir: |
|||
|
|||
 |
|||
|
|||
* Sonra "Execute" düyməsini klikləyin, istifadəçi interfeysi API ilə əlaqə quracaq, parametrləri göndərəcək, nəticələri əldə edəcək və onları ekranda göstərəcək: |
|||
|
|||
 |
|||
|
|||
### Alternativ API Sənədlərindəki Dəyişikliyə Baxaq |
|||
|
|||
İndi isə yenidən <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> ünvanına daxil olun. |
|||
|
|||
* Alternativ sənədlər həm də yeni sorğu parametri və gövdəsini əks etdirəcək: |
|||
|
|||
 |
|||
|
|||
### Xülasə |
|||
|
|||
Ümumiləşdirsək, parametrlər, gövdə və s. Biz məlumat növlərini **bir dəfə** funksiya parametrləri kimi təyin edirik. |
|||
|
|||
Bunu standart müasir Python tipləri ilə edirsiniz. |
|||
|
|||
Yeni sintaksis, müəyyən bir kitabxananın metodlarını və ya siniflərini və s. öyrənmək məcburiyyətində deyilsiniz. |
|||
|
|||
Sadəcə standart **Python 3.8+**. |
|||
|
|||
Məsələn, `int` üçün: |
|||
|
|||
```Python |
|||
item_id: int |
|||
``` |
|||
|
|||
və ya daha mürəkkəb `Item` modeli üçün: |
|||
|
|||
```Python |
|||
item: Item |
|||
``` |
|||
|
|||
...və yalnız parametr tipini təyin etməklə bunları əldə edirsiniz: |
|||
|
|||
* Redaktor dəstəyi ilə: |
|||
* Avtomatik tamamlama. |
|||
* Tip yoxlanması. |
|||
* Məlumatların Təsdiqlənməsi: |
|||
* Məlumat etibarsız olduqda avtomatik olaraq aydın xətalar göstərir. |
|||
* Hətta çox dərin JSON obyektlərində belə doğrulama aparır. |
|||
* Daxil olan məlumatları <abbr title="Çevrilmə: serialization, parsing, marshalling olaraq da bilinir">çevirmək</abbr> üçün aşağıdakı məlumat növlərindən istifadə edilir: |
|||
* JSON. |
|||
* <abbr title="Yol: Path">Yol</abbr> parametrləri. |
|||
* <abbr title="Sorğu: Query">Sorğu</abbr> parametrləri. |
|||
* <abbr title="Çərəz: Cookie">Çərəzlər</abbr>. |
|||
* <abbr title="Başlıq: Header">Başlıqlaq</abbr>. |
|||
* <abbr title="Forma: Form">Formalar</abbr>. |
|||
* Fayllar. |
|||
* Daxil olan məlumatları <abbr title="Çevrilmə: serialization, parsing, marshalling olaraq da bilinir">çevirmək</abbr> üçün aşağıdakı məlumat növlərindən istifadə edilir (JSON olaraq): |
|||
* Python tiplərinin (`str`, `int`, `float`, `bool`, `list`, və s) çevrilməsi. |
|||
* `datetime` obyektləri. |
|||
* `UUID` obyektləri. |
|||
* Verilənlər bazası modelləri. |
|||
* və daha çoxu... |
|||
* 2 alternativ istifadəçi interfeysi daxil olmaqla avtomatik interaktiv API sənədlərini təmin edir: |
|||
* Swagger UI. |
|||
* ReDoc. |
|||
|
|||
--- |
|||
|
|||
Gəlin əvvəlki nümunəyə qayıdaq və **FastAPI**-nin nələr edəcəyinə nəzər salaq: |
|||
|
|||
* `GET` və `PUT` sorğuları üçün `item_id`-nin <abbr title="Yol: Path">yolda</abbr> olub-olmadığını yoxlayacaq. |
|||
* `item_id`-nin `GET` və `PUT` sorğuları üçün növünün `int` olduğunu yoxlayacaq. |
|||
* Əgər `int` deyilsə, səbəbini göstərən bir xəta mesajı göstərəcəkdir. |
|||
* <abbr title="Məcburi olmayan: Optional">məcburi olmayan</abbr> `q` parametrinin `GET` (`http://127.0.0.1:8000/items/foo?q=somequery` burdakı kimi) sorğusu içərisində olub olmadığını yoxlayacaq. |
|||
* `q` parametrini `= None` ilə yaratdığımız üçün, <abbr title="Məcburi olmayan: Optional">məcburi olmayan</abbr> parametr olacaq. |
|||
* Əgər `None` olmasaydı, bu məcburi parametr olardı (`PUT` metodunun gövdəsində olduğu kimi). |
|||
* `PUT` sorğusu üçün, `/items/{item_id}` gövdəsini JSON olaraq oxuyacaq: |
|||
* `name` adında məcburi bir parametr olub olmadığını və əgər varsa, tipinin `str` olub olmadığını yoxlayacaq. |
|||
* `price` adında məcburi bir parametr olub olmadığını və əgər varsa, tipinin `float` olub olmadığını yoxlayacaq. |
|||
* `is_offer` adında <abbr title="Məcburi olmayan: Optional">məcburi olmayan</abbr> bir parametr olub olmadığını və əgər varsa, tipinin `float` olub olmadığını yoxlayacaq. |
|||
* Bütün bunlar ən dərin JSON obyektlərində belə işləyəcək. |
|||
* Məlumatların JSON-a və JSON-un Python obyektinə çevrilməsi avtomatik həyata keçiriləcək. |
|||
* Hər şeyi OpenAPI ilə uyğun olacaq şəkildə avtomatik olaraq sənədləşdirəcək və onları aşağıdakı kimi istifadə edə biləcək: |
|||
* İnteraktiv sənədləşmə sistemləri. |
|||
* Bir çox proqramlaşdırma dilləri üçün avtomatlaşdırılmış <abbr title="Müştəri: Client">müştəri</abbr> kodu yaratma sistemləri. |
|||
* 2 interaktiv sənədləşmə veb interfeysini birbaşa təmin edəcək. |
|||
|
|||
--- |
|||
|
|||
Yeni başlamışıq, amma siz artıq işin məntiqini başa düşmüsünüz. |
|||
|
|||
İndi aşağıdakı sətri dəyişdirməyə çalışın: |
|||
|
|||
```Python |
|||
return {"item_name": item.name, "item_id": item_id} |
|||
``` |
|||
|
|||
...bundan: |
|||
|
|||
```Python |
|||
... "item_name": item.name ... |
|||
``` |
|||
|
|||
...buna: |
|||
|
|||
```Python |
|||
... "item_price": item.price ... |
|||
``` |
|||
|
|||
...və redaktorun məlumat tiplərini bildiyini və avtomatik tamaladığını görəcəksiniz: |
|||
|
|||
 |
|||
|
|||
Daha çox funksiyaya malik daha dolğun nümunə üçün <a href="https://fastapi.tiangolo.com/az/tutorial/">Öyrədici - İstifadəçi Təlimatı</a> səhifəsinə baxa bilərsiniz. |
|||
|
|||
**Spoiler xəbərdarlığı**: Öyrədici - istifadəçi təlimatına bunlar daxildir: |
|||
|
|||
* **Parametrlərin**, <abbr title="Başlıq: Header">**başlıqlar**</abbr>, <abbr title="Çərəz: Cookie">çərəzlər</abbr>, **forma sahələri** və **fayllar** olaraq müəyyən edilməsi. |
|||
* `maximum_length` və ya `regex` kimi **doğrulama məhdudiyyətlərinin** necə təyin ediləcəyi. |
|||
* Çox güclü və istifadəsi asan **<abbr title="components, resources, providers, services, injectables olaraq da bilinir">Dependency Injection</abbr>** sistemi. |
|||
* Təhlükəsizlik və autentifikasiya, **JWT tokenləri** ilə **OAuth2** dəstəyi və **HTTP Basic** autentifikasiyası. |
|||
* **çox dərin JSON modellərini** müəyyən etmək üçün daha irəli səviyyə (lakin eyni dərəcədə asan) üsullar (Pydantic sayəsində). |
|||
* <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> və digər kitabxanalar ilə **GraphQL** inteqrasiyası. |
|||
* Digər əlavə xüsusiyyətlər (Starlette sayəsində): |
|||
* **WebSockets** |
|||
* HTTPX və `pytest` sayəsində çox asan testlər |
|||
* **CORS** |
|||
* **Cookie Sessions** |
|||
* ...və daha çoxu. |
|||
|
|||
## Performans |
|||
|
|||
Müstəqil TechEmpower meyarları göstərir ki, Uvicorn üzərində işləyən **FastAPI** proqramları <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">ən sürətli Python kitabxanalarından biridir</a>, yalnız Starlette və Uvicorn-un özündən yavaşdır, ki FastAPI bunların üzərinə qurulmuş bir framework-dür. (*) |
|||
|
|||
Ətraflı məlumat üçün bu bölməyə nəzər salın <a href="https://fastapi.tiangolo.com/az/benchmarks/" class="internal-link" target="_blank"><abbr title="Müqayisələr: Benchmarks">Müqayisələr</abbr></a>. |
|||
|
|||
## Məcburi Olmayan Tələblər |
|||
|
|||
Pydantic tərəfindən istifadə olunanlar: |
|||
|
|||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - e-poçtun yoxlanılması üçün. |
|||
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - parametrlərin idarə edilməsi üçün. |
|||
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - Pydantic ilə istifadə edilə bilən əlavə tiplər üçün. |
|||
|
|||
Starlette tərəfindən istifadə olunanlar: |
|||
|
|||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Əgər `TestClient` strukturundan istifadə edəcəksinizsə, tələb olunur. |
|||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Standart <abbr title="Şablon: Template">şablon</abbr> konfiqurasiyasından istifadə etmək istəyirsinizsə, tələb olunur. |
|||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - `request.form()` ilə forma <abbr title="HTTP sorğusu ilə alınan string məlumatın Python obyektinə çevrilməsi">"çevirmə"</abbr> dəstəyindən istifadə etmək istəyirsinizsə, tələb olunur. |
|||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` dəstəyi üçün tələb olunur. |
|||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - `SchemaGenerator` dəstəyi üçün tələb olunur (Çox güman ki, FastAPI istifadə edərkən buna ehtiyacınız olmayacaq). |
|||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - `UJSONResponse` istifadə etmək istəyirsinizsə, tələb olunur. |
|||
|
|||
Həm FastAPI, həm də Starlette tərəfindən istifadə olunur: |
|||
|
|||
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - Yaratdığımız proqramı servis edəcək veb server kimi fəaliyyət göstərir. |
|||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - `ORJSONResponse` istifadə edəcəksinizsə tələb olunur. |
|||
|
|||
Bütün bunları `pip install fastapi[all]` ilə quraşdıra bilərsiniz. |
|||
|
|||
## Lisenziya |
|||
|
|||
Bu layihə MIT lisenziyasının şərtlərinə əsasən lisenziyalaşdırılıb. |
@ -0,0 +1 @@ |
|||
INHERIT: ../en/mkdocs.yml |
@ -0,0 +1,34 @@ |
|||
# Benchmarks |
|||
|
|||
Unabhängige TechEmpower-Benchmarks zeigen, **FastAPI**-Anwendungen, die unter Uvicorn ausgeführt werden, gehören zu <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">den schnellsten existierenden Python-Frameworks</a>, nur Starlette und Uvicorn selbst (intern von FastAPI verwendet) sind schneller. |
|||
|
|||
Beim Ansehen von Benchmarks und Vergleichen sollten Sie jedoch Folgende Punkte beachten. |
|||
|
|||
## Benchmarks und Geschwindigkeit |
|||
|
|||
Wenn Sie sich die Benchmarks ansehen, werden häufig mehrere Tools mit unterschiedlichen Eigenschaften als gleichwertig verglichen. |
|||
|
|||
Konkret geht es darum, Uvicorn, Starlette und FastAPI miteinander zu vergleichen (neben vielen anderen Tools). |
|||
|
|||
Je einfacher das Problem, welches durch das Tool gelöst wird, desto besser ist die Performanz. Und die meisten Benchmarks testen nicht die zusätzlichen Funktionen, welche das Tool bietet. |
|||
|
|||
Die Hierarchie ist wie folgt: |
|||
|
|||
* **Uvicorn**: ein ASGI-Server |
|||
* **Starlette**: (verwendet Uvicorn) ein Web-Mikroframework |
|||
* **FastAPI**: (verwendet Starlette) ein API-Mikroframework mit mehreren zusätzlichen Funktionen zum Erstellen von APIs, mit Datenvalidierung, usw. |
|||
|
|||
* **Uvicorn**: |
|||
* Bietet die beste Leistung, da außer dem Server selbst nicht viel zusätzlicher Code vorhanden ist. |
|||
* Sie würden eine Anwendung nicht direkt in Uvicorn schreiben. Das würde bedeuten, dass Ihr Code zumindest mehr oder weniger den gesamten von Starlette (oder **FastAPI**) bereitgestellten Code enthalten müsste. Und wenn Sie das täten, hätte Ihre endgültige Anwendung den gleichen Overhead wie die Verwendung eines Frameworks nebst Minimierung Ihres Anwendungscodes und der Fehler. |
|||
* Wenn Sie Uvicorn vergleichen, vergleichen Sie es mit Anwendungsservern wie Daphne, Hypercorn, uWSGI, usw. |
|||
* **Starlette**: |
|||
* Wird nach Uvicorn die nächstbeste Performanz erbringen. Tatsächlich nutzt Starlette intern Uvicorn. Daher kann es wahrscheinlich nur „langsamer“ als Uvicorn sein, weil mehr Code ausgeführt wird. |
|||
* Aber es bietet Ihnen die Tools zum Erstellen einfacher Webanwendungen, mit Routing basierend auf Pfaden, usw. |
|||
* Wenn Sie Starlette vergleichen, vergleichen Sie es mit Webframeworks (oder Mikroframeworks) wie Sanic, Flask, Django, usw. |
|||
* **FastAPI**: |
|||
* So wie Starlette Uvicorn verwendet und nicht schneller als dieses sein kann, verwendet **FastAPI** Starlette, sodass es nicht schneller als dieses sein kann. |
|||
* FastAPI bietet zusätzlich zu Starlette weitere Funktionen. Funktionen, die Sie beim Erstellen von APIs fast immer benötigen, wie Datenvalidierung und Serialisierung. Und wenn Sie es verwenden, erhalten Sie kostenlos automatische Dokumentation (die automatische Dokumentation verursacht nicht einmal zusätzlichen Aufwand für laufende Anwendungen, sie wird beim Start generiert). |
|||
* Wenn Sie FastAPI nicht, und direkt Starlette (oder ein anderes Tool wie Sanic, Flask, Responder, usw.) verwenden würden, müssten Sie die gesamte Datenvalidierung und Serialisierung selbst implementieren. Ihre finale Anwendung hätte also immer noch den gleichen Overhead, als ob sie mit FastAPI erstellt worden wäre. Und in vielen Fällen ist diese Datenvalidierung und Serialisierung der größte Teil des in Anwendungen geschriebenen Codes. |
|||
* Durch die Verwendung von FastAPI sparen Sie also Entwicklungszeit, Fehler und Codezeilen und würden wahrscheinlich die gleiche Leistung (oder eine bessere) erzielen, die Sie hätten, wenn Sie es nicht verwenden würden (da Sie alles in Ihrem Code implementieren müssten). |
|||
* Wenn Sie FastAPI vergleichen, vergleichen Sie es mit einem Webanwendung-Framework (oder einer Reihe von Tools), welche Datenvalidierung, Serialisierung und Dokumentation bereitstellen, wie Flask-apispec, NestJS, Molten, usw. – Frameworks mit integrierter automatischer Datenvalidierung, Serialisierung und Dokumentation. |
@ -0,0 +1,115 @@ |
|||
# Body – Felder |
|||
|
|||
So wie Sie zusätzliche Validation und Metadaten in Parametern der **Pfadoperation-Funktion** mittels `Query`, `Path` und `Body` deklarieren, können Sie auch innerhalb von Pydantic-Modellen zusätzliche Validation und Metadaten deklarieren, mittels Pydantics `Field`. |
|||
|
|||
## `Field` importieren |
|||
|
|||
Importieren Sie es zuerst: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../../docs_src/body_fields/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="2" |
|||
{!> ../../../docs_src/body_fields/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../../docs_src/body_fields/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! warning "Achtung" |
|||
Beachten Sie, dass `Field` direkt von `pydantic` importiert wird, nicht von `fastapi`, wie die anderen (`Query`, `Path`, `Body`, usw.) |
|||
|
|||
## Modellattribute deklarieren |
|||
|
|||
Dann können Sie `Field` mit Modellattributen deklarieren: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="11-14" |
|||
{!> ../../../docs_src/body_fields/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="11-14" |
|||
{!> ../../../docs_src/body_fields/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="12-15" |
|||
{!> ../../../docs_src/body_fields/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="9-12" |
|||
{!> ../../../docs_src/body_fields/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="11-14" |
|||
{!> ../../../docs_src/body_fields/tutorial001.py!} |
|||
``` |
|||
|
|||
`Field` funktioniert genauso wie `Query`, `Path` und `Body`, es hat die gleichen Parameter, usw. |
|||
|
|||
!!! note "Technische Details" |
|||
Tatsächlich erstellen `Query`, `Path` und andere, die sie kennenlernen werden, Instanzen von Unterklassen einer allgemeinen Klasse `Param`, die ihrerseits eine Unterklasse von Pydantics `FieldInfo`-Klasse ist. |
|||
|
|||
Und Pydantics `Field` gibt ebenfalls eine Instanz von `FieldInfo` zurück. |
|||
|
|||
`Body` gibt auch Instanzen einer Unterklasse von `FieldInfo` zurück. Und später werden Sie andere sehen, die Unterklassen der `Body`-Klasse sind. |
|||
|
|||
Denken Sie daran, dass `Query`, `Path` und andere von `fastapi` tatsächlich Funktionen sind, die spezielle Klassen zurückgeben. |
|||
|
|||
!!! tip "Tipp" |
|||
Beachten Sie, dass jedes Modellattribut mit einem Typ, Defaultwert und `Field` die gleiche Struktur hat wie ein Parameter einer Pfadoperation-Funktion, nur mit `Field` statt `Path`, `Query`, `Body`. |
|||
|
|||
## Zusätzliche Information hinzufügen |
|||
|
|||
Sie können zusätzliche Information in `Field`, `Query`, `Body`, usw. deklarieren. Und es wird im generierten JSON-Schema untergebracht. |
|||
|
|||
Sie werden später mehr darüber lernen, wie man zusätzliche Information unterbringt, wenn Sie lernen, Beispiele zu deklarieren. |
|||
|
|||
!!! warning "Achtung" |
|||
Extra-Schlüssel, die `Field` überreicht werden, werden auch im resultierenden OpenAPI-Schema Ihrer Anwendung gelistet. Da diese Schlüssel nicht notwendigerweise Teil der OpenAPI-Spezifikation sind, könnten einige OpenAPI-Tools, wie etwa [der OpenAPI-Validator](https://validator.swagger.io/), nicht mit Ihrem generierten Schema funktionieren. |
|||
|
|||
## Zusammenfassung |
|||
|
|||
Sie können Pydantics `Field` verwenden, um zusätzliche Validierungen und Metadaten für Modellattribute zu deklarieren. |
|||
|
|||
Sie können auch Extra-Schlüssel verwenden, um zusätzliche JSON-Schema-Metadaten zu überreichen. |
@ -0,0 +1,308 @@ |
|||
# Body – Mehrere Parameter |
|||
|
|||
Jetzt, da wir gesehen haben, wie `Path` und `Query` verwendet werden, schauen wir uns fortgeschrittenere Verwendungsmöglichkeiten von Requestbody-Deklarationen an. |
|||
|
|||
## `Path`-, `Query`- und Body-Parameter vermischen |
|||
|
|||
Zuerst einmal, Sie können `Path`-, `Query`- und Requestbody-Parameter-Deklarationen frei mischen und **FastAPI** wird wissen, was zu tun ist. |
|||
|
|||
Und Sie können auch Body-Parameter als optional kennzeichnen, indem Sie den Defaultwert auf `None` setzen: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="18-20" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="18-20" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="19-21" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="17-19" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="19-21" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! note "Hinweis" |
|||
Beachten Sie, dass in diesem Fall das `item`, welches vom Body genommen wird, optional ist. Da es `None` als Defaultwert hat. |
|||
|
|||
## Mehrere Body-Parameter |
|||
|
|||
Im vorherigen Beispiel erwartete die *Pfadoperation* einen JSON-Body mit den Attributen eines `Item`s, etwa: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
``` |
|||
|
|||
Aber Sie können auch mehrere Body-Parameter deklarieren, z. B. `item` und `user`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="22" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial002.py!} |
|||
``` |
|||
|
|||
In diesem Fall wird **FastAPI** bemerken, dass es mehr als einen Body-Parameter in der Funktion gibt (zwei Parameter, die Pydantic-Modelle sind). |
|||
|
|||
Es wird deshalb die Parameternamen als Schlüssel (Feldnamen) im Body verwenden, und erwartet einen Body wie folgt: |
|||
|
|||
```JSON |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
}, |
|||
"user": { |
|||
"username": "dave", |
|||
"full_name": "Dave Grohl" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
!!! note "Hinweis" |
|||
Beachten Sie, dass, obwohl `item` wie zuvor deklariert wurde, es nun unter einem Schlüssel `item` im Body erwartet wird. |
|||
|
|||
**FastAPI** wird die automatische Konvertierung des Requests übernehmen, sodass der Parameter `item` seinen spezifischen Inhalt bekommt, genau so wie der Parameter `user`. |
|||
|
|||
Es wird die Validierung dieser zusammengesetzten Daten übernehmen, und sie im OpenAPI-Schema und der automatischen Dokumentation dokumentieren. |
|||
|
|||
## Einzelne Werte im Body |
|||
|
|||
So wie `Query` und `Path` für Query- und Pfad-Parameter, hat **FastAPI** auch das Äquivalent `Body`, um Extra-Daten für Body-Parameter zu definieren. |
|||
|
|||
Zum Beispiel, das vorherige Modell erweiternd, könnten Sie entscheiden, dass Sie einen weiteren Schlüssel <abbr title="Wichtigkeit">`importance`</abbr> haben möchten, im selben Body, Seite an Seite mit `item` und `user`. |
|||
|
|||
Wenn Sie diesen Parameter einfach so hinzufügen, wird **FastAPI** annehmen, dass es ein Query-Parameter ist. |
|||
|
|||
Aber Sie können **FastAPI** instruieren, ihn als weiteren Body-Schlüssel zu erkennen, indem Sie `Body` verwenden: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="23" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial003_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="23" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial003_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="24" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial003_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="22" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial003.py!} |
|||
``` |
|||
|
|||
In diesem Fall erwartet **FastAPI** einen Body wie: |
|||
|
|||
```JSON |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
}, |
|||
"user": { |
|||
"username": "dave", |
|||
"full_name": "Dave Grohl" |
|||
}, |
|||
"importance": 5 |
|||
} |
|||
``` |
|||
|
|||
Wiederum wird es die Daten konvertieren, validieren, dokumentieren, usw. |
|||
|
|||
## Mehrere Body-Parameter und Query-Parameter |
|||
|
|||
Natürlich können Sie auch, wann immer Sie das brauchen, weitere Query-Parameter hinzufügen, zusätzlich zu den Body-Parametern. |
|||
|
|||
Da einfache Werte standardmäßig als Query-Parameter interpretiert werden, müssen Sie `Query` nicht explizit hinzufügen, Sie können einfach schreiben: |
|||
|
|||
```Python |
|||
q: Union[str, None] = None |
|||
``` |
|||
|
|||
Oder in Python 3.10 und darüber: |
|||
|
|||
```Python |
|||
q: str | None = None |
|||
``` |
|||
|
|||
Zum Beispiel: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="27" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial004_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="27" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial004_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="28" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial004_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="25" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="27" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial004.py!} |
|||
``` |
|||
|
|||
!!! info |
|||
`Body` hat die gleichen zusätzlichen Validierungs- und Metadaten-Parameter wie `Query` und `Path` und andere, die Sie später kennenlernen. |
|||
|
|||
## Einen einzelnen Body-Parameter einbetten |
|||
|
|||
Nehmen wir an, Sie haben nur einen einzelnen `item`-Body-Parameter, ein Pydantic-Modell `Item`. |
|||
|
|||
Normalerweise wird **FastAPI** dann seinen JSON-Body direkt erwarten. |
|||
|
|||
Aber wenn Sie möchten, dass es einen JSON-Body erwartet, mit einem Schlüssel `item` und darin den Inhalt des Modells, so wie es das tut, wenn Sie mehrere Body-Parameter deklarieren, dann können Sie den speziellen `Body`-Parameter `embed` setzen: |
|||
|
|||
```Python |
|||
item: Item = Body(embed=True) |
|||
``` |
|||
|
|||
so wie in: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="17" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial005_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="17" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial005_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial005_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="15" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="17" |
|||
{!> ../../../docs_src/body_multiple_params/tutorial005.py!} |
|||
``` |
|||
|
|||
In diesem Fall erwartet **FastAPI** einen Body wie: |
|||
|
|||
```JSON hl_lines="2" |
|||
{ |
|||
"item": { |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
} |
|||
``` |
|||
|
|||
statt: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2 |
|||
} |
|||
``` |
|||
|
|||
## Zusammenfassung |
|||
|
|||
Sie können mehrere Body-Parameter zu ihrer *Pfadoperation-Funktion* hinzufügen, obwohl ein Request nur einen einzigen Body enthalten kann. |
|||
|
|||
**FastAPI** wird sich darum kümmern, Ihnen korrekte Daten in Ihrer Funktion zu überreichen, und das korrekte Schema in der *Pfadoperation* zu validieren und zu dokumentieren. |
|||
|
|||
Sie können auch einzelne Werte deklarieren, die als Teil des Bodys empfangen werden. |
|||
|
|||
Und Sie können **FastAPI** instruieren, den Body in einem Schlüssel unterzubringen, selbst wenn nur ein einzelner Body-Parameter deklariert ist. |
@ -0,0 +1,382 @@ |
|||
# Body – Verschachtelte Modelle |
|||
|
|||
Mit **FastAPI** können Sie (dank Pydantic) beliebig tief verschachtelte Modelle definieren, validieren und dokumentieren. |
|||
|
|||
## Listen als Felder |
|||
|
|||
Sie können ein Attribut als Kindtyp definieren, zum Beispiel eine Python-`list`e. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/body_nested_models/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial001.py!} |
|||
``` |
|||
|
|||
Das bewirkt, dass `tags` eine Liste ist, wenngleich es nichts über den Typ der Elemente der Liste aussagt. |
|||
|
|||
## Listen mit Typ-Parametern als Felder |
|||
|
|||
Aber Python erlaubt es, Listen mit inneren Typen, auch „Typ-Parameter“ genannt, zu deklarieren. |
|||
|
|||
### `List` von `typing` importieren |
|||
|
|||
In Python 3.9 oder darüber können Sie einfach `list` verwenden, um diese Typannotationen zu deklarieren, wie wir unten sehen werden. 💡 |
|||
|
|||
In Python-Versionen vor 3.9 (3.6 und darüber), müssen Sie zuerst `List` von Pythons Standardmodul `typing` importieren. |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
### Eine `list`e mit einem Typ-Parameter deklarieren |
|||
|
|||
Um Typen wie `list`, `dict`, `tuple` mit inneren Typ-Parametern (inneren Typen) zu deklarieren: |
|||
|
|||
* Wenn Sie eine Python-Version kleiner als 3.9 verwenden, importieren Sie das Äquivalent zum entsprechenden Typ vom `typing`-Modul |
|||
* Überreichen Sie den/die inneren Typ(en) von eckigen Klammern umschlossen, `[` und `]`, als „Typ-Parameter“ |
|||
|
|||
In Python 3.9 wäre das: |
|||
|
|||
```Python |
|||
my_list: list[str] |
|||
``` |
|||
|
|||
Und in Python-Versionen vor 3.9: |
|||
|
|||
```Python |
|||
from typing import List |
|||
|
|||
my_list: List[str] |
|||
``` |
|||
|
|||
Das ist alles Standard-Python-Syntax für Typdeklarationen. |
|||
|
|||
Verwenden Sie dieselbe Standardsyntax für Modellattribute mit inneren Typen. |
|||
|
|||
In unserem Beispiel können wir also bewirken, dass `tags` spezifisch eine „Liste von Strings“ ist: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
## Set-Typen |
|||
|
|||
Aber dann denken wir darüber nach und stellen fest, dass sich die Tags nicht wiederholen sollen, es sollen eindeutige Strings sein. |
|||
|
|||
Python hat einen Datentyp speziell für Mengen eindeutiger Dinge: das <abbr title="Menge">`set`</abbr>. |
|||
|
|||
Deklarieren wir also `tags` als Set von Strings. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/body_nested_models/tutorial003_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial003_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="1 14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial003.py!} |
|||
``` |
|||
|
|||
Jetzt, selbst wenn Sie einen Request mit duplizierten Daten erhalten, werden diese zu einem Set eindeutiger Dinge konvertiert. |
|||
|
|||
Und wann immer Sie diese Daten ausgeben, selbst wenn die Quelle Duplikate hatte, wird es als Set von eindeutigen Dingen ausgegeben. |
|||
|
|||
Und es wird entsprechend annotiert/dokumentiert. |
|||
|
|||
## Verschachtelte Modelle |
|||
|
|||
Jedes Attribut eines Pydantic-Modells hat einen Typ. |
|||
|
|||
Aber dieser Typ kann selbst ein anderes Pydantic-Modell sein. |
|||
|
|||
Sie können also tief verschachtelte JSON-„Objekte“ deklarieren, mit spezifischen Attributnamen, -typen, und -validierungen. |
|||
|
|||
Alles das beliebig tief verschachtelt. |
|||
|
|||
### Ein Kindmodell definieren |
|||
|
|||
Wir können zum Beispiel ein `Image`-Modell definieren. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="7-9" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="9-11" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9-11" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
### Das Kindmodell als Typ verwenden |
|||
|
|||
Und dann können wir es als Typ eines Attributes verwenden. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
Das würde bedeuten, dass **FastAPI** einen Body erwartet wie: |
|||
|
|||
```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" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Wiederum, nur mit dieser Deklaration erhalten Sie von **FastAPI**: |
|||
|
|||
* Editor-Unterstützung (Codevervollständigung, usw.), selbst für verschachtelte Modelle |
|||
* Datenkonvertierung |
|||
* Datenvalidierung |
|||
* Automatische Dokumentation |
|||
|
|||
## Spezielle Typen und Validierungen |
|||
|
|||
Abgesehen von normalen einfachen Typen, wie `str`, `int`, `float`, usw. können Sie komplexere einfache Typen verwenden, die von `str` erben. |
|||
|
|||
Um alle Optionen kennenzulernen, die Sie haben, schauen Sie sich <a href="https://pydantic-docs.helpmanual.io/usage/types/" class="external-link" target="_blank">Pydantics Typübersicht</a> an. Sie werden im nächsten Kapitel ein paar Beispiele kennenlernen. |
|||
|
|||
Da wir zum Beispiel im `Image`-Modell ein Feld `url` haben, können wir deklarieren, dass das eine Instanz von Pydantics `HttpUrl` sein soll, anstelle eines `str`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="2 8" |
|||
{!> ../../../docs_src/body_nested_models/tutorial005_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="4 10" |
|||
{!> ../../../docs_src/body_nested_models/tutorial005_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="4 10" |
|||
{!> ../../../docs_src/body_nested_models/tutorial005.py!} |
|||
``` |
|||
|
|||
Es wird getestet, ob der String eine gültige URL ist, und als solche wird er in JSON Schema / OpenAPI dokumentiert. |
|||
|
|||
## Attribute mit Listen von Kindmodellen |
|||
|
|||
Sie können Pydantic-Modelle auch als Typen innerhalb von `list`, `set`, usw. verwenden: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body_nested_models/tutorial006_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial006_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial006.py!} |
|||
``` |
|||
|
|||
Das wird einen JSON-Body erwarten (konvertieren, validieren, dokumentieren), wie: |
|||
|
|||
```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 |
|||
Beachten Sie, dass der `images`-Schlüssel jetzt eine Liste von Bild-Objekten hat. |
|||
|
|||
## Tief verschachtelte Modelle |
|||
|
|||
Sie können beliebig tief verschachtelte Modelle definieren: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="7 12 18 21 25" |
|||
{!> ../../../docs_src/body_nested_models/tutorial007_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="9 14 20 23 27" |
|||
{!> ../../../docs_src/body_nested_models/tutorial007_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9 14 20 23 27" |
|||
{!> ../../../docs_src/body_nested_models/tutorial007.py!} |
|||
``` |
|||
|
|||
!!! info |
|||
Beachten Sie, wie `Offer` eine Liste von `Item`s hat, von denen jedes seinerseits eine optionale Liste von `Image`s hat. |
|||
|
|||
## Bodys aus reinen Listen |
|||
|
|||
Wenn Sie möchten, dass das äußerste Element des JSON-Bodys ein JSON-`array` (eine Python-`list`e) ist, können Sie den Typ im Funktionsparameter deklarieren, mit der gleichen Syntax wie in Pydantic-Modellen: |
|||
|
|||
```Python |
|||
images: List[Image] |
|||
``` |
|||
|
|||
oder in Python 3.9 und darüber: |
|||
|
|||
```Python |
|||
images: list[Image] |
|||
``` |
|||
|
|||
so wie in: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="13" |
|||
{!> ../../../docs_src/body_nested_models/tutorial008_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="15" |
|||
{!> ../../../docs_src/body_nested_models/tutorial008.py!} |
|||
``` |
|||
|
|||
## Editor-Unterstützung überall |
|||
|
|||
Und Sie erhalten Editor-Unterstützung überall. |
|||
|
|||
Selbst für Dinge in Listen: |
|||
|
|||
<img src="/img/tutorial/body-nested-models/image01.png"> |
|||
|
|||
Sie würden diese Editor-Unterstützung nicht erhalten, wenn Sie direkt mit `dict`, statt mit Pydantic-Modellen arbeiten würden. |
|||
|
|||
Aber Sie müssen sich auch nicht weiter um die Modelle kümmern, hereinkommende Dicts werden automatisch in sie konvertiert. Und was Sie zurückgeben, wird automatisch nach JSON konvertiert. |
|||
|
|||
## Bodys mit beliebigen `dict`s |
|||
|
|||
Sie können einen Body auch als `dict` deklarieren, mit Schlüsseln eines Typs und Werten eines anderen Typs. |
|||
|
|||
So brauchen Sie vorher nicht zu wissen, wie die Feld-/Attribut-Namen lauten (wie es bei Pydantic-Modellen der Fall wäre). |
|||
|
|||
Das ist nützlich, wenn Sie Schlüssel empfangen, deren Namen Sie nicht bereits kennen. |
|||
|
|||
--- |
|||
|
|||
Ein anderer nützlicher Anwendungsfall ist, wenn Sie Schlüssel eines anderen Typs haben wollen, z. B. `int`. |
|||
|
|||
Das schauen wir uns mal an. |
|||
|
|||
Im folgenden Beispiel akzeptieren Sie irgendein `dict`, solange es `int`-Schlüssel und `float`-Werte hat. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../../docs_src/body_nested_models/tutorial009_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/body_nested_models/tutorial009.py!} |
|||
``` |
|||
|
|||
!!! tip "Tipp" |
|||
Bedenken Sie, dass JSON nur `str` als Schlüssel unterstützt. |
|||
|
|||
Aber Pydantic hat automatische Datenkonvertierung. |
|||
|
|||
Das bedeutet, dass Ihre API-Clients nur Strings senden können, aber solange diese Strings nur Zahlen enthalten, wird Pydantic sie konvertieren und validieren. |
|||
|
|||
Und das `dict` welches Sie als `weights` erhalten, wird `int`-Schlüssel und `float`-Werte haben. |
|||
|
|||
## Zusammenfassung |
|||
|
|||
Mit **FastAPI** haben Sie die maximale Flexibilität von Pydantic-Modellen, während Ihr Code einfach, kurz und elegant bleibt. |
|||
|
|||
Aber mit all den Vorzügen: |
|||
|
|||
* Editor-Unterstützung (Codevervollständigung überall) |
|||
* Datenkonvertierung (auch bekannt als Parsen, Serialisierung) |
|||
* Datenvalidierung |
|||
* Schema-Dokumentation |
|||
* Automatische Dokumentation |
@ -0,0 +1,213 @@ |
|||
# Requestbody |
|||
|
|||
Wenn Sie Daten von einem <abbr title="Client: Eine Software, die sich mit einem Server verbindet.">Client</abbr> (sagen wir, einem Browser) zu Ihrer API senden, dann senden Sie diese als einen **Requestbody** (Deutsch: Anfragekörper). |
|||
|
|||
Ein **Request**body sind Daten, die vom Client zu Ihrer API gesendet werden. Ein **Response**body (Deutsch: Antwortkörper) sind Daten, die Ihre API zum Client sendet. |
|||
|
|||
Ihre API sendet fast immer einen **Response**body. Aber Clients senden nicht unbedingt immer **Request**bodys (sondern nur Metadaten). |
|||
|
|||
Um einen **Request**body zu deklarieren, verwenden Sie <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a>-Modelle mit allen deren Fähigkeiten und Vorzügen. |
|||
|
|||
!!! info |
|||
Um Daten zu versenden, sollten Sie eines von: `POST` (meistverwendet), `PUT`, `DELETE` oder `PATCH` verwenden. |
|||
|
|||
Senden Sie einen Body mit einem `GET`-Request, dann führt das laut Spezifikation zu undefiniertem Verhalten. Trotzdem wird es von FastAPI unterstützt, für sehr komplexe/extreme Anwendungsfälle. |
|||
|
|||
Da aber davon abgeraten wird, zeigt die interaktive Dokumentation mit Swagger-Benutzeroberfläche die Dokumentation für den Body auch nicht an, wenn `GET` verwendet wird. Dazwischengeschaltete Proxys unterstützen es möglicherweise auch nicht. |
|||
|
|||
## Importieren Sie Pydantics `BaseModel` |
|||
|
|||
Zuerst müssen Sie `BaseModel` von `pydantic` importieren: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="2" |
|||
{!> ../../../docs_src/body/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../../docs_src/body/tutorial001.py!} |
|||
``` |
|||
|
|||
## Erstellen Sie Ihr Datenmodell |
|||
|
|||
Dann deklarieren Sie Ihr Datenmodell als eine Klasse, die von `BaseModel` erbt. |
|||
|
|||
Verwenden Sie Standard-Python-Typen für die Klassenattribute: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="5-9" |
|||
{!> ../../../docs_src/body/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="7-11" |
|||
{!> ../../../docs_src/body/tutorial001.py!} |
|||
``` |
|||
|
|||
Wie auch bei Query-Parametern gilt, wenn ein Modellattribut einen Defaultwert hat, ist das Attribut nicht erforderlich. Ansonsten ist es erforderlich. Verwenden Sie `None`, um es als optional zu kennzeichnen. |
|||
|
|||
Zum Beispiel deklariert das obige Modell ein JSON "`object`" (oder Python-`dict`) wie dieses: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "An optional description", |
|||
"price": 45.2, |
|||
"tax": 3.5 |
|||
} |
|||
``` |
|||
|
|||
Da `description` und `tax` optional sind (mit `None` als Defaultwert), wäre folgendes JSON "`object`" auch gültig: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"price": 45.2 |
|||
} |
|||
``` |
|||
|
|||
## Deklarieren Sie es als Parameter |
|||
|
|||
Um es zu Ihrer *Pfadoperation* hinzuzufügen, deklarieren Sie es auf die gleiche Weise, wie Sie Pfad- und Query-Parameter deklariert haben: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="16" |
|||
{!> ../../../docs_src/body/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body/tutorial001.py!} |
|||
``` |
|||
|
|||
... und deklarieren Sie seinen Typ als das Modell, welches Sie erstellt haben, `Item`. |
|||
|
|||
## Resultate |
|||
|
|||
Mit nur dieser Python-Typdeklaration, wird **FastAPI**: |
|||
|
|||
* Den Requestbody als JSON lesen. |
|||
* Die entsprechenden Typen konvertieren (falls nötig). |
|||
* Diese Daten validieren. |
|||
* Wenn die Daten ungültig sind, einen klar lesbaren Fehler zurückgeben, der anzeigt, wo und was die inkorrekten Daten waren. |
|||
* Ihnen die erhaltenen Daten im Parameter `item` übergeben. |
|||
* Da Sie diesen in der Funktion als vom Typ `Item` deklariert haben, erhalten Sie die ganze Editor-Unterstützung (Autovervollständigung, usw.) für alle Attribute und deren Typen. |
|||
* Eine <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> Definition für Ihr Modell generieren, welche Sie überall sonst verwenden können, wenn es für Ihr Projekt Sinn macht. |
|||
* Diese Schemas werden Teil des generierten OpenAPI-Schemas und werden von den <abbr title="User Interface – Benutzeroberfläche">UIs</abbr> der automatischen Dokumentation verwendet. |
|||
|
|||
## Automatische Dokumentation |
|||
|
|||
Die JSON-Schemas Ihrer Modelle werden Teil ihrer OpenAPI-generierten Schemas und werden in der interaktiven API Dokumentation angezeigt: |
|||
|
|||
<img src="/img/tutorial/body/image01.png"> |
|||
|
|||
Und werden auch verwendet in der API-Dokumentation innerhalb jeder *Pfadoperation*, welche sie braucht: |
|||
|
|||
<img src="/img/tutorial/body/image02.png"> |
|||
|
|||
## Editor Unterstützung |
|||
|
|||
In Ihrem Editor, innerhalb Ihrer Funktion, erhalten Sie Typhinweise und Code-Vervollständigung überall (was nicht der Fall wäre, wenn Sie ein `dict` anstelle eines Pydantic Modells erhalten hätten): |
|||
|
|||
<img src="/img/tutorial/body/image03.png"> |
|||
|
|||
Sie bekommen auch Fehler-Meldungen für inkorrekte Typoperationen: |
|||
|
|||
<img src="/img/tutorial/body/image04.png"> |
|||
|
|||
Das ist nicht zufällig so, das ganze Framework wurde um dieses Design herum aufgebaut. |
|||
|
|||
Und es wurde in der Designphase gründlich getestet, vor der Implementierung, um sicherzustellen, dass es mit jedem Editor funktioniert. |
|||
|
|||
Es gab sogar ein paar Änderungen an Pydantic selbst, um das zu unterstützen. |
|||
|
|||
Die vorherigen Screenshots zeigten <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>. |
|||
|
|||
Aber Sie bekommen die gleiche Editor-Unterstützung in <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> und in den meisten anderen Python-Editoren: |
|||
|
|||
<img src="/img/tutorial/body/image05.png"> |
|||
|
|||
!!! tip "Tipp" |
|||
Wenn Sie <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> als Ihren Editor verwenden, probieren Sie das <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a> aus. |
|||
|
|||
Es verbessert die Editor-Unterstützung für Pydantic-Modelle, mit: |
|||
|
|||
* Code-Vervollständigung |
|||
* Typüberprüfungen |
|||
* Refaktorisierung |
|||
* Suchen |
|||
* Inspektionen |
|||
|
|||
## Das Modell verwenden |
|||
|
|||
Innerhalb der Funktion können Sie alle Attribute des Modells direkt verwenden: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/body/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="21" |
|||
{!> ../../../docs_src/body/tutorial002.py!} |
|||
``` |
|||
|
|||
## Requestbody- + Pfad-Parameter |
|||
|
|||
Sie können Pfad- und Requestbody-Parameter gleichzeitig deklarieren. |
|||
|
|||
**FastAPI** erkennt, dass Funktionsparameter, die mit Pfad-Parametern übereinstimmen, **vom Pfad genommen** werden sollen, und dass Funktionsparameter, welche Pydantic-Modelle sind, **vom Requestbody genommen** werden sollen. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="15-16" |
|||
{!> ../../../docs_src/body/tutorial003_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="17-18" |
|||
{!> ../../../docs_src/body/tutorial003.py!} |
|||
``` |
|||
|
|||
## Requestbody- + Pfad- + Query-Parameter |
|||
|
|||
Sie können auch zur gleichen Zeit **Body-**, **Pfad-** und **Query-Parameter** deklarieren. |
|||
|
|||
**FastAPI** wird jeden Parameter korrekt erkennen und die Daten vom richtigen Ort holen. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="16" |
|||
{!> ../../../docs_src/body/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body/tutorial004.py!} |
|||
``` |
|||
|
|||
Die Funktionsparameter werden wie folgt erkannt: |
|||
|
|||
* Wenn der Parameter auch im **Pfad** deklariert wurde, wird er als Pfad-Parameter interpretiert. |
|||
* Wenn der Parameter ein **einfacher Typ** ist (wie `int`, `float`, `str`, `bool`, usw.), wird er als **Query**-Parameter interpretiert. |
|||
* Wenn der Parameter vom Typ eines **Pydantic-Modells** ist, wird er als Request**body** interpretiert. |
|||
|
|||
!!! note "Hinweis" |
|||
FastAPI weiß, dass der Wert von `q` nicht erforderlich ist, wegen des definierten Defaultwertes `= None` |
|||
|
|||
Das `Union` in `Union[str, None]` wird von FastAPI nicht verwendet, aber es erlaubt Ihrem Editor, Sie besser zu unterstützen und Fehler zu erkennen. |
|||
|
|||
## Ohne Pydantic |
|||
|
|||
Wenn Sie keine Pydantic-Modelle verwenden wollen, können Sie auch **Body**-Parameter nehmen. Siehe die Dokumentation unter [Body – Mehrere Parameter: Einfache Werte im Body](body-multiple-params.md#einzelne-werte-im-body){.internal-link target=\_blank}. |
@ -0,0 +1,226 @@ |
|||
# Query-Parameter |
|||
|
|||
Wenn Sie in ihrer Funktion Parameter deklarieren, die nicht Teil der Pfad-Parameter sind, dann werden diese automatisch als „Query“-Parameter interpretiert. |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/query_params/tutorial001.py!} |
|||
``` |
|||
|
|||
Query-Parameter (Deutsch: Abfrage-Parameter) sind die Schlüssel-Wert-Paare, die nach dem `?` in einer URL aufgelistet sind, getrennt durch `&`-Zeichen. |
|||
|
|||
Zum Beispiel sind in der URL: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/?skip=0&limit=10 |
|||
``` |
|||
|
|||
... die Query-Parameter: |
|||
|
|||
* `skip`: mit dem Wert `0` |
|||
* `limit`: mit dem Wert `10` |
|||
|
|||
Da sie Teil der URL sind, sind sie „naturgemäß“ Strings. |
|||
|
|||
Aber wenn Sie sie mit Python-Typen deklarieren (im obigen Beispiel als `int`), werden sie zu diesem Typ konvertiert, und gegen diesen validiert. |
|||
|
|||
Die gleichen Prozesse, die für Pfad-Parameter stattfinden, werden auch auf Query-Parameter angewendet: |
|||
|
|||
* Editor Unterstützung (natürlich) |
|||
* <abbr title="Konvertieren des Strings, der von einer HTTP-Anfrage kommt, in Python-Daten">„Parsen“</abbr> der Daten |
|||
* Datenvalidierung |
|||
* Automatische Dokumentation |
|||
|
|||
## Defaultwerte |
|||
|
|||
Da Query-Parameter nicht ein festgelegter Teil des Pfades sind, können sie optional sein und Defaultwerte haben. |
|||
|
|||
Im obigen Beispiel haben sie die Defaultwerte `skip=0` und `limit=10`. |
|||
|
|||
Wenn Sie also zur URL: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/ |
|||
``` |
|||
|
|||
gehen, so ist das das gleiche wie die URL: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/?skip=0&limit=10 |
|||
``` |
|||
|
|||
Aber wenn Sie zum Beispiel zu: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/?skip=20 |
|||
``` |
|||
|
|||
gehen, werden die Parameter-Werte Ihrer Funktion sein: |
|||
|
|||
* `skip=20`: da Sie das in der URL gesetzt haben |
|||
* `limit=10`: weil das der Defaultwert ist |
|||
|
|||
## Optionale Parameter |
|||
|
|||
Auf die gleiche Weise können Sie optionale Query-Parameter deklarieren, indem Sie deren Defaultwert auf `None` setzen: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../../docs_src/query_params/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/query_params/tutorial002.py!} |
|||
``` |
|||
|
|||
In diesem Fall wird der Funktionsparameter `q` optional, und standardmäßig `None` sein. |
|||
|
|||
!!! check |
|||
Beachten Sie auch, dass **FastAPI** intelligent genug ist, um zu erkennen, dass `item_id` ein Pfad-Parameter ist und `q` keiner, daher muss letzteres ein Query-Parameter sein. |
|||
|
|||
## Query-Parameter Typkonvertierung |
|||
|
|||
Sie können auch `bool`-Typen deklarieren und sie werden konvertiert: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../../docs_src/query_params/tutorial003_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/query_params/tutorial003.py!} |
|||
``` |
|||
|
|||
Wenn Sie nun zu: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=1 |
|||
``` |
|||
|
|||
oder |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=True |
|||
``` |
|||
|
|||
oder |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=true |
|||
``` |
|||
|
|||
oder |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=on |
|||
``` |
|||
|
|||
oder |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo?short=yes |
|||
``` |
|||
|
|||
gehen, oder zu irgendeiner anderen Variante der Groß-/Kleinschreibung (Alles groß, Anfangsbuchstabe groß, usw.), dann wird Ihre Funktion den Parameter `short` mit dem `bool`-Wert `True` sehen, ansonsten mit dem Wert `False`. |
|||
|
|||
## Mehrere Pfad- und Query-Parameter |
|||
|
|||
Sie können mehrere Pfad-Parameter und Query-Parameter gleichzeitig deklarieren, **FastAPI** weiß, was welches ist. |
|||
|
|||
Und Sie müssen sie auch nicht in einer spezifischen Reihenfolge deklarieren. |
|||
|
|||
Parameter werden anhand ihres Namens erkannt: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="6 8" |
|||
{!> ../../../docs_src/query_params/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="8 10" |
|||
{!> ../../../docs_src/query_params/tutorial004.py!} |
|||
``` |
|||
|
|||
## Erforderliche Query-Parameter |
|||
|
|||
Wenn Sie einen Defaultwert für Nicht-Pfad-Parameter deklarieren (Bis jetzt haben wir nur Query-Parameter gesehen), dann ist der Parameter nicht erforderlich. |
|||
|
|||
Wenn Sie keinen spezifischen Wert haben wollen, sondern der Parameter einfach optional sein soll, dann setzen Sie den Defaultwert auf `None`. |
|||
|
|||
Aber wenn Sie wollen, dass ein Query-Parameter erforderlich ist, vergeben Sie einfach keinen Defaultwert: |
|||
|
|||
```Python hl_lines="6-7" |
|||
{!../../../docs_src/query_params/tutorial005.py!} |
|||
``` |
|||
|
|||
Hier ist `needy` ein erforderlicher Query-Parameter vom Typ `str`. |
|||
|
|||
Wenn Sie in Ihrem Browser eine URL wie: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo-item |
|||
``` |
|||
|
|||
... öffnen, ohne den benötigten Parameter `needy`, dann erhalten Sie einen Fehler wie den folgenden: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "missing", |
|||
"loc": [ |
|||
"query", |
|||
"needy" |
|||
], |
|||
"msg": "Field required", |
|||
"input": null, |
|||
"url": "https://errors.pydantic.dev/2.1/v/missing" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
Da `needy` ein erforderlicher Parameter ist, müssen Sie ihn in der URL setzen: |
|||
|
|||
``` |
|||
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy |
|||
``` |
|||
|
|||
... Das funktioniert: |
|||
|
|||
```JSON |
|||
{ |
|||
"item_id": "foo-item", |
|||
"needy": "sooooneedy" |
|||
} |
|||
``` |
|||
|
|||
Und natürlich können Sie einige Parameter als erforderlich, einige mit Defaultwert, und einige als vollständig optional definieren: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="8" |
|||
{!> ../../../docs_src/query_params/tutorial006_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="10" |
|||
{!> ../../../docs_src/query_params/tutorial006.py!} |
|||
``` |
|||
|
|||
In diesem Fall gibt es drei Query-Parameter: |
|||
|
|||
* `needy`, ein erforderlicher `str`. |
|||
* `skip`, ein `int` mit einem Defaultwert `0`. |
|||
* `limit`, ein optionales `int`. |
|||
|
|||
!!! tip "Tipp" |
|||
Sie können auch `Enum`s verwenden, auf die gleiche Weise wie mit [Pfad-Parametern](path-params.md#vordefinierte-parameterwerte){.internal-link target=_blank}. |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 9.5 KiB |
@ -0,0 +1,33 @@ |
|||
# Enlaces Externos y Artículos |
|||
|
|||
**FastAPI** tiene una gran comunidad en constante crecimiento. |
|||
|
|||
Hay muchas publicaciones, artículos, herramientas y proyectos relacionados con **FastAPI**. |
|||
|
|||
Aquí hay una lista incompleta de algunos de ellos. |
|||
|
|||
!!! tip "Consejo" |
|||
Si tienes un artículo, proyecto, herramienta o cualquier cosa relacionada con **FastAPI** que aún no aparece aquí, crea un <a href="https://github.com/tiangolo/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">Pull Request agregándolo</a>. |
|||
|
|||
{% for section_name, section_content in external_links.items() %} |
|||
|
|||
## {{ section_name }} |
|||
|
|||
{% for lang_name, lang_content in section_content.items() %} |
|||
|
|||
### {{ lang_name }} |
|||
|
|||
{% for item in lang_content %} |
|||
|
|||
* <a href="{{ item.link }}" class="external-link" target="_blank">{{ item.title }}</a> by <a href="{{ item.author_link }}" class="external-link" target="_blank">{{ item.author }}</a>. |
|||
|
|||
{% endfor %} |
|||
{% endfor %} |
|||
{% endfor %} |
|||
|
|||
## Projects |
|||
|
|||
Últimos proyectos de GitHub con el tema `fastapi`: |
|||
|
|||
<div class="github-topic-projects"> |
|||
</div> |
@ -0,0 +1,5 @@ |
|||
# Boletín de Noticias de FastAPI y amigos |
|||
|
|||
<iframe data-w-type="embedded" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://xr4n4.mjt.lu/wgt/xr4n4/hj5/form?c=40a44fa4" width="100%" style="height: 0;"></iframe> |
|||
|
|||
<script type="text/javascript" src="https://app.mailjet.com/pas-nc-embedded-v1.js"></script> |
@ -0,0 +1,59 @@ |
|||
# میانافزار - middleware |
|||
|
|||
شما میتوانید میانافزارها را در **FastAPI** اضافه کنید. |
|||
|
|||
"میانافزار" یک تابع است که با هر درخواست(request) قبل از پردازش توسط هر path operation (عملیات مسیر) خاص کار میکند. همچنین با هر پاسخ(response) قبل از بازگشت آن نیز کار میکند. |
|||
|
|||
* هر **درخواستی (request)** که به برنامه شما می آید را می گیرد. |
|||
* سپس می تواند کاری برای آن **درخواست** انجام دهید یا هر کد مورد نیازتان را اجرا کنید. |
|||
* سپس **درخواست** را به بخش دیگری از برنامه (توسط یک path operation مشخص) برای پردازش ارسال می کند. |
|||
* سپس **پاسخ** تولید شده توسط برنامه را (توسط یک path operation مشخص) دریافت میکند. |
|||
* می تواند کاری با **پاسخ** انجام دهید یا هر کد مورد نیازتان را اجرا کند. |
|||
* سپس **پاسخ** را برمی گرداند. |
|||
|
|||
!!! توجه "جزئیات فنی" |
|||
در صورت وجود وابستگی هایی با `yield`، کد خروجی **پس از** اجرای میانافزار اجرا خواهد شد. |
|||
|
|||
در صورت وجود هر گونه وظایف پس زمینه (که در ادامه توضیح داده میشوند)، تمام میانافزارها *پس از آن* اجرا خواهند شد. |
|||
|
|||
## ساخت یک میان افزار |
|||
|
|||
برای ایجاد یک میانافزار، از دکوریتور `@app.middleware("http")` در بالای یک تابع استفاده میشود. |
|||
|
|||
تابع میان افزار دریافت می کند: |
|||
* `درخواست` |
|||
* تابع `call_next` که `درخواست` را به عنوان پارامتر دریافت می کند |
|||
* این تابع `درخواست` را به *path operation* مربوطه ارسال می کند. |
|||
* سپس `پاسخ` تولید شده توسط *path operation* مربوطه را برمیگرداند. |
|||
* شما میتوانید سپس `پاسخ` را تغییر داده و پس از آن را برگردانید. |
|||
|
|||
```Python hl_lines="8-9 11 14" |
|||
{!../../../docs_src/middleware/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! نکته به خاطر داشته باشید که هدرهای اختصاصی سفارشی را می توان با استفاده از پیشوند "X-" اضافه کرد. |
|||
|
|||
اما اگر هدرهای سفارشی دارید که میخواهید مرورگر کاربر بتواند آنها را ببیند، باید آنها را با استفاده از پارامتر `expose_headers` که در مستندات <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">CORS از Starlette</a> توضیح داده شده است، به پیکربندی CORS خود اضافه کنید. |
|||
|
|||
!!! توجه "جزئیات فنی" |
|||
شما همچنین میتوانید از `from starlette.requests import Request` استفاده کنید. |
|||
|
|||
**FastAPI** این را به عنوان یک سهولت برای شما به عنوان برنامهنویس فراهم میکند. اما این مستقیما از Starlette به دست میآید. |
|||
|
|||
### قبل و بعد از `پاسخ` |
|||
|
|||
شما میتوانید کدی را برای اجرا با `درخواست`، قبل از اینکه هر *path operation* آن را دریافت کند، اضافه کنید. |
|||
|
|||
همچنین پس از تولید `پاسخ`، قبل از بازگشت آن، میتوانید کدی را اضافه کنید. |
|||
|
|||
به عنوان مثال، میتوانید یک هدر سفارشی به نام `X-Process-Time` که شامل زمان پردازش درخواست و تولید پاسخ به صورت ثانیه است، اضافه کنید. |
|||
|
|||
```Python hl_lines="10 12-13" |
|||
{!../../../docs_src/middleware/tutorial001.py!} |
|||
``` |
|||
|
|||
## سایر میان افزار |
|||
|
|||
شما میتوانید بعداً در مورد میانافزارهای دیگر در [راهنمای کاربر پیشرفته: میانافزار پیشرفته](../advanced/middleware.md){.internal-link target=_blank} بیشتر بخوانید. |
|||
|
|||
شما در بخش بعدی در مورد این که چگونه با استفاده از یک میانافزار، <abbr title="Cross-Origin Resource Sharing">CORS</abbr> را مدیریت کنید، خواهید خواند. |
@ -0,0 +1,100 @@ |
|||
# امنیت |
|||
|
|||
روشهای مختلفی برای مدیریت امنیت، تأیید هویت و اعتبارسنجی وجود دارد. |
|||
|
|||
عموماً این یک موضوع پیچیده و "سخت" است. |
|||
|
|||
در بسیاری از فریم ورک ها و سیستمها، فقط مدیریت امنیت و تأیید هویت نیاز به تلاش و کد نویسی زیادی دارد (در بسیاری از موارد میتواند 50% یا بیشتر کل کد نوشته شده باشد). |
|||
|
|||
|
|||
فریم ورک **FastAPI** ابزارهای متعددی را در اختیار شما قرار می دهد تا به راحتی، با سرعت، به صورت استاندارد و بدون نیاز به مطالعه و یادگیری همه جزئیات امنیت، در مدیریت **امنیت** به شما کمک کند. |
|||
|
|||
اما قبل از آن، بیایید برخی از مفاهیم کوچک را بررسی کنیم. |
|||
|
|||
## عجله دارید؟ |
|||
|
|||
اگر به هیچ یک از این اصطلاحات اهمیت نمی دهید و فقط نیاز به افزودن امنیت با تأیید هویت بر اساس نام کاربری و رمز عبور دارید، *همین الان* به فصل های بعدی بروید. |
|||
|
|||
## پروتکل استاندارد OAuth2 |
|||
|
|||
پروتکل استاندارد OAuth2 یک مشخصه است که چندین روش برای مدیریت تأیید هویت و اعتبار سنجی تعریف می کند. |
|||
|
|||
این مشخصه بسیار گسترده است و چندین حالت استفاده پیچیده را پوشش می دهد. |
|||
|
|||
در آن روش هایی برای تأیید هویت با استفاده از "برنامه های شخص ثالث" وجود دارد. |
|||
|
|||
این همان چیزی است که تمامی سیستم های با "ورود با فیسبوک، گوگل، توییتر، گیت هاب" در پایین آن را استفاده می کنند. |
|||
|
|||
### پروتکل استاندارد OAuth 1 |
|||
|
|||
پروتکل استاندارد OAuth1 نیز وجود داشت که با OAuth2 خیلی متفاوت است و پیچیدگی بیشتری داشت، زیرا شامل مشخصات مستقیم در مورد رمزگذاری ارتباط بود. |
|||
|
|||
در حال حاضر OAuth1 بسیار محبوب یا استفاده شده نیست. |
|||
|
|||
پروتکل استاندارد OAuth2 روش رمزگذاری ارتباط را مشخص نمی کند، بلکه انتظار دارد که برنامه شما با HTTPS سرویس دهی شود. |
|||
|
|||
!!! نکته |
|||
در بخش در مورد **استقرار** ، شما یاد خواهید گرفت که چگونه با استفاده از Traefik و Let's Encrypt رایگان HTTPS را راه اندازی کنید. |
|||
|
|||
## استاندارد OpenID Connect |
|||
|
|||
استاندارد OpenID Connect، مشخصهای دیگر است که بر پایه **OAuth2** ساخته شده است. |
|||
|
|||
این مشخصه، به گسترش OAuth2 میپردازد و برخی مواردی که در OAuth2 نسبتاً تردید برانگیز هستند را مشخص میکند تا سعی شود آن را با سایر سیستمها قابل ارتباط کند. |
|||
|
|||
به عنوان مثال، ورود به سیستم گوگل از OpenID Connect استفاده میکند (که در زیر از OAuth2 استفاده میکند). |
|||
|
|||
اما ورود به سیستم فیسبوک، از OpenID Connect پشتیبانی نمیکند. به جای آن، نسخه خودش از OAuth2 را دارد. |
|||
|
|||
### استاندارد OpenID (نه "OpenID Connect" ) |
|||
|
|||
همچنین مشخصه "OpenID" نیز وجود داشت که سعی در حل مسائل مشابه OpenID Connect داشت، اما بر پایه OAuth2 ساخته نشده بود. |
|||
|
|||
بنابراین، یک سیستم جداگانه بود. |
|||
|
|||
اکنون این مشخصه کمتر استفاده میشود و محبوبیت زیادی ندارد. |
|||
|
|||
## استاندارد OpenAPI |
|||
|
|||
استاندارد OpenAPI (قبلاً با نام Swagger شناخته میشد) یک open specification برای ساخت APIs (که در حال حاضر جزئی از بنیاد لینوکس میباشد) است. |
|||
|
|||
فریم ورک **FastAPI** بر اساس **OpenAPI** است. |
|||
|
|||
این خاصیت، امکان دارد تا چندین رابط مستندات تعاملی خودکار(automatic interactive documentation interfaces)، تولید کد و غیره وجود داشته باشد. |
|||
|
|||
مشخصه OpenAPI روشی برای تعریف چندین "schemes" دارد. |
|||
|
|||
با استفاده از آنها، شما میتوانید از همه این ابزارهای مبتنی بر استاندارد استفاده کنید، از جمله این سیستمهای مستندات تعاملی(interactive documentation systems). |
|||
|
|||
استاندارد OpenAPI شیوههای امنیتی زیر را تعریف میکند: |
|||
|
|||
* شیوه `apiKey`: یک کلید اختصاصی برای برنامه که میتواند از موارد زیر استفاده شود: |
|||
* پارامتر جستجو. |
|||
* هدر. |
|||
* کوکی. |
|||
* شیوه `http`: سیستمهای استاندارد احراز هویت HTTP، از جمله: |
|||
* مقدار `bearer`: یک هدر `Authorization` با مقدار `Bearer` به همراه یک توکن. این از OAuth2 به ارث برده شده است. |
|||
* احراز هویت پایه HTTP. |
|||
* ویژگی HTTP Digest و غیره. |
|||
* شیوه `oauth2`: تمام روشهای OAuth2 برای مدیریت امنیت (به نام "flows"). |
|||
* چندین از این flows برای ساخت یک ارائهدهنده احراز هویت OAuth 2.0 مناسب هستند (مانند گوگل، فیسبوک، توییتر، گیتهاب و غیره): |
|||
* ویژگی `implicit` |
|||
* ویژگی `clientCredentials` |
|||
* ویژگی `authorizationCode` |
|||
* اما یک "flow" خاص وجود دارد که میتواند به طور کامل برای مدیریت احراز هویت در همان برنامه به کار رود: |
|||
* بررسی `password`: چند فصل بعدی به مثالهای این مورد خواهیم پرداخت. |
|||
* شیوه `openIdConnect`: یک روش برای تعریف نحوه کشف دادههای احراز هویت OAuth2 به صورت خودکار. |
|||
* کشف خودکار این موضوع را که در مشخصه OpenID Connect تعریف شده است، مشخص میکند. |
|||
|
|||
!!! نکته |
|||
ادغام سایر ارائهدهندگان احراز هویت/اجازهدهی مانند گوگل، فیسبوک، توییتر، گیتهاب و غیره نیز امکانپذیر و نسبتاً آسان است. |
|||
|
|||
مشکل پیچیدهترین مسئله، ساخت یک ارائهدهنده احراز هویت/اجازهدهی مانند آنها است، اما **FastAPI** ابزارهای لازم برای انجام این کار را با سهولت به شما میدهد و همه کارهای سنگین را برای شما انجام میدهد. |
|||
|
|||
## ابزارهای **FastAPI** |
|||
|
|||
فریم ورک FastAPI ابزارهایی برای هر یک از این شیوههای امنیتی در ماژول`fastapi.security` فراهم میکند که استفاده از این مکانیزمهای امنیتی را سادهتر میکند. |
|||
|
|||
در فصلهای بعدی، شما یاد خواهید گرفت که چگونه با استفاده از این ابزارهای ارائه شده توسط **FastAPI**، امنیت را به API خود اضافه کنید. |
|||
|
|||
همچنین، خواهید دید که چگونه به صورت خودکار در سیستم مستندات تعاملی ادغام میشود. |
@ -0,0 +1,464 @@ |
|||
|
|||
{!../../../docs/missing-translation.md!} |
|||
|
|||
|
|||
<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 framework, alte prestazioni, facile da imparare, rapido da implementare, pronto per il rilascio in produzione</em> |
|||
</p> |
|||
<p align="center"> |
|||
<a href="https://travis-ci.com/tiangolo/fastapi" target="_blank"> |
|||
<img src="https://travis-ci.com/tiangolo/fastapi.svg?branch=master" alt="Build Status"> |
|||
</a> |
|||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank"> |
|||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi" alt="Coverage"> |
|||
</a> |
|||
<a href="https://pypi.org/project/fastapi" target="_blank"> |
|||
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version"> |
|||
</a> |
|||
</p> |
|||
|
|||
--- |
|||
|
|||
**Documentazione**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a> |
|||
|
|||
**Codice Sorgente**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a> |
|||
|
|||
--- |
|||
|
|||
FastAPI è un web framework moderno e veloce (a prestazioni elevate) che serve a creare API con Python 3.6+ basato sulle annotazioni di tipo di Python. |
|||
|
|||
Le sue caratteristiche principali sono: |
|||
|
|||
* **Velocità**: Prestazioni molto elevate, alla pari di **NodeJS** e **Go** (grazie a Starlette e Pydantic). [Uno dei framework Python più veloci in circolazione](#performance). |
|||
* **Veloce da programmare**: Velocizza il lavoro consentendo il rilascio di nuove funzionalità tra il 200% e il 300% più rapidamente. * |
|||
* **Meno bug**: Riduce di circa il 40% gli errori che commettono gli sviluppatori durante la scrittura del codice. * |
|||
* **Intuitivo**: Grande supporto per gli editor di testo con <abbr title="anche conosciuto come auto-completamento, autocompletion, IntelliSense">autocompletamento</abbr> in ogni dove. In questo modo si può dedicare meno tempo al debugging. |
|||
* **Facile**: Progettato per essere facile da usare e imparare. Si riduce il tempo da dedicare alla lettura della documentazione. |
|||
* **Sintentico**: Minimizza la duplicazione di codice. Molteplici funzionalità, ognuna con la propria dichiarazione dei parametri. Meno errori. |
|||
* **Robusto**: Crea codice pronto per la produzione con documentazione automatica interattiva. |
|||
* **Basato sugli standard**: Basato su (e completamente compatibile con) gli open standard per le API: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (precedentemente Swagger) e <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>. |
|||
|
|||
<small>* Stima basata sull'esito di test eseguiti su codice sorgente di applicazioni rilasciate in produzione da un team interno di sviluppatori.</small> |
|||
|
|||
## Sponsor |
|||
|
|||
<!-- 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">Altri sponsor</a> |
|||
|
|||
## Recensioni |
|||
|
|||
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._" |
|||
|
|||
<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> |
|||
|
|||
--- |
|||
|
|||
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, e Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div> |
|||
|
|||
--- |
|||
|
|||
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **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> |
|||
|
|||
--- |
|||
|
|||
"_I’m over the moon excited about **FastAPI**. It’s so fun!_" |
|||
|
|||
<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> |
|||
|
|||
--- |
|||
|
|||
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._" |
|||
|
|||
<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> |
|||
|
|||
--- |
|||
|
|||
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_" |
|||
|
|||
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_" |
|||
|
|||
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <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> |
|||
|
|||
--- |
|||
|
|||
## **Typer**, la FastAPI delle CLI |
|||
|
|||
<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> |
|||
|
|||
Se stai sviluppando un'app <abbr title="Command Line Interface (interfaccia della riga di comando)">CLI</abbr> da usare nel terminale invece che una web API, ti consigliamo <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>. |
|||
|
|||
**Typer** è il fratello minore di FastAPI. Ed è stato ideato per essere la **FastAPI delle CLI**. ⌨️ 🚀 |
|||
|
|||
## Requisiti |
|||
|
|||
Python 3.6+ |
|||
|
|||
FastAPI è basata su importanti librerie: |
|||
|
|||
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> per le parti web. |
|||
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> per le parti dei dati. |
|||
|
|||
## Installazione |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install fastapi |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Per il rilascio in produzione, sarà necessario un server ASGI come <a href="http://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> oppure <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>. |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install uvicorn[standard] |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## Esempio |
|||
|
|||
### Crea un file |
|||
|
|||
* Crea un file `main.py` con: |
|||
|
|||
```Python |
|||
from fastapi import FastAPI |
|||
from typing import Optional |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
@app.get("/") |
|||
def read_root(): |
|||
return {"Hello": "World"} |
|||
|
|||
|
|||
@app.get("/items/{item_id}") |
|||
def read_item(item_id: int, q: str = Optional[None]): |
|||
return {"item_id": item_id, "q": q} |
|||
``` |
|||
|
|||
<details markdown="1"> |
|||
<summary>Oppure usa <code>async def</code>...</summary> |
|||
|
|||
Se il tuo codice usa `async` / `await`, allora usa `async def`: |
|||
|
|||
```Python hl_lines="7 12" |
|||
from fastapi import FastAPI |
|||
from typing import Optional |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
@app.get("/") |
|||
async def read_root(): |
|||
return {"Hello": "World"} |
|||
|
|||
|
|||
@app.get("/items/{item_id}") |
|||
async def read_item(item_id: int, q: Optional[str] = None): |
|||
return {"item_id": item_id, "q": q} |
|||
``` |
|||
|
|||
**Nota**: |
|||
|
|||
e vuoi approfondire, consulta la sezione _"In a hurry?"_ su <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` e `await` nella documentazione</a>. |
|||
|
|||
</details> |
|||
|
|||
### Esegui il server |
|||
|
|||
Puoi far partire il server così: |
|||
|
|||
<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>Informazioni sul comando <code>uvicorn main:app --reload</code>...</summary> |
|||
|
|||
Vediamo il comando `uvicorn main:app` in dettaglio: |
|||
|
|||
* `main`: il file `main.py` (il "modulo" Python). |
|||
* `app`: l'oggetto creato dentro `main.py` con la riga di codice `app = FastAPI()`. |
|||
* `--reload`: ricarica il server se vengono rilevati cambiamenti del codice. Usalo solo durante la fase di sviluppo. |
|||
|
|||
</details> |
|||
|
|||
### Testa l'API |
|||
|
|||
Apri il browser all'indirizzo <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>. |
|||
|
|||
Vedrai la seguente risposta JSON: |
|||
|
|||
```JSON |
|||
{"item_id": 5, "q": "somequery"} |
|||
``` |
|||
|
|||
Hai appena creato un'API che: |
|||
|
|||
* Riceve richieste HTTP sui _paths_ `/` and `/items/{item_id}`. |
|||
* Entrambi i _paths_ accettano`GET` <em>operations</em> (conosciuti anche come <abbr title="metodi HTTP">HTTP _methods_</abbr>). |
|||
* Il _path_ `/items/{item_id}` ha un _path parameter_ `item_id` che deve essere un `int`. |
|||
* Il _path_ `/items/{item_id}` ha una `str` _query parameter_ `q`. |
|||
|
|||
### Documentazione interattiva dell'API |
|||
|
|||
Adesso vai all'indirizzo <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. |
|||
|
|||
Vedrai la documentazione interattiva dell'API (offerta da <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>): |
|||
|
|||
 |
|||
|
|||
### Documentazione interattiva alternativa |
|||
|
|||
Adesso accedi all'url <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>. |
|||
|
|||
Vedrai la documentazione interattiva dell'API (offerta da <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>): |
|||
|
|||
 |
|||
|
|||
## Esempio più avanzato |
|||
|
|||
Adesso modifica il file `main.py` per ricevere un _body_ da una richiesta `PUT`. |
|||
|
|||
Dichiara il _body_ usando le annotazioni di tipo standard di Python, grazie a Pydantic. |
|||
|
|||
```Python hl_lines="2 7-10 23-25" |
|||
from fastapi import FastAPI |
|||
from pydantic import BaseModel |
|||
from typing import Optional |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class Item(BaseModel): |
|||
name: str |
|||
price: float |
|||
is_offer: bool = Optional[None] |
|||
|
|||
|
|||
@app.get("/") |
|||
def read_root(): |
|||
return {"Hello": "World"} |
|||
|
|||
|
|||
@app.get("/items/{item_id}") |
|||
def read_item(item_id: int, q: Optional[str] = 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} |
|||
``` |
|||
|
|||
Il server dovrebbe ricaricarsi in automatico (perché hai specificato `--reload` al comando `uvicorn` lanciato precedentemente). |
|||
|
|||
### Aggiornamento della documentazione interattiva |
|||
|
|||
Adesso vai su <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. |
|||
|
|||
* La documentazione interattiva dell'API verrà automaticamente aggiornata, includendo il nuovo _body_: |
|||
|
|||
 |
|||
|
|||
* Fai click sul pulsante "Try it out", che ti permette di inserire i parametri per interagire direttamente con l'API: |
|||
|
|||
 |
|||
|
|||
* Successivamente, premi sul pulsante "Execute". L'interfaccia utente comunicherà con la tua API, invierà i parametri, riceverà i risultati della richiesta, e li mostrerà sullo schermo: |
|||
|
|||
 |
|||
|
|||
### Aggiornamento della documentazione alternativa |
|||
|
|||
Ora vai su <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>. |
|||
|
|||
* Anche la documentazione alternativa dell'API mostrerà il nuovo parametro della query e il _body_: |
|||
|
|||
 |
|||
|
|||
### Riepilogo |
|||
|
|||
Ricapitolando, è sufficiente dichiarare **una sola volta** i tipi dei parametri, del body, ecc. come parametri di funzioni. |
|||
|
|||
Questo con le annotazioni per i tipi standard di Python. |
|||
|
|||
Non c'è bisogno di imparare una nuova sintassi, metodi o classi specifici a una libreria, ecc. |
|||
|
|||
È normalissimo **Python 3.6+**. |
|||
|
|||
Per esempio, per un `int`: |
|||
|
|||
```Python |
|||
item_id: int |
|||
``` |
|||
|
|||
o per un modello `Item` più complesso: |
|||
|
|||
```Python |
|||
item: Item |
|||
``` |
|||
|
|||
...e con quella singola dichiarazione hai in cambio: |
|||
|
|||
* Supporto per gli editor di testo, incluso: |
|||
* Autocompletamento. |
|||
* Controllo sulle annotazioni di tipo. |
|||
* Validazione dei dati: |
|||
* Errori chiari e automatici quando i dati sono invalidi. |
|||
* Validazione anche per gli oggetti JSON più complessi. |
|||
* <abbr title="anche noto come: serializzazione, parsing, marshalling">Conversione</abbr> dei dati di input: da risorse esterne a dati e tipi di Python. È possibile leggere da: |
|||
* JSON. |
|||
* Path parameters. |
|||
* Query parameters. |
|||
* Cookies. |
|||
* Headers. |
|||
* Form. |
|||
* File. |
|||
* <abbr title="detta anche: serialization, parsing, marshalling">Conversione</abbr> dei dati di output: converte dati e tipi di Python a dati per la rete (come JSON): |
|||
* Converte i tipi di Python (`str`, `int`, `float`, `bool`, `list`, ecc). |
|||
* Oggetti `datetime`. |
|||
* Oggetti `UUID`. |
|||
* Modelli del database. |
|||
* ...e molto di più. |
|||
* Generazione di una documentazione dell'API interattiva, con scelta dell'interfaccia grafica: |
|||
* Swagger UI. |
|||
* ReDoc. |
|||
|
|||
--- |
|||
|
|||
Tornando al precedente esempio, **FastAPI**: |
|||
|
|||
* Validerà che esiste un `item_id` nel percorso delle richieste `GET` e `PUT`. |
|||
* Validerà che `item_id` sia di tipo `int` per le richieste `GET` e `PUT`. |
|||
* Se non lo è, il client vedrà un errore chiaro e utile. |
|||
* Controllerà se ci sia un parametro opzionale chiamato `q` (per esempio `http://127.0.0.1:8000/items/foo?q=somequery`) per le richieste `GET`. |
|||
* Siccome il parametro `q` è dichiarato con `= None`, è opzionale. |
|||
* Senza il `None` sarebbe stato obbligatorio (come per il body della richiesta `PUT`). |
|||
* Per le richieste `PUT` su `/items/{item_id}`, leggerà il body come JSON, questo comprende: |
|||
* verifica che la richiesta abbia un attributo obbligatorio `name` e che sia di tipo `str`. |
|||
* verifica che la richiesta abbia un attributo obbligatorio `price` e che sia di tipo `float`. |
|||
* verifica che la richiesta abbia un attributo opzionale `is_offer` e che sia di tipo `bool`, se presente. |
|||
* Tutto questo funzionerebbe anche con oggetti JSON più complessi. |
|||
* Convertirà *da* e *a* JSON automaticamente. |
|||
* Documenterà tutto con OpenAPI, che può essere usato per: |
|||
* Sistemi di documentazione interattivi. |
|||
* Sistemi di generazione di codice dal lato client, per molti linguaggi. |
|||
* Fornirà 2 interfacce di documentazione dell'API interattive. |
|||
|
|||
--- |
|||
|
|||
Questa è solo la punta dell'iceberg, ma dovresti avere già un'idea di come il tutto funzioni. |
|||
|
|||
Prova a cambiare questa riga di codice: |
|||
|
|||
```Python |
|||
return {"item_name": item.name, "item_id": item_id} |
|||
``` |
|||
|
|||
...da: |
|||
|
|||
```Python |
|||
... "item_name": item.name ... |
|||
``` |
|||
|
|||
...a: |
|||
|
|||
```Python |
|||
... "item_price": item.price ... |
|||
``` |
|||
|
|||
...e osserva come il tuo editor di testo autocompleterà gli attributi e sarà in grado di riconoscere i loro tipi: |
|||
|
|||
 |
|||
|
|||
Per un esempio più completo che mostra più funzionalità del framework, consulta <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - Guida Utente</a>. |
|||
|
|||
**Spoiler alert**: il tutorial - Guida Utente include: |
|||
|
|||
* Dichiarazione di **parameters** da altri posti diversi come: **headers**, **cookies**, **form fields** e **files**. |
|||
* Come stabilire **vincoli di validazione** come `maximum_length` o `regex`. |
|||
* Un sistema di **<abbr title="detto anche components, resources, providers, services, injectables">Dependency Injection</abbr>** facile da usare e molto potente. |
|||
e potente. |
|||
* Sicurezza e autenticazione, incluso il supporto per **OAuth2** con **token JWT** e autenticazione **HTTP Basic**. |
|||
* Tecniche più avanzate (ma ugualmente semplici) per dichiarare **modelli JSON altamente nidificati** (grazie a Pydantic). |
|||
* E altre funzionalità (grazie a Starlette) come: |
|||
* **WebSockets** |
|||
* **GraphQL** |
|||
* test molto facili basati su `requests` e `pytest` |
|||
* **CORS** |
|||
* **Cookie Sessions** |
|||
* ...e altro ancora. |
|||
|
|||
## Prestazioni |
|||
|
|||
Benchmark indipendenti di TechEmpower mostrano che **FastAPI** basato su Uvicorn è <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">uno dei framework Python più veloci in circolazione</a>, solamente dietro a Starlette e Uvicorn (usate internamente da FastAPI). (*) |
|||
|
|||
Per approfondire, consulta la sezione <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>. |
|||
|
|||
## Dipendenze opzionali |
|||
|
|||
Usate da Pydantic: |
|||
|
|||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - per un <abbr title="convertire la stringa che proviene da una richiesta HTTP in dati Python">"parsing"</abbr> di JSON più veloce. |
|||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - per la validazione di email. |
|||
|
|||
Usate da Starlette: |
|||
|
|||
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Richiesto se vuoi usare il `TestClient`. |
|||
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Richiesto se vuoi usare `FileResponse` o `StaticFiles`. |
|||
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Richiesto se vuoi usare la configurazione template di default. |
|||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Richiesto se vuoi supportare il <abbr title="convertire la stringa che proviene da una richiesta HTTP in dati Python">"parsing"</abbr> con `request.form()`. |
|||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Richiesto per usare `SessionMiddleware`. |
|||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Richiesto per il supporto dello `SchemaGenerator` di Starlette (probabilmente non ti serve con FastAPI). |
|||
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Richiesto per il supporto di `GraphQLApp`. |
|||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Richiesto se vuoi usare `UJSONResponse`. |
|||
|
|||
Usate da FastAPI / Starlette: |
|||
|
|||
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - per il server che carica e serve la tua applicazione. |
|||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - ichiesto se vuoi usare `ORJSONResponse`. |
|||
|
|||
Puoi installarle tutte con `pip install fastapi[all]`. |
|||
|
|||
## Licenza |
|||
|
|||
Questo progetto è concesso in licenza in base ai termini della licenza MIT. |
@ -0,0 +1 @@ |
|||
INHERIT: ../en/mkdocs.yml |
@ -0,0 +1,114 @@ |
|||
# 現在のユーザーの取得 |
|||
|
|||
一つ前の章では、(依存性注入システムに基づいた)セキュリティシステムは、 *path operation関数* に `str` として `token` を与えていました: |
|||
|
|||
```Python hl_lines="10" |
|||
{!../../../docs_src/security/tutorial001.py!} |
|||
``` |
|||
|
|||
しかし、それはまだそんなに有用ではありません。 |
|||
|
|||
現在のユーザーを取得するようにしてみましょう。 |
|||
|
|||
## ユーザーモデルの作成 |
|||
|
|||
まずは、Pydanticのユーザーモデルを作成しましょう。 |
|||
|
|||
ボディを宣言するのにPydanticを使用するのと同じやり方で、Pydanticを別のどんなところでも使うことができます: |
|||
|
|||
```Python hl_lines="5 12-16" |
|||
{!../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
## 依存関係 `get_current_user` を作成 |
|||
|
|||
依存関係 `get_current_user` を作ってみましょう。 |
|||
|
|||
依存関係はサブ依存関係を持つことができるのを覚えていますか? |
|||
|
|||
`get_current_user` は前に作成した `oauth2_scheme` と同じ依存関係を持ちます。 |
|||
|
|||
以前直接 *path operation* の中でしていたのと同じように、新しい依存関係である `get_current_user` は `str` として `token` を受け取るようになります: |
|||
|
|||
```Python hl_lines="25" |
|||
{!../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
## ユーザーの取得 |
|||
|
|||
`get_current_user` は作成した(偽物の)ユーティリティ関数を使って、 `str` としてトークンを受け取り、先ほどのPydanticの `User` モデルを返却します: |
|||
|
|||
```Python hl_lines="19-22 26-27" |
|||
{!../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
## 現在のユーザーの注入 |
|||
|
|||
ですので、 `get_current_user` に対して同様に *path operation* の中で `Depends` を利用できます。 |
|||
|
|||
```Python hl_lines="31" |
|||
{!../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
Pydanticモデルの `User` として、 `current_user` の型を宣言することに注意してください。 |
|||
|
|||
その関数の中ですべての入力補完や型チェックを行う際に役に立ちます。 |
|||
|
|||
!!! tip "豆知識" |
|||
リクエストボディはPydanticモデルでも宣言できることを覚えているかもしれません。 |
|||
|
|||
ここでは `Depends` を使っているおかげで、 **FastAPI** が混乱することはありません。 |
|||
|
|||
|
|||
!!! check "確認" |
|||
依存関係システムがこのように設計されているおかげで、 `User` モデルを返却する別の依存関係(別の"dependables")を持つことができます。 |
|||
|
|||
同じデータ型を返却する依存関係は一つだけしか持てない、という制約が入ることはないのです。 |
|||
|
|||
|
|||
## 別のモデル |
|||
|
|||
これで、*path operation関数* の中で現在のユーザーを直接取得し、`Depends` を使って、 **依存性注入** レベルでセキュリティメカニズムを処理できるようになりました。 |
|||
|
|||
そして、セキュリティ要件のためにどんなモデルやデータでも利用することができます。(この場合は、 Pydanticモデルの `User`) |
|||
|
|||
しかし、特定のデータモデルやクラス、型に制限されることはありません。 |
|||
|
|||
モデルを、 `id` と `email` は持つが、 `username` は全く持たないようにしたいですか? わかりました。同じ手段でこうしたこともできます。 |
|||
|
|||
ある `str` だけを持ちたい? あるいはある `dict` だけですか? それとも、データベースクラスのモデルインスタンスを直接持ちたいですか? すべて同じやり方で機能します。 |
|||
|
|||
実際には、あなたのアプリケーションにはログインするようなユーザーはおらず、単にアクセストークンを持つロボットやボット、別のシステムがありますか?ここでも、全く同じようにすべて機能します。 |
|||
|
|||
あなたのアプリケーションに必要なのがどんな種類のモデル、どんな種類のクラス、どんな種類のデータベースであったとしても、 **FastAPI** は依存性注入システムでカバーしてくれます。 |
|||
|
|||
|
|||
## コードサイズ |
|||
|
|||
この例は冗長に見えるかもしれません。セキュリティとデータモデルユーティリティ関数および *path operations* が同じファイルに混在しているということを覚えておいてください。 |
|||
|
|||
しかし、ここに重要なポイントがあります。 |
|||
|
|||
セキュリティと依存性注入に関するものは、一度だけ書きます。 |
|||
|
|||
そして、それは好きなだけ複雑にすることができます。それでも、一箇所に、一度だけ書くのです。すべての柔軟性を備えます。 |
|||
|
|||
しかし、同じセキュリティシステムを使って何千ものエンドポイント(*path operations*)を持つことができます。 |
|||
|
|||
そして、それらエンドポイントのすべて(必要な、どの部分でも)がこうした依存関係や、あなたが作成する別の依存関係を再利用する利点を享受できるのです。 |
|||
|
|||
さらに、こうした何千もの *path operations* は、たった3行で表現できるのです: |
|||
|
|||
```Python hl_lines="30-32" |
|||
{!../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
## まとめ |
|||
|
|||
これで、 *path operation関数* の中で直接現在のユーザーを取得できるようになりました。 |
|||
|
|||
既に半分のところまで来ています。 |
|||
|
|||
あとは、 `username` と `password` を実際にそのユーザーやクライアントに送る、 *path operation* を追加する必要があるだけです。 |
|||
|
|||
次はそれを説明します。 |
@ -0,0 +1,101 @@ |
|||
# セキュリティ入門 |
|||
|
|||
セキュリティ、認証、認可を扱うには多くの方法があります。 |
|||
|
|||
そして、通常、それは複雑で「難しい」トピックです。 |
|||
|
|||
多くのフレームワークやシステムでは、セキュリティと認証を処理するだけで、膨大な労力とコードが必要になります(多くの場合、書かれた全コードの50%以上を占めることがあります)。 |
|||
|
|||
**FastAPI** は、セキュリティの仕様をすべて勉強して学ぶことなく、標準的な方法で簡単に、迅速に**セキュリティ**を扱うためのツールをいくつか提供します。 |
|||
|
|||
しかし、その前に、いくつかの小さな概念を確認しましょう。 |
|||
|
|||
## お急ぎですか? |
|||
|
|||
もし、これらの用語に興味がなく、ユーザー名とパスワードに基づく認証でセキュリティを**今すぐ**確保する必要がある場合は、次の章に進んでください。 |
|||
|
|||
## OAuth2 |
|||
|
|||
OAuth2は、認証と認可を処理するためのいくつかの方法を定義した仕様です。 |
|||
|
|||
かなり広範囲な仕様で、いくつかの複雑なユースケースをカバーしています。 |
|||
|
|||
これには「サードパーティ」を使用して認証する方法が含まれています。 |
|||
|
|||
これが、「Facebook、Google、Twitter、GitHubを使ってログイン」を使用したすべてのシステムの背後で使われている仕組みです。 |
|||
|
|||
### OAuth 1 |
|||
|
|||
OAuth 1というものもありましたが、これはOAuth2とは全く異なり、通信をどのように暗号化するかという仕様が直接的に含まれており、より複雑なものとなっています。 |
|||
|
|||
現在ではあまり普及していませんし、使われてもいません。 |
|||
|
|||
OAuth2は、通信を暗号化する方法を指定せず、アプリケーションがHTTPSで提供されることを想定しています。 |
|||
|
|||
!!! tip "豆知識" |
|||
**デプロイ**のセクションでは、TraefikとLet's Encryptを使用して、無料でHTTPSを設定する方法が紹介されています。 |
|||
|
|||
|
|||
## OpenID Connect |
|||
|
|||
OpenID Connectは、**OAuth2**をベースにした別の仕様です。 |
|||
|
|||
これはOAuth2を拡張したもので、OAuth2ではやや曖昧だった部分を明確にし、より相互運用性を高めようとしたものです。 |
|||
|
|||
例として、GoogleのログインはOpenID Connectを使用しています(これはOAuth2がベースになっています)。 |
|||
|
|||
しかし、FacebookのログインはOpenID Connectをサポートしていません。OAuth2を独自にアレンジしています。 |
|||
|
|||
### OpenID (「OpenID Connect」ではない) |
|||
|
|||
また、「OpenID」という仕様もありました。それは、**OpenID Connect**と同じことを解決しようとしたものですが、OAuth2に基づいているわけではありませんでした。 |
|||
|
|||
つまり、完全な追加システムだったのです。 |
|||
|
|||
現在ではあまり普及していませんし、使われてもいません。 |
|||
|
|||
## OpenAPI |
|||
|
|||
OpenAPI(以前はSwaggerとして知られていました)は、APIを構築するためのオープンな仕様です(現在はLinux Foundationの一部になっています)。 |
|||
|
|||
**FastAPI**は、**OpenAPI**をベースにしています。 |
|||
|
|||
それが、複数の自動対話型ドキュメント・インターフェースやコード生成などを可能にしているのです。 |
|||
|
|||
OpenAPIには、複数のセキュリティ「スキーム」を定義する方法があります。 |
|||
|
|||
それらを使用することで、これらの対話型ドキュメントシステムを含む、標準ベースのツールをすべて活用できます。 |
|||
|
|||
OpenAPIでは、以下のセキュリティスキームを定義しています: |
|||
|
|||
* `apiKey`: アプリケーション固有のキーで、これらのものから取得できます。 |
|||
* クエリパラメータ |
|||
* ヘッダー |
|||
* クッキー |
|||
* `http`: 標準的なHTTP認証システムで、これらのものを含みます。 |
|||
* `bearer`: ヘッダ `Authorization` の値が `Bearer ` で、トークンが含まれます。これはOAuth2から継承しています。 |
|||
* HTTP Basic認証 |
|||
* HTTP ダイジェスト認証など |
|||
* `oauth2`: OAuth2のセキュリティ処理方法(「フロー」と呼ばれます)のすべて。 |
|||
* これらのフローのいくつかは、OAuth 2.0認証プロバイダ(Google、Facebook、Twitter、GitHubなど)を構築するのに適しています。 |
|||
* `implicit` |
|||
* `clientCredentials` |
|||
* `authorizationCode` |
|||
* しかし、同じアプリケーション内で認証を直接処理するために完全に機能する特定の「フロー」があります。 |
|||
* `password`: 次のいくつかの章では、その例を紹介します。 |
|||
* `openIdConnect`: OAuth2認証データを自動的に発見する方法を定義できます。 |
|||
* この自動検出メカニズムは、OpenID Connectの仕様で定義されているものです。 |
|||
|
|||
|
|||
!!! tip "豆知識" |
|||
Google、Facebook、Twitter、GitHubなど、他の認証/認可プロバイダを統合することも可能で、比較的簡単です。 |
|||
|
|||
最も複雑な問題は、それらのような認証/認可プロバイダを構築することですが、**FastAPI**は、あなたのために重い仕事をこなしながら、それを簡単に行うためのツールを提供します。 |
|||
|
|||
## **FastAPI** ユーティリティ |
|||
|
|||
FastAPIは `fastapi.security` モジュールの中で、これらのセキュリティスキームごとにいくつかのツールを提供し、これらのセキュリティメカニズムを簡単に使用できるようにします。 |
|||
|
|||
次の章では、**FastAPI** が提供するこれらのツールを使って、あなたのAPIにセキュリティを追加する方法について見ていきます。 |
|||
|
|||
また、それがどのようにインタラクティブなドキュメントシステムに自動的に統合されるかも見ていきます。 |
@ -0,0 +1,203 @@ |
|||
--- |
|||
hide: |
|||
- navigation |
|||
--- |
|||
|
|||
# 기능 |
|||
|
|||
## FastAPI의 기능 |
|||
|
|||
**FastAPI**는 다음과 같은 기능을 제공합니다: |
|||
|
|||
### 개방형 표준을 기반으로 |
|||
|
|||
* <abbr title="엔드포인트, 라우트로도 알려져 있습니다">경로</abbr><abbr title="POST, GET, PUT, DELETE와 같은 HTTP 메소드로 알려져 있습니다">작동</abbr>, 매개변수, 본문 요청, 보안 그 외의 선언을 포함한 API 생성을 위한 <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> |
|||
* <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (OpenAPI 자체가 JSON Schema를 기반으로 하고 있습니다)를 사용한 자동 데이터 모델 문서화. |
|||
* 단순히 떠올려서 덧붙인 기능이 아닙니다. 세심한 검토를 거친 후, 이러한 표준을 기반으로 설계되었습니다. |
|||
* 이는 또한 다양한 언어로 자동적인 **클라이언트 코드 생성**을 사용할 수 있게 지원합니다. |
|||
|
|||
### 문서 자동화 |
|||
|
|||
대화형 API 문서와 웹 탐색 유저 인터페이스를 제공합니다. 프레임워크가 OpenAPI를 기반으로 하기에, 2가지 옵션이 기본적으로 들어간 여러 옵션이 존재합니다. |
|||
|
|||
* 대화형 탐색 <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank"><strong>Swagger UI</strong></a>를 이용해, 브라우저에서 바로 여러분의 API를 호출하거나 테스트할 수 있습니다. |
|||
|
|||
 |
|||
|
|||
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>을 이용해 API 문서화를 대체할 수 있습니다. |
|||
|
|||
 |
|||
|
|||
### 그저 현대 파이썬 |
|||
|
|||
(Pydantic 덕분에) FastAPI는 표준 **파이썬 3.6 타입** 선언에 기반하고 있습니다. 새로 배울 문법이 없습니다. 그저 표준적인 현대 파이썬입니다. |
|||
|
|||
만약 여러분이 파이썬 타입을 어떻게 사용하는지에 대한 2분 정도의 복습이 필요하다면 (비록 여러분이 FastAPI를 사용하지 않는다 하더라도), 다음의 짧은 자습서를 확인하세요: [파이썬 타입](python-types.md){.internal-link target=\_blank}. |
|||
|
|||
여러분은 타입을 이용한 표준 파이썬을 다음과 같이 적을 수 있습니다: |
|||
|
|||
```Python |
|||
from datetime import date |
|||
|
|||
from pydantic import BaseModel |
|||
|
|||
# 변수를 str로 선언 |
|||
# 그 후 함수 안에서 편집기 지원을 받으세요 |
|||
def main(user_id: str): |
|||
return user_id |
|||
|
|||
|
|||
# Pydantic 모델 |
|||
class User(BaseModel): |
|||
id: int |
|||
name: str |
|||
joined: date |
|||
``` |
|||
|
|||
위의 코드는 다음과 같이 사용될 수 있습니다: |
|||
|
|||
```Python |
|||
my_user: User = User(id=3, name="John Doe", joined="2018-07-19") |
|||
|
|||
second_user_data = { |
|||
"id": 4, |
|||
"name": "Mary", |
|||
"joined": "2018-11-30", |
|||
} |
|||
|
|||
my_second_user: User = User(**second_user_data) |
|||
``` |
|||
|
|||
!!! 정보 |
|||
`**second_user_data`가 뜻하는 것: |
|||
|
|||
`second_user_data` 딕셔너리의 키와 값을 키-값 인자로서 바로 넘겨줍니다. 다음과 동일합니다: `User(id=4, name="Mary", joined="2018-11-30")` |
|||
|
|||
### 편집기 지원 |
|||
|
|||
모든 프레임워크는 사용하기 쉽고 직관적으로 설계되었으며, 좋은 개발 경험을 보장하기 위해 개발을 시작하기도 전에 모든 결정들은 여러 편집기에서 테스트됩니다. |
|||
|
|||
최근 파이썬 개발자 설문조사에서 <a href="https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features" class="external-link" target="_blank">"자동 완성"이 가장 많이 사용되는 기능</a>이라는 것이 밝혀졌습니다. |
|||
|
|||
**FastAPI** 프레임워크의 모든 부분은 이를 충족하기 위해 설계되었습니다. 자동완성은 어느 곳에서나 작동합니다. |
|||
|
|||
여러분은 문서로 다시 돌아올 일이 거의 없을 겁니다. |
|||
|
|||
다음은 편집기가 어떻게 여러분을 도와주는지 보여줍니다: |
|||
|
|||
* <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>에서: |
|||
|
|||
 |
|||
|
|||
* <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>에서: |
|||
|
|||
 |
|||
|
|||
여러분이 이전에 불가능하다고 고려했던 코드도 완성할 수 있을 겁니다. 예를 들어, 요청에서 전달되는 (중첩될 수도 있는)JSON 본문 내부에 있는 `price` 키입니다. |
|||
|
|||
잘못된 키 이름을 적을 일도, 문서를 왔다 갔다할 일도 없으며, 혹은 마지막으로 `username` 또는 `user_name`을 사용했는지 찾기 위해 위 아래로 스크롤할 일도 없습니다. |
|||
|
|||
### 토막 정보 |
|||
|
|||
어느 곳에서나 선택적 구성이 가능한 모든 것에 합리적인 기본값이 설정되어 있습니다. 모든 매개변수는 여러분이 필요하거나, 원하는 API를 정의하기 위해 미세하게 조정할 수 있습니다. |
|||
|
|||
하지만 기본적으로 모든 것이 "그냥 작동합니다". |
|||
|
|||
### 검증 |
|||
|
|||
* 다음을 포함한, 대부분의 (혹은 모든?) 파이썬 **데이터 타입** 검증할 수 있습니다: |
|||
* JSON 객체 (`dict`). |
|||
* 아이템 타입을 정의하는 JSON 배열 (`list`). |
|||
* 최소 길이와 최대 길이를 정의하는 문자열 (`str`) 필드. |
|||
* 최솟값과 최댓값을 가지는 숫자 (`int`, `float`), 그 외. |
|||
|
|||
* 다음과 같이 더욱 이색적인 타입에 대해 검증할 수 있습니다: |
|||
* URL. |
|||
* 이메일. |
|||
* UUID. |
|||
* ...다른 것들. |
|||
|
|||
모든 검증은 견고하면서 잘 확립된 **Pydantic**에 의해 처리됩니다. |
|||
|
|||
### 보안과 인증 |
|||
|
|||
보안과 인증이 통합되어 있습니다. 데이터베이스나 데이터 모델과의 타협없이 사용할 수 있습니다. |
|||
|
|||
다음을 포함하는, 모든 보안 스키마가 OpenAPI에 정의되어 있습니다. |
|||
|
|||
* HTTP Basic. |
|||
* **OAuth2** (**JWT tokens** 또한 포함). [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=\_blank}에 있는 자습서를 확인해 보세요. |
|||
* 다음에 들어 있는 API 키: |
|||
* 헤더. |
|||
* 매개변수. |
|||
* 쿠키 및 그 외. |
|||
|
|||
추가적으로 (**세션 쿠키**를 포함한) 모든 보안 기능은 Starlette에 있습니다. |
|||
|
|||
모두 재사용할 수 있는 도구와 컴포넌트로 만들어져 있어 여러분의 시스템, 데이터 저장소, 관계형 및 NoSQL 데이터베이스 등과 쉽게 통합할 수 있습니다. |
|||
|
|||
### 의존성 주입 |
|||
|
|||
FastAPI는 사용하기 매우 간편하지만, 엄청난 <abbr title='"컴포넌트", "자원", "서비스", "제공자"로도 알려진'><strong>의존성 주입</strong></abbr>시스템을 포함하고 있습니다. |
|||
|
|||
* 의존성은 의존성을 가질수도 있어, 이를 통해 의존성의 계층이나 **의존성의 "그래프"**를 형성합니다. |
|||
* 모든 것이 프레임워크에 의해 **자동적으로 처리됩니다**. |
|||
* 모든 의존성은 요청에서 데이터를 요구하여 자동 문서화와 **경로 작동 제약을 강화할 수 있습니다**. |
|||
* 의존성에서 정의된 _경로 작동_ 매개변수에 대해서도 **자동 검증**이 이루어 집니다. |
|||
* 복잡한 사용자의 인증 시스템, **데이터베이스 연결**, 등등을 지원합니다. |
|||
* 데이터베이스, 프론트엔드 등과 관련되어 **타협하지 않아도 됩니다**. 하지만 그 모든 것과 쉽게 통합이 가능합니다. |
|||
|
|||
### 제한 없는 "플러그인" |
|||
|
|||
또는 다른 방법으로, 그것들을 사용할 필요 없이 필요한 코드만 임포트할 수 있습니다. |
|||
|
|||
어느 통합도 (의존성과 함께) 사용하기 쉽게 설계되어 있어, *경로 작동*에 사용된 것과 동일한 구조와 문법을 사용하여 2줄의 코드로 여러분의 어플리케이션에 사용할 "플러그인"을 만들 수 있습니다. |
|||
|
|||
### 테스트 결과 |
|||
|
|||
* 100% <abbr title="자동적으로 테스트된 코드의 양">테스트 범위</abbr>. |
|||
* 100% <abbr title="파이썬의 타입 어노테이션, 이를 통해 여러분의 편집기와 외부 도구는 여러분에게 더 나은 지원을 할 수 있습니다">타입이 명시된</abbr> 코드 베이스. |
|||
* 상용 어플리케이션에서의 사용. |
|||
|
|||
## Starlette 기능 |
|||
|
|||
**FastAPI**는 <a href="https://www.starlette.io/" class="external-link" target="_blank"><strong>Starlette</strong></a>를 기반으로 구축되었으며, 이와 완전히 호환됩니다. 따라서, 여러분이 보유하고 있는 어떤 추가적인 Starlette 코드도 작동할 것입니다. |
|||
|
|||
`FastAPI`는 실제로 `Starlette`의 하위 클래스입니다. 그래서, 여러분이 이미 Starlette을 알고 있거나 사용하고 있으면, 대부분의 기능이 같은 방식으로 작동할 것입니다. |
|||
|
|||
**FastAPI**를 사용하면 여러분은 **Starlette**의 기능 대부분을 얻게 될 것입니다(FastAPI가 단순히 Starlette를 강화했기 때문입니다): |
|||
|
|||
* 아주 인상적인 성능. 이는 <a href="https://github.com/encode/starlette#performance" class="external-link" target="_blank">**NodeJS**와 **Go**와 동등하게 사용 가능한 가장 빠른 파이썬 프레임워크 중 하나입니다</a>. |
|||
* **WebSocket** 지원. |
|||
* 프로세스 내의 백그라운드 작업. |
|||
* 시작과 종료 이벤트. |
|||
* HTTPX 기반 테스트 클라이언트. |
|||
* **CORS**, GZip, 정적 파일, 스트리밍 응답. |
|||
* **세션과 쿠키** 지원. |
|||
* 100% 테스트 범위. |
|||
* 100% 타입이 명시된 코드 베이스. |
|||
|
|||
## Pydantic 기능 |
|||
|
|||
**FastAPI**는 <a href="https://pydantic-docs.helpmanual.io" class="external-link" target="_blank"><strong>Pydantic</strong></a>을 기반으로 하며 Pydantic과 완벽하게 호환됩니다. 그래서 어느 추가적인 Pydantic 코드를 여러분이 가지고 있든 작동할 것입니다. |
|||
|
|||
Pydantic을 기반으로 하는, 데이터베이스를 위한 <abbr title="Object-Relational Mapper">ORM</abbr>, <abbr title="Object-Document Mapper">ODM</abbr>을 포함한 외부 라이브러리를 포함합니다. |
|||
|
|||
이는 모든 것이 자동으로 검증되기 때문에, 많은 경우에서 요청을 통해 얻은 동일한 객체를, **직접 데이터베이스로** 넘겨줄 수 있습니다. |
|||
|
|||
반대로도 마찬가지이며, 많은 경우에서 여러분은 **직접 클라이언트로** 그저 객체를 넘겨줄 수 있습니다. |
|||
|
|||
**FastAPI**를 사용하면 (모든 데이터 처리를 위해 FastAPI가 Pydantic을 기반으로 하기 있기에) **Pydantic**의 모든 기능을 얻게 됩니다: |
|||
|
|||
* **어렵지 않은 언어**: |
|||
* 새로운 스키마 정의 마이크로 언어를 배우지 않아도 됩니다. |
|||
* 여러분이 파이썬 타입을 안다면, 여러분은 Pydantic을 어떻게 사용하는지 아는 겁니다. |
|||
* 여러분의 **<abbr title="통합 개발 환경, 코드 편집기와 비슷합니다">IDE</abbr>/<abbr title="코드 에러를 확인하는 프로그램">린터</abbr>/뇌**와 잘 어울립니다: |
|||
* Pydantic 데이터 구조는 단순 여러분이 정의한 클래스의 인스턴스이기 때문에, 자동 완성, 린팅, mypy 그리고 여러분의 직관까지 여러분의 검증된 데이터와 올바르게 작동합니다. |
|||
* **복잡한 구조**를 검증합니다: |
|||
* 계층적인 Pydantic 모델, 파이썬 `typing`의 `List`와 `Dict`, 그 외를 사용합니다. |
|||
* 그리고 검증자는 복잡한 데이터 스키마를 명확하고 쉽게 정의 및 확인하며 JSON 스키마로 문서화합니다. |
|||
* 여러분은 깊게 **중첩된 JSON** 객체를 가질 수 있으며, 이 객체 모두 검증하고 설명을 붙일 수 있습니다. |
|||
* **확장 가능성**: |
|||
* Pydantic은 사용자 정의 데이터 타입을 정의할 수 있게 하거나, 검증자 데코레이터가 붙은 모델의 메소드를 사용하여 검증을 확장할 수 있습니다. |
|||
* 100% 테스트 범위. |
@ -0,0 +1,3 @@ |
|||
# 도움 |
|||
|
|||
도움을 주고 받고, 기여하고, 참여합니다. 🤝 |
@ -0,0 +1,102 @@ |
|||
# 백그라운드 작업 |
|||
|
|||
FastAPI에서는 응답을 반환한 후에 실행할 백그라운드 작업을 정의할 수 있습니다. |
|||
|
|||
백그라운드 작업은 클라이언트가 응답을 받기 위해 작업이 완료될 때까지 기다릴 필요가 없기 때문에 요청 후에 발생해야하는 작업에 매우 유용합니다. |
|||
|
|||
이러한 작업에는 다음이 포함됩니다. |
|||
|
|||
* 작업을 수행한 후 전송되는 이메일 알림 |
|||
* 이메일 서버에 연결하고 이메일을 전송하는 것은 (몇 초 정도) "느린" 경향이 있으므로, 응답은 즉시 반환하고 이메일 알림은 백그라운드에서 전송하는 게 가능합니다. |
|||
* 데이터 처리: |
|||
* 예를 들어 처리에 오랜 시간이 걸리는 데이터를 받았을 때 "Accepted" (HTTP 202)을 반환하고, 백그라운드에서 데이터를 처리할 수 있습니다. |
|||
|
|||
## `백그라운드 작업` 사용 |
|||
|
|||
먼저 아래와 같이 `BackgroundTasks`를 임포트하고, `BackgroundTasks`를 _경로 동작 함수_ 에서 매개변수로 가져오고 정의합니다. |
|||
|
|||
```Python hl_lines="1 13" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
**FastAPI** 는 `BackgroundTasks` 개체를 생성하고, 매개 변수로 전달합니다. |
|||
|
|||
## 작업 함수 생성 |
|||
|
|||
백그라운드 작업으로 실행할 함수를 정의합니다. |
|||
|
|||
이것은 단순히 매개변수를 받을 수 있는 표준 함수일 뿐입니다. |
|||
|
|||
**FastAPI**는 이것이 `async def` 함수이든, 일반 `def` 함수이든 내부적으로 이를 올바르게 처리합니다. |
|||
|
|||
이 경우, 아래 작업은 파일에 쓰는 함수입니다. (이메일 보내기 시물레이션) |
|||
|
|||
그리고 이 작업은 `async`와 `await`를 사용하지 않으므로 일반 `def` 함수로 선언합니다. |
|||
|
|||
```Python hl_lines="6-9" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
## 백그라운드 작업 추가 |
|||
|
|||
_경로 동작 함수_ 내에서 작업 함수를 `.add_task()` 함수 통해 _백그라운드 작업_ 개체에 전달합니다. |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/background_tasks/tutorial001.py!} |
|||
``` |
|||
|
|||
`.add_task()` 함수는 다음과 같은 인자를 받습니다 : |
|||
|
|||
- 백그라운드에서 실행되는 작업 함수 (`write_notification`). |
|||
- 작업 함수에 순서대로 전달되어야 하는 일련의 인자 (`email`). |
|||
- 작업 함수에 전달되어야하는 모든 키워드 인자 (`message="some notification"`). |
|||
|
|||
## 의존성 주입 |
|||
|
|||
`BackgroundTasks`를 의존성 주입 시스템과 함께 사용하면 _경로 동작 함수_, 종속성, 하위 종속성 등 여러 수준에서 BackgroundTasks 유형의 매개변수를 선언할 수 있습니다. |
|||
|
|||
**FastAPI**는 각 경우에 수행할 작업과 동일한 개체를 내부적으로 재사용하기에, 모든 백그라운드 작업이 함께 병합되고 나중에 백그라운드에서 실행됩니다. |
|||
|
|||
=== "Python 3.6 and above" |
|||
|
|||
```Python hl_lines="13 15 22 25" |
|||
{!> ../../../docs_src/background_tasks/tutorial002.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10 and above" |
|||
|
|||
```Python hl_lines="11 13 20 23" |
|||
{!> ../../../docs_src/background_tasks/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
이 예제에서는 응답이 반환된 후에 `log.txt` 파일에 메시지가 기록됩니다. |
|||
|
|||
요청에 쿼리가 있는 경우 백그라운드 작업의 로그에 기록됩니다. |
|||
|
|||
그리고 _경로 동작 함수_ 에서 생성된 또 다른 백그라운드 작업은 경로 매개 변수를 활용하여 사용하여 메시지를 작성합니다. |
|||
|
|||
## 기술적 세부사항 |
|||
|
|||
`BackgroundTasks` 클래스는 <a href="https://www.starlette.io/background/" class="external-link" target="_blank">`starlette.background`</a>에서 직접 가져옵니다. |
|||
|
|||
`BackgroundTasks` 클래스는 FastAPI에서 직접 임포트하거나 포함하기 때문에 실수로 `BackgroundTask` (끝에 `s`가 없음)을 임포트하더라도 starlette.background에서 `BackgroundTask`를 가져오는 것을 방지할 수 있습니다. |
|||
|
|||
(`BackgroundTask`가 아닌) `BackgroundTasks`를 사용하면, _경로 동작 함수_ 매개변수로 사용할 수 있게 되고 나머지는 **FastAPI**가 대신 처리하도록 할 수 있습니다. 이것은 `Request` 객체를 직접 사용하는 것과 같은 방식입니다. |
|||
|
|||
FastAPI에서 `BackgroundTask`를 단독으로 사용하는 것은 여전히 가능합니다. 하지만 객체를 코드에서 생성하고, 이 객체를 포함하는 Starlette `Response`를 반환해야 합니다. |
|||
|
|||
<a href="https://www.starlette.io/background/" class="external-link" target="_blank">`Starlette의 공식 문서`</a>에서 백그라운드 작업에 대한 자세한 내용을 확인할 수 있습니다. |
|||
|
|||
## 경고 |
|||
|
|||
만약 무거운 백그라운드 작업을 수행해야하고 동일한 프로세스에서 실행할 필요가 없는 경우 (예: 메모리, 변수 등을 공유할 필요가 없음) <a href="https://docs.celeryq.dev" class="external-link" target="_blank">`Celery`</a>와 같은 큰 도구를 사용하면 도움이 될 수 있습니다. |
|||
|
|||
RabbitMQ 또는 Redis와 같은 메시지/작업 큐 시스템 보다 복잡한 구성이 필요한 경향이 있지만, 여러 작업 프로세스를 특히 여러 서버의 백그라운드에서 실행할 수 있습니다. |
|||
|
|||
예제를 보시려면 [프로젝트 생성기](../project-generation.md){.internal-link target=\_blank} 를 참고하세요. 해당 예제에는 이미 구성된 `Celery`가 포함되어 있습니다. |
|||
|
|||
그러나 동일한 FastAPI 앱에서 변수 및 개체에 접근해야햐는 작은 백그라운드 수행이 필요한 경우 (예 : 알림 이메일 보내기) 간단하게 `BackgroundTasks`를 사용해보세요. |
|||
|
|||
## 요약 |
|||
|
|||
백그라운드 작업을 추가하기 위해 _경로 동작 함수_ 에 매개변수로 `BackgroundTasks`를 가져오고 사용합니다. |
@ -0,0 +1,213 @@ |
|||
# 요청 본문 |
|||
|
|||
클라이언트(브라우저라고 해봅시다)로부터 여러분의 API로 데이터를 보내야 할 때, **요청 본문**으로 보냅니다. |
|||
|
|||
**요청** 본문은 클라이언트에서 API로 보내지는 데이터입니다. **응답** 본문은 API가 클라이언트로 보내는 데이터입니다. |
|||
|
|||
여러분의 API는 대부분의 경우 **응답** 본문을 보내야 합니다. 하지만 클라이언트는 **요청** 본문을 매 번 보낼 필요가 없습니다. |
|||
|
|||
**요청** 본문을 선언하기 위해서 모든 강력함과 이점을 갖춘 <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> 모델을 사용합니다. |
|||
|
|||
!!! 정보 |
|||
데이터를 보내기 위해, (좀 더 보편적인) `POST`, `PUT`, `DELETE` 혹은 `PATCH` 중에 하나를 사용하는 것이 좋습니다. |
|||
|
|||
`GET` 요청에 본문을 담아 보내는 것은 명세서에 정의되지 않은 행동입니다. 그럼에도 불구하고, 이 방식은 아주 복잡한/극한의 사용 상황에서만 FastAPI에 의해 지원됩니다. |
|||
|
|||
`GET` 요청에 본문을 담는 것은 권장되지 않기에, Swagger UI같은 대화형 문서에서는 `GET` 사용시 담기는 본문에 대한 문서를 표시하지 않으며, 중간에 있는 프록시는 이를 지원하지 않을 수도 있습니다. |
|||
|
|||
## Pydantic의 `BaseModel` 임포트 |
|||
|
|||
먼저 `pydantic`에서 `BaseModel`를 임포트해야 합니다: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="2" |
|||
{!> ../../../docs_src/body/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="4" |
|||
{!> ../../../docs_src/body/tutorial001.py!} |
|||
``` |
|||
|
|||
## 여러분의 데이터 모델 만들기 |
|||
|
|||
`BaseModel`를 상속받은 클래스로 여러분의 데이터 모델을 선언합니다. |
|||
|
|||
모든 어트리뷰트에 대해 표준 파이썬 타입을 사용합니다: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="5-9" |
|||
{!> ../../../docs_src/body/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="7-11" |
|||
{!> ../../../docs_src/body/tutorial001.py!} |
|||
``` |
|||
|
|||
쿼리 매개변수를 선언할 때와 같이, 모델 어트리뷰트가 기본 값을 가지고 있어도 이는 필수가 아닙니다. 그외에는 필수입니다. 그저 `None`을 사용하여 선택적으로 만들 수 있습니다. |
|||
|
|||
예를 들면, 위의 이 모델은 JSON "`object`" (혹은 파이썬 `dict`)을 다음과 같이 선언합니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "선택적인 설명란", |
|||
"price": 45.2, |
|||
"tax": 3.5 |
|||
} |
|||
``` |
|||
|
|||
...`description`과 `tax`는 (기본 값이 `None`으로 되어 있어) 선택적이기 때문에, 이 JSON "`object`"는 다음과 같은 상황에서도 유효합니다: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"price": 45.2 |
|||
} |
|||
``` |
|||
|
|||
## 매개변수로서 선언하기 |
|||
|
|||
여러분의 *경로 작동*에 추가하기 위해, 경로 매개변수 그리고 쿼리 매개변수에서 선언했던 것과 같은 방식으로 선언하면 됩니다. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="16" |
|||
{!> ../../../docs_src/body/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body/tutorial001.py!} |
|||
``` |
|||
|
|||
...그리고 만들어낸 모델인 `Item`으로 타입을 선언합니다. |
|||
|
|||
## 결과 |
|||
|
|||
위에서의 단순한 파이썬 타입 선언으로, **FastAPI**는 다음과 같이 동작합니다: |
|||
|
|||
* 요청의 본문을 JSON으로 읽어 들입니다. |
|||
* (필요하다면) 대응되는 타입으로 변환합니다. |
|||
* 데이터를 검증합니다. |
|||
* 만약 데이터가 유효하지 않다면, 정확히 어떤 것이 그리고 어디에서 데이터가 잘 못 되었는지 지시하는 친절하고 명료한 에러를 반환할 것입니다. |
|||
* 매개변수 `item`에 포함된 수신 데이터를 제공합니다. |
|||
* 함수 내에서 매개변수를 `Item` 타입으로 선언했기 때문에, 모든 어트리뷰트와 그에 대한 타입에 대한 편집기 지원(완성 등)을 또한 받을 수 있습니다. |
|||
* 여러분의 모델을 위한 <a href="https://json-schema.org" class="external-link" target="_blank">JSON 스키마</a> 정의를 생성합니다. 여러분의 프로젝트에 적합하다면 여러분이 사용하고 싶은 곳 어디에서나 사용할 수 있습니다. |
|||
* 이러한 스키마는, 생성된 OpenAPI 스키마 일부가 될 것이며, 자동 문서화 <abbr title="사용자 인터페이스">UI</abbr>에 사용됩니다. |
|||
|
|||
## 자동 문서화 |
|||
|
|||
모델의 JSON 스키마는 생성된 OpenAPI 스키마에 포함되며 대화형 API 문서에 표시됩니다: |
|||
|
|||
<img src="/img/tutorial/body/image01.png"> |
|||
|
|||
이를 필요로 하는 각각의 *경로 작동*내부의 API 문서에도 사용됩니다: |
|||
|
|||
<img src="/img/tutorial/body/image02.png"> |
|||
|
|||
## 편집기 지원 |
|||
|
|||
편집기에서, 함수 내에서 타입 힌트와 완성을 어디서나 (만약 Pydantic model 대신에 `dict`을 받을 경우 나타나지 않을 수 있습니다) 받을 수 있습니다: |
|||
|
|||
<img src="/img/tutorial/body/image03.png"> |
|||
|
|||
잘못된 타입 연산에 대한 에러 확인도 받을 수 있습니다: |
|||
|
|||
<img src="/img/tutorial/body/image04.png"> |
|||
|
|||
단순한 우연이 아닙니다. 프레임워크 전체가 이러한 디자인을 중심으로 설계되었습니다. |
|||
|
|||
그 어떤 실행 전에, 모든 편집기에서 작동할 수 있도록 보장하기 위해 설계 단계에서 혹독하게 테스트되었습니다. |
|||
|
|||
이를 지원하기 위해 Pydantic 자체에서 몇몇 변경점이 있었습니다. |
|||
|
|||
이전 스크린샷은 <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>를 찍은 것입니다. |
|||
|
|||
하지만 똑같은 편집기 지원을 <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>에서 받을 수 있거나, 대부분의 다른 편집기에서도 받을 수 있습니다: |
|||
|
|||
<img src="/img/tutorial/body/image05.png"> |
|||
|
|||
!!! 팁 |
|||
만약 <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>를 편집기로 사용한다면, <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>을 사용할 수 있습니다. |
|||
|
|||
다음 사항을 포함해 Pydantic 모델에 대한 편집기 지원을 향상시킵니다: |
|||
|
|||
* 자동 완성 |
|||
* 타입 확인 |
|||
* 리팩토링 |
|||
* 검색 |
|||
* 점검 |
|||
|
|||
## 모델 사용하기 |
|||
|
|||
함수 안에서 모델 객체의 모든 어트리뷰트에 직접 접근 가능합니다: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="19" |
|||
{!> ../../../docs_src/body/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="21" |
|||
{!> ../../../docs_src/body/tutorial002.py!} |
|||
``` |
|||
|
|||
## 요청 본문 + 경로 매개변수 |
|||
|
|||
경로 매개변수와 요청 본문을 동시에 선언할 수 있습니다. |
|||
|
|||
**FastAPI**는 경로 매개변수와 일치하는 함수 매개변수가 **경로에서 가져와야 한다**는 것을 인지하며, Pydantic 모델로 선언된 그 함수 매개변수는 **요청 본문에서 가져와야 한다**는 것을 인지할 것입니다. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="15-16" |
|||
{!> ../../../docs_src/body/tutorial003_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="17-18" |
|||
{!> ../../../docs_src/body/tutorial003.py!} |
|||
``` |
|||
|
|||
## 요청 본문 + 경로 + 쿼리 매개변수 |
|||
|
|||
**본문**, **경로** 그리고 **쿼리** 매개변수 모두 동시에 선언할 수도 있습니다. |
|||
|
|||
**FastAPI**는 각각을 인지하고 데이터를 옳바른 위치에 가져올 것입니다. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="16" |
|||
{!> ../../../docs_src/body/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body/tutorial004.py!} |
|||
``` |
|||
|
|||
함수 매개변수는 다음을 따라서 인지하게 됩니다: |
|||
|
|||
* 만약 매개변수가 **경로**에도 선언되어 있다면, 이는 경로 매개변수로 사용될 것입니다. |
|||
* 만약 매개변수가 (`int`, `float`, `str`, `bool` 등과 같은) **유일한 타입**으로 되어있으면, **쿼리** 매개변수로 해석될 것입니다. |
|||
* 만약 매개변수가 **Pydantic 모델** 타입으로 선언되어 있으면, 요청 **본문**으로 해석될 것입니다. |
|||
|
|||
!!! 참고 |
|||
FastAPI는 `q`의 값이 필요없음을 알게 될 것입니다. 기본 값이 `= None`이기 때문입니다. |
|||
|
|||
`Union[str, None]`에 있는 `Union`은 FastAPI에 의해 사용된 것이 아니지만, 편집기로 하여금 더 나은 지원과 에러 탐지를 지원할 것입니다. |
|||
|
|||
## Pydantic없이 |
|||
|
|||
만약 Pydantic 모델을 사용하고 싶지 않다면, **Body** 매개변수를 사용할 수도 있습니다. [Body - 다중 매개변수: 본문에 있는 유일한 값](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank} 문서를 확인하세요. |
@ -0,0 +1,353 @@ |
|||
# 의존성 |
|||
|
|||
**FastAPI**는 아주 강력하지만 직관적인 **<abbr title="컴포넌트, 자원, 제공자, 서비스, 인젝터블로 알려져 있습니다">의존성 주입</abbr>** 시스템을 가지고 있습니다. |
|||
|
|||
이는 사용하기 아주 쉽게 설계했으며, 어느 개발자나 다른 컴포넌트와 **FastAPI**를 쉽게 통합할 수 있도록 만들었습니다. |
|||
|
|||
## "의존성 주입"은 무엇입니까? |
|||
|
|||
**"의존성 주입"**은 프로그래밍에서 여러분의 코드(이 경우, 경로 동작 함수)가 작동하고 사용하는 데 필요로 하는 것, 즉 "의존성"을 선언할 수 있는 방법을 의미합니다. |
|||
|
|||
그 후에, 시스템(이 경우 FastAPI)은 여러분의 코드가 요구하는 의존성을 제공하기 위해 필요한 모든 작업을 처리합니다.(의존성을 "주입"합니다) |
|||
|
|||
이는 여러분이 다음과 같은 사항을 필요로 할 때 매우 유용합니다: |
|||
|
|||
* 공용된 로직을 가졌을 경우 (같은 코드 로직이 계속 반복되는 경우). |
|||
* 데이터베이스 연결을 공유하는 경우. |
|||
* 보안, 인증, 역할 요구 사항 등을 강제하는 경우. |
|||
* 그리고 많은 다른 사항... |
|||
|
|||
이 모든 사항을 할 때 코드 반복을 최소화합니다. |
|||
|
|||
## 첫번째 단계 |
|||
|
|||
아주 간단한 예제를 봅시다. 너무 간단할 것이기에 지금 당장은 유용하지 않을 수 있습니다. |
|||
|
|||
하지만 이를 통해 **의존성 주입** 시스템이 어떻게 작동하는지에 중점을 둘 것입니다. |
|||
|
|||
### 의존성 혹은 "디펜더블" 만들기 |
|||
|
|||
의존성에 집중해 봅시다. |
|||
|
|||
*경로 작동 함수*가 가질 수 있는 모든 매개변수를 갖는 단순한 함수입니다: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="8-9" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="8-11" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9-12" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ Annotated가 없는 경우" |
|||
|
|||
!!! 팁 |
|||
가능하다면 `Annotated`가 달린 버전을 권장합니다. |
|||
|
|||
```Python hl_lines="6-7" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ Annotated가 없는 경우" |
|||
|
|||
!!! 팁 |
|||
가능하다면 `Annotated`가 달린 버전을 권장합니다. |
|||
|
|||
```Python hl_lines="8-11" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
이게 다입니다. |
|||
|
|||
**단 두 줄입니다**. |
|||
|
|||
그리고, 이 함수는 여러분의 모든 *경로 작동 함수*가 가지고 있는 것과 같은 형태와 구조를 가지고 있습니다. |
|||
|
|||
여러분은 이를 "데코레이터"가 없는 (`@app.get("/some-path")`가 없는) *경로 작동 함수*라고 생각할 수 있습니다. |
|||
|
|||
그리고 여러분이 원하는 무엇이든 반환할 수 있습니다. |
|||
|
|||
이 경우, 이 의존성은 다음과 같은 경우를 기대합니다: |
|||
|
|||
* 선택적인 쿼리 매개변수 `q`, `str`을 자료형으로 가집니다. |
|||
* 선택적인 쿼리 매개변수 `skip`, `int`를 자료형으로 가지며 기본 값은 `0`입니다. |
|||
* 선택적인 쿼리 매개변수 `limit`,`int`를 자료형으로 가지며 기본 값은 `100`입니다. |
|||
|
|||
그 후 위의 값을 포함한 `dict` 자료형으로 반환할 뿐입니다. |
|||
|
|||
!!! 정보 |
|||
FastAPI는 0.95.0 버전부터 `Annotated`에 대한 지원을 (그리고 이를 사용하기 권장합니다) 추가했습니다. |
|||
|
|||
옛날 버전을 가지고 있는 경우, `Annotated`를 사용하려 하면 에러를 맞이하게 될 것입니다. |
|||
|
|||
`Annotated`를 사용하기 전에 최소 0.95.1로 [FastAPI 버전 업그레이드](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}를 확실하게 하세요. |
|||
|
|||
### `Depends` 불러오기 |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ Annotated가 없는 경우" |
|||
|
|||
!!! 팁 |
|||
가능하다면 `Annotated`가 달린 버전을 권장합니다. |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ Annotated가 없는 경우" |
|||
|
|||
!!! 팁 |
|||
가능하다면 `Annotated`가 달린 버전을 권장합니다. |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
### "의존자"에 의존성 명시하기 |
|||
|
|||
*경로 작동 함수*의 매개변수로 `Body`, `Query` 등을 사용하는 방식과 같이 새로운 매개변수로 `Depends`를 사용합니다: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="13 18" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="15 20" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="16 21" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ Annotated가 없는 경우" |
|||
|
|||
!!! 팁 |
|||
가능하다면 `Annotated`가 달린 버전을 권장합니다. |
|||
|
|||
```Python hl_lines="11 16" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ Annotated가 없는 경우" |
|||
|
|||
!!! 팁 |
|||
가능하다면 `Annotated`가 달린 버전을 권장합니다. |
|||
|
|||
```Python hl_lines="15 20" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
비록 `Body`, `Query` 등을 사용하는 것과 같은 방식으로 여러분의 함수의 매개변수에 있는 `Depends`를 사용하지만, `Depends`는 약간 다르게 작동합니다. |
|||
|
|||
`Depends`에 단일 매개변수만 전달했습니다. |
|||
|
|||
이 매개변수는 함수같은 것이어야 합니다. |
|||
|
|||
여러분은 직접 **호출하지 않았습니다** (끝에 괄호를 치지 않았습니다), 단지 `Depends()`에 매개변수로 넘겨 줬을 뿐입니다. |
|||
|
|||
그리고 그 함수는 *경로 작동 함수*가 작동하는 것과 같은 방식으로 매개변수를 받습니다. |
|||
|
|||
!!! 팁 |
|||
여러분은 다음 장에서 함수를 제외하고서, "다른 것들"이 어떻게 의존성으로 사용되는지 알게 될 것입니다. |
|||
|
|||
새로운 요청이 도착할 때마다, **FastAPI**는 다음을 처리합니다: |
|||
|
|||
* 올바른 매개변수를 가진 의존성("디펜더블") 함수를 호출합니다. |
|||
* 함수에서 결과를 받아옵니다. |
|||
* *경로 작동 함수*에 있는 매개변수에 그 결과를 할당합니다 |
|||
|
|||
```mermaid |
|||
graph TB |
|||
|
|||
common_parameters(["common_parameters"]) |
|||
read_items["/items/"] |
|||
read_users["/users/"] |
|||
|
|||
common_parameters --> read_items |
|||
common_parameters --> read_users |
|||
``` |
|||
|
|||
이렇게 하면 공용 코드를 한번만 적어도 되며, **FastAPI**는 *경로 작동*을 위해 이에 대한 호출을 처리합니다. |
|||
|
|||
!!! 확인 |
|||
특별한 클래스를 만들지 않아도 되며, 이러한 것 혹은 비슷한 종류를 **FastAPI**에 "등록"하기 위해 어떤 곳에 넘겨주지 않아도 됩니다. |
|||
|
|||
단순히 `Depends`에 넘겨주기만 하면 되며, **FastAPI**는 나머지를 어찌할지 알고 있습니다. |
|||
|
|||
## `Annotated`인 의존성 공유하기 |
|||
|
|||
위의 예제에서 몇몇 작은 **코드 중복**이 있다는 것을 보았을 겁니다. |
|||
|
|||
`common_parameters()`의존을 사용해야 한다면, 타입 명시와 `Depends()`와 함께 전체 매개변수를 적어야 합니다: |
|||
|
|||
```Python |
|||
commons: Annotated[dict, Depends(common_parameters)] |
|||
``` |
|||
|
|||
하지만 `Annotated`를 사용하고 있기에, `Annotated` 값을 변수에 저장하고 여러 장소에서 사용할 수 있습니다: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12 16 21" |
|||
{!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="14 18 23" |
|||
{!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="15 19 24" |
|||
{!> ../../../docs_src/dependencies/tutorial001_02_an.py!} |
|||
``` |
|||
|
|||
!!! 팁 |
|||
이는 그저 표준 파이썬이고 "type alias"라고 부르며 사실 **FastAPI**에 국한되는 것은 아닙니다. |
|||
|
|||
하지만, `Annotated`를 포함하여, **FastAPI**가 파이썬 표준을 기반으로 하고 있기에, 이를 여러분의 코드 트릭으로 사용할 수 있습니다. 😎 |
|||
|
|||
이 의존성은 계속해서 예상한대로 작동할 것이며, **제일 좋은 부분**은 **타입 정보가 보존된다는 것입니다**. 즉 여러분의 편집기가 **자동 완성**, **인라인 에러** 등을 계속해서 제공할 수 있다는 것입니다. `mypy`같은 다른 도구도 마찬가지입니다. |
|||
|
|||
이는 특히 **많은 *경로 작동***에서 **같은 의존성**을 계속해서 사용하는 **거대 코드 기반**안에서 사용하면 유용할 것입니다. |
|||
|
|||
## `async`하게, 혹은 `async`하지 않게 |
|||
|
|||
의존성이 (*경로 작동 함수*에서 처럼 똑같이) **FastAPI**에 의해 호출될 수 있으며, 함수를 정의할 때 동일한 규칙이 적용됩니다. |
|||
|
|||
`async def`을 사용하거나 혹은 일반적인 `def`를 사용할 수 있습니다. |
|||
|
|||
그리고 일반적인 `def` *경로 작동 함수* 안에 `async def`로 의존성을 선언할 수 있으며, `async def` *경로 작동 함수* 안에 `def`로 의존성을 선언하는 등의 방법이 있습니다. |
|||
|
|||
아무 문제 없습니다. **FastAPI**는 무엇을 할지 알고 있습니다. |
|||
|
|||
!!! 참고 |
|||
잘 모르시겠다면, [Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank} 문서에서 `async`와 `await`에 대해 확인할 수 있습니다. |
|||
|
|||
## OpenAPI와 통합 |
|||
|
|||
모든 요청 선언, 검증과 의존성(및 하위 의존성)에 대한 요구 사항은 동일한 OpenAPI 스키마에 통합됩니다. |
|||
|
|||
따라서 대화형 문서에 이러한 의존성에 대한 모든 정보 역시 포함하고 있습니다: |
|||
|
|||
<img src="/img/tutorial/dependencies/image01.png"> |
|||
|
|||
## 간단한 사용법 |
|||
|
|||
이를 보면, *경로 작동 함수*는 *경로*와 *작동*이 매칭되면 언제든지 사용되도록 정의되었으며, **FastAPI**는 올바른 매개변수를 가진 함수를 호출하고 해당 요청에서 데이터를 추출합니다. |
|||
|
|||
사실, 모든 (혹은 대부분의) 웹 프레임워크는 이와 같은 방식으로 작동합니다. |
|||
|
|||
여러분은 이러한 함수들을 절대 직접 호출하지 않습니다. 프레임워크(이 경우 **FastAPI**)에 의해 호출됩니다. |
|||
|
|||
의존성 주입 시스템과 함께라면 **FastAPI**에게 여러분의 *경로 작동 함수*가 실행되기 전에 실행되어야 하는 무언가에 여러분의 *경로 작동 함수* 또한 "의존"하고 있음을 알릴 수 있으며, **FastAPI**는 이를 실행하고 결과를 "주입"할 것입니다. |
|||
|
|||
"의존성 주입"이라는 동일한 아이디어에 대한 다른 일반적인 용어는 다음과 같습니다: |
|||
|
|||
* 리소스 |
|||
* 제공자 |
|||
* 서비스 |
|||
* 인젝터블 |
|||
* 컴포넌트 |
|||
|
|||
## **FastAPI** 플러그인 |
|||
|
|||
통합과 "플러그인"은 **의존성 주입** 시스템을 사용하여 구축할 수 있습니다. 하지만 실제로 **"플러그인"을 만들 필요는 없습니다**, 왜냐하면 의존성을 사용함으로써 여러분의 *경로 작동 함수*에 통합과 상호 작용을 무한대로 선언할 수 있기 때문입니다. |
|||
|
|||
그리고 "말 그대로", 그저 필요로 하는 파이썬 패키지를 임포트하고 단 몇 줄의 코드로 여러분의 API 함수와 통합함으로써, 의존성을 아주 간단하고 직관적인 방법으로 만들 수 있습니다. |
|||
|
|||
관계형 및 NoSQL 데이터베이스, 보안 등, 이에 대한 예시를 다음 장에서 볼 수 있습니다. |
|||
|
|||
## **FastAPI** 호환성 |
|||
|
|||
의존성 주입 시스템의 단순함은 **FastAPI**를 다음과 같은 요소들과 호환할 수 있게 합니다: |
|||
|
|||
* 모든 관계형 데이터베이스 |
|||
* NoSQL 데이터베이스 |
|||
* 외부 패키지 |
|||
* 외부 API |
|||
* 인증 및 권한 부여 시스템 |
|||
* API 사용 모니터링 시스템 |
|||
* 응답 데이터 주입 시스템 |
|||
* 기타 등등. |
|||
|
|||
## 간편하고 강력하다 |
|||
|
|||
계층적인 의존성 주입 시스템은 정의하고 사용하기 쉽지만, 여전히 매우 강력합니다. |
|||
|
|||
여러분은 스스로를 의존하는 의존성을 정의할 수 있습니다. |
|||
|
|||
끝에는, 계층적인 나무로 된 의존성이 만들어지며, 그리고 **의존성 주입** 시스템은 (하위 의존성도 마찬가지로) 이러한 의존성들을 처리하고 각 단계마다 결과를 제공합니다(주입합니다). |
|||
|
|||
예를 들면, 여러분이 4개의 API 엔드포인트(*경로 작동*)를 가지고 있다고 해봅시다: |
|||
|
|||
* `/items/public/` |
|||
* `/items/private/` |
|||
* `/users/{user_id}/activate` |
|||
* `/items/pro/` |
|||
|
|||
그 다음 각각에 대해 그저 의존성과 하위 의존성을 사용하여 다른 권한 요구 사항을 추가할 수 있을 겁니다: |
|||
|
|||
```mermaid |
|||
graph TB |
|||
|
|||
current_user(["current_user"]) |
|||
active_user(["active_user"]) |
|||
admin_user(["admin_user"]) |
|||
paying_user(["paying_user"]) |
|||
|
|||
public["/items/public/"] |
|||
private["/items/private/"] |
|||
activate_user["/users/{user_id}/activate"] |
|||
pro_items["/items/pro/"] |
|||
|
|||
current_user --> active_user |
|||
active_user --> admin_user |
|||
active_user --> paying_user |
|||
|
|||
current_user --> public |
|||
active_user --> private |
|||
admin_user --> activate_user |
|||
paying_user --> pro_items |
|||
``` |
|||
|
|||
## **OpenAPI**와의 통합 |
|||
|
|||
이 모든 의존성은 각각의 요구사항을 선언하는 동시에, *경로 작동*에 매개변수, 검증 등을 추가합니다. |
|||
|
|||
**FastAPI**는 이 모든 것을 OpenAPI 스키마에 추가할 것이며, 이를 통해 대화형 문서 시스템에 나타날 것입니다. |
@ -0,0 +1,61 @@ |
|||
# 미들웨어 |
|||
|
|||
미들웨어를 **FastAPI** 응용 프로그램에 추가할 수 있습니다. |
|||
|
|||
"미들웨어"는 특정 *경로 작동*에 의해 처리되기 전, 모든 **요청**에 대해서 동작하는 함수입니다. 또한 모든 **응답**이 반환되기 전에도 동일하게 동작합니다. |
|||
|
|||
* 미들웨어는 응용 프로그램으로 오는 **요청**를 가져옵니다. |
|||
* **요청** 또는 다른 필요한 코드를 실행 시킬 수 있습니다. |
|||
* **요청**을 응용 프로그램의 *경로 작동*으로 전달하여 처리합니다. |
|||
* 애플리케이션의 *경로 작업*에서 생성한 **응답**를 받습니다. |
|||
* **응답** 또는 다른 필요한 코드를 실행시키는 동작을 할 수 있습니다. |
|||
* **응답**를 반환합니다. |
|||
|
|||
!!! note "기술 세부사항" |
|||
만약 `yield`를 사용한 의존성을 가지고 있다면, 미들웨어가 실행되고 난 후에 exit이 실행됩니다. |
|||
|
|||
만약 (나중에 문서에서 다룰) 백그라운드 작업이 있다면, 모든 미들웨어가 실행되고 *난 후에* 실행됩니다. |
|||
|
|||
## 미들웨어 만들기 |
|||
|
|||
미들웨어를 작성하기 위해서 함수 상단에 `@app.middleware("http")` 데코레이터를 사용할 수 있습니다. |
|||
|
|||
미들웨어 함수는 다음 항목들을 받습니다: |
|||
|
|||
* `request`. |
|||
* `request`를 매개변수로 받는 `call_next` 함수. |
|||
* 이 함수는 `request`를 해당하는 *경로 작업*으로 전달합니다. |
|||
* 그런 다음, *경로 작업*에 의해 생성된 `response` 를 반환합니다. |
|||
* `response`를 반환하기 전에 추가로 `response`를 수정할 수 있습니다. |
|||
|
|||
```Python hl_lines="8-9 11 14" |
|||
{!../../../docs_src/middleware/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! tip "팁" |
|||
사용자 정의 헤더는 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">'X-' 접두사를 사용</a>하여 추가할 수 있습니다. |
|||
|
|||
그러나 만약 클라이언트의 브라우저에서 볼 수 있는 사용자 정의 헤더를 가지고 있다면, 그것들을 CORS 설정([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})에 <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette CORS 문서</a>에 명시된 `expose_headers` 매개변수를 이용하여 헤더들을 추가하여야합니다. |
|||
|
|||
!!! note "기술적 세부사항" |
|||
`from starlette.requests import request`를 사용할 수도 있습니다. |
|||
|
|||
**FastAPI**는 개발자에게 편의를 위해 이를 제공합니다. 그러나 Starlette에서 직접 파생되었습니다. |
|||
|
|||
### `response`의 전과 후 |
|||
|
|||
*경로 작동*을 받기 전 `request`와 함께 작동할 수 있는 코드를 추가할 수 있습니다. |
|||
|
|||
그리고 `response` 또한 생성된 후 반환되기 전에 코드를 추가 할 수 있습니다. |
|||
|
|||
예를 들어, 요청을 수행하고 응답을 생성하는데 까지 걸린 시간 값을 가지고 있는 `X-Process-Time` 같은 사용자 정의 헤더를 추가할 수 있습니다. |
|||
|
|||
```Python hl_lines="10 12-13" |
|||
{!../../../docs_src/middleware/tutorial001.py!} |
|||
``` |
|||
|
|||
## 다른 미들웨어 |
|||
|
|||
미들웨어에 대한 더 많은 정보는 [숙련된 사용자 안내서: 향상된 미들웨어](../advanced/middleware.md){.internal-link target=\_blank}에서 확인할 수 있습니다. |
|||
|
|||
다음 부분에서 미들웨어와 함께 <abbr title="교차-출처 리소스 공유">CORS</abbr>를 어떻게 다루는지에 대해 확인할 것입니다. |
@ -0,0 +1,151 @@ |
|||
# 현재 사용자 가져오기 |
|||
|
|||
이전 장에서 (의존성 주입 시스템을 기반으로 한)보안 시스템은 *경로 작동 함수*에서 `str`로 `token`을 제공했습니다: |
|||
|
|||
```Python hl_lines="10" |
|||
{!../../../docs_src/security/tutorial001.py!} |
|||
``` |
|||
|
|||
그러나 아직도 유용하지 않습니다. |
|||
|
|||
현재 사용자를 제공하도록 합시다. |
|||
|
|||
## 유저 모델 생성하기 |
|||
|
|||
먼저 Pydantic 유저 모델을 만들어 보겠습니다. |
|||
|
|||
Pydantic을 사용하여 본문을 선언하는 것과 같은 방식으로 다른 곳에서 사용할 수 있습니다. |
|||
|
|||
=== "파이썬 3.7 이상" |
|||
|
|||
```Python hl_lines="5 12-16" |
|||
{!> ../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
=== "파이썬 3.10 이상" |
|||
|
|||
```Python hl_lines="3 10-14" |
|||
{!> ../../../docs_src/security/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
## `get_current_user` 의존성 생성하기 |
|||
|
|||
의존성 `get_current_user`를 만들어 봅시다. |
|||
|
|||
의존성이 하위 의존성을 가질 수 있다는 것을 기억하십니까? |
|||
|
|||
`get_current_user`는 이전에 생성한 것과 동일한 `oauth2_scheme`과 종속성을 갖게 됩니다. |
|||
|
|||
이전에 *경로 동작*에서 직접 수행했던 것과 동일하게 새 종속성 `get_current_user`는 하위 종속성 `oauth2_scheme`에서 `str`로 `token`을 수신합니다. |
|||
|
|||
=== "파이썬 3.7 이상" |
|||
|
|||
```Python hl_lines="25" |
|||
{!> ../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
=== "파이썬 3.10 이상" |
|||
|
|||
```Python hl_lines="23" |
|||
{!> ../../../docs_src/security/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
## 유저 가져오기 |
|||
|
|||
`get_current_user`는 토큰을 `str`로 취하고 Pydantic `User` 모델을 반환하는 우리가 만든 (가짜) 유틸리티 함수를 사용합니다. |
|||
|
|||
=== "파이썬 3.7 이상" |
|||
|
|||
```Python hl_lines="19-22 26-27" |
|||
{!> ../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
=== "파이썬 3.10 이상" |
|||
|
|||
```Python hl_lines="17-20 24-25" |
|||
{!> ../../../docs_src/security/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
## 현재 유저 주입하기 |
|||
|
|||
이제 *경로 작동*에서 `get_current_user`와 동일한 `Depends`를 사용할 수 있습니다. |
|||
|
|||
=== "파이썬 3.7 이상" |
|||
|
|||
```Python hl_lines="31" |
|||
{!> ../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
=== "파이썬 3.10 이상" |
|||
|
|||
```Python hl_lines="29" |
|||
{!> ../../../docs_src/security/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
Pydantic 모델인 `User`로 `current_user`의 타입을 선언하는 것을 알아야 합니다. |
|||
|
|||
이것은 모든 완료 및 타입 검사를 통해 함수 내부에서 우리를 도울 것입니다. |
|||
|
|||
!!! 팁 |
|||
요청 본문도 Pydantic 모델로 선언된다는 것을 기억할 것입니다. |
|||
|
|||
여기서 **FastAPI**는 `Depends`를 사용하고 있기 때문에 혼동되지 않습니다. |
|||
|
|||
!!! 확인 |
|||
이 의존성 시스템이 설계된 방식은 모두 `User` 모델을 반환하는 다양한 의존성(다른 "의존적인")을 가질 수 있도록 합니다. |
|||
|
|||
해당 타입의 데이터를 반환할 수 있는 의존성이 하나만 있는 것으로 제한되지 않습니다. |
|||
|
|||
## 다른 모델 |
|||
|
|||
이제 *경로 작동 함수*에서 현재 사용자를 직접 가져올 수 있으며 `Depends`를 사용하여 **의존성 주입** 수준에서 보안 메커니즘을 처리할 수 있습니다. |
|||
|
|||
그리고 보안 요구 사항에 대한 모든 모델 또는 데이터를 사용할 수 있습니다(이 경우 Pydantic 모델 `User`). |
|||
|
|||
그러나 일부 특정 데이터 모델, 클래스 또는 타입을 사용하도록 제한되지 않습니다. |
|||
|
|||
모델에 `id`와 `email`이 있고 `username`이 없길 원하십니까? 맞습니다. 이들은 동일한 도구를 사용할 수 있습니다. |
|||
|
|||
`str`만 갖고 싶습니까? 아니면 그냥 `dict`를 갖고 싶습니까? 아니면 데이터베이스 클래스 모델 인스턴스를 직접 갖고 싶습니까? 그들은 모두 같은 방식으로 작동합니다. |
|||
|
|||
실제로 애플리케이션에 로그인하는 사용자가 없지만 액세스 토큰만 있는 로봇, 봇 또는 기타 시스템이 있습니까? 다시 말하지만 모두 동일하게 작동합니다. |
|||
|
|||
애플리케이션에 필요한 모든 종류의 모델, 모든 종류의 클래스, 모든 종류의 데이터베이스를 사용하십시오. **FastAPI**는 의존성 주입 시스템을 다루었습니다. |
|||
|
|||
## 코드 사이즈 |
|||
|
|||
이 예는 장황해 보일 수 있습니다. 동일한 파일에서 보안, 데이터 모델, 유틸리티 기능 및 *경로 작동*을 혼합하고 있음을 염두에 두십시오. |
|||
|
|||
그러나 이게 키포인트입니다. |
|||
|
|||
보안과 종속성 주입 항목을 한 번만 작성하면 됩니다. |
|||
|
|||
그리고 원하는 만큼 복잡하게 만들 수 있습니다. 그래도 유연성과 함께 한 곳에 한 번에 작성할 수 있습니다. |
|||
|
|||
그러나 동일한 보안 시스템을 사용하여 수천 개의 엔드포인트(*경로 작동*)를 가질 수 있습니다. |
|||
|
|||
그리고 그들 모두(또는 원하는 부분)는 이러한 의존성 또는 생성한 다른 의존성을 재사용하는 이점을 얻을 수 있습니다. |
|||
|
|||
그리고 이 수천 개의 *경로 작동*은 모두 3줄 정도로 줄일 수 있습니다. |
|||
|
|||
=== "파이썬 3.7 이상" |
|||
|
|||
```Python hl_lines="30-32" |
|||
{!> ../../../docs_src/security/tutorial002.py!} |
|||
``` |
|||
|
|||
=== "파이썬 3.10 이상" |
|||
|
|||
```Python hl_lines="28-30" |
|||
{!> ../../../docs_src/security/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
## 요약 |
|||
|
|||
이제 *경로 작동 함수*에서 현재 사용자를 직접 가져올 수 있습니다. |
|||
|
|||
우리는 이미 이들 사이에 있습니다. |
|||
|
|||
사용자/클라이언트가 실제로 `username`과 `password`를 보내려면 *경로 작동*을 추가하기만 하면 됩니다. |
|||
|
|||
다음 장을 확인해 봅시다. |
@ -0,0 +1,109 @@ |
|||
# Declare um exemplo dos dados da requisição |
|||
|
|||
Você pode declarar exemplos dos dados que a sua aplicação pode receber. |
|||
|
|||
Aqui estão várias formas de se fazer isso. |
|||
|
|||
## `schema_extra` do Pydantic |
|||
|
|||
Você pode declarar um `example` para um modelo Pydantic usando `Config` e `schema_extra`, conforme descrito em <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Documentação do Pydantic: Schema customization</a>: |
|||
|
|||
```Python hl_lines="15-23" |
|||
{!../../../docs_src/schema_extra_example/tutorial001.py!} |
|||
``` |
|||
|
|||
Essas informações extras serão adicionadas como se encontram no **JSON Schema** de resposta desse modelo e serão usadas na documentação da API. |
|||
|
|||
!!! tip "Dica" |
|||
Você pode usar a mesma técnica para estender o JSON Schema e adicionar suas próprias informações extras de forma personalizada. |
|||
|
|||
Por exemplo, você pode usar isso para adicionar metadados para uma interface de usuário de front-end, etc. |
|||
|
|||
## `Field` de argumentos adicionais |
|||
|
|||
Ao usar `Field ()` com modelos Pydantic, você também pode declarar informações extras para o **JSON Schema** passando quaisquer outros argumentos arbitrários para a função. |
|||
|
|||
Você pode usar isso para adicionar um `example` para cada campo: |
|||
|
|||
```Python hl_lines="4 10-13" |
|||
{!../../../docs_src/schema_extra_example/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! warning "Atenção" |
|||
Lembre-se de que esses argumentos extras passados não adicionarão nenhuma validação, apenas informações extras, para fins de documentação. |
|||
|
|||
## `example` e `examples` no OpenAPI |
|||
|
|||
Ao usar quaisquer dos: |
|||
|
|||
* `Path()` |
|||
* `Query()` |
|||
* `Header()` |
|||
* `Cookie()` |
|||
* `Body()` |
|||
* `Form()` |
|||
* `File()` |
|||
|
|||
você também pode declarar um dado `example` ou um grupo de `examples` com informações adicionais que serão adicionadas ao **OpenAPI**. |
|||
|
|||
### `Body` com `example` |
|||
|
|||
Aqui nós passamos um `example` dos dados esperados por `Body()`: |
|||
|
|||
```Python hl_lines="21-26" |
|||
{!../../../docs_src/schema_extra_example/tutorial003.py!} |
|||
``` |
|||
|
|||
### Exemplo na UI da documentação |
|||
|
|||
Com qualquer um dos métodos acima, os `/docs` vão ficar assim: |
|||
|
|||
<img src="/img/tutorial/body-fields/image01.png"> |
|||
|
|||
### `Body` com vários `examples` |
|||
|
|||
Alternativamente ao único `example`, você pode passar `examples` usando um `dict` com **vários examples**, cada um com informações extras que serão adicionadas no **OpenAPI** também. |
|||
|
|||
As chaves do `dict` identificam cada exemplo, e cada valor é outro `dict`. |
|||
|
|||
Cada `dict` de exemplo específico em `examples` pode conter: |
|||
|
|||
* `summary`: Pequena descrição do exemplo. |
|||
* `description`: Uma descrição longa que pode conter texto em Markdown. |
|||
* `value`: O próprio exemplo mostrado, ex: um `dict`. |
|||
* `externalValue`: alternativa ao `value`, uma URL apontando para o exemplo. Embora isso possa não ser suportado por tantas ferramentas quanto `value`. |
|||
|
|||
```Python hl_lines="22-48" |
|||
{!../../../docs_src/schema_extra_example/tutorial004.py!} |
|||
``` |
|||
|
|||
### Exemplos na UI da documentação |
|||
|
|||
Com `examples` adicionado a `Body()`, os `/docs` vão ficar assim: |
|||
|
|||
<img src="/img/tutorial/body-fields/image02.png"> |
|||
|
|||
## Detalhes técnicos |
|||
|
|||
!!! warning "Atenção" |
|||
Esses são detalhes muito técnicos sobre os padrões **JSON Schema** e **OpenAPI**. |
|||
|
|||
Se as ideias explicadas acima já funcionam para você, isso pode ser o suficiente, e você provavelmente não precisa desses detalhes, fique à vontade para pular. |
|||
|
|||
Quando você adiciona um exemplo dentro de um modelo Pydantic, usando `schema_extra` ou` Field(example="something") `esse exemplo é adicionado ao **JSON Schema** para esse modelo Pydantic. |
|||
|
|||
E esse **JSON Schema** do modelo Pydantic está incluído no **OpenAPI** da sua API e, em seguida, é usado na UI da documentação. |
|||
|
|||
O **JSON Schema** na verdade não tem um campo `example` nos padrões. Versões recentes do JSON Schema definem um campo <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a>, mas o OpenAPI 3.0.3 é baseado numa versão mais antiga do JSON Schema que não tinha `examples`. |
|||
|
|||
Por isso, o OpenAPI 3.0.3 definiu o seu próprio <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-20" class="external-link" target="_blank">`example`</a> para a versão modificada do **JSON Schema** que é usada, para o mesmo próposito (mas é apenas `example` no singular, não `examples`), e é isso que é usado pela UI da documentação da API(usando o Swagger UI). |
|||
|
|||
Portanto, embora `example` não seja parte do JSON Schema, é parte da versão customizada do JSON Schema usada pelo OpenAPI, e é isso que vai ser usado dentro da UI de documentação. |
|||
|
|||
Mas quando você usa `example` ou `examples` com qualquer um dos outros utilitários (`Query()`, `Body()`, etc.) esses exemplos não são adicionados ao JSON Schema que descreve esses dados (nem mesmo para versão própria do OpenAPI do JSON Schema), eles são adicionados diretamente à declaração da *operação de rota* no OpenAPI (fora das partes do OpenAPI que usam o JSON Schema). |
|||
|
|||
Para `Path()`, `Query()`, `Header()`, e `Cookie()`, o `example` e `examples` são adicionados a <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object" class="external-link" target="_blank">definição do OpenAPI, dentro do `Parameter Object` (na especificação)</a>. |
|||
|
|||
E para `Body()`, `File()`, e `Form()`, o `example` e `examples` são de maneira equivalente adicionados para a <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#mediaTypeObject" class="external-link" target="_blank">definição do OpenAPI, dentro do `Request Body Object`, no campo `content`, no `Media Type Object` (na especificação)</a>. |
|||
|
|||
Por outro lado, há uma versão mais recente do OpenAPI: **3.1.0**, lançada recentemente. Baseado no JSON Schema mais recente e a maioria das modificações da versão customizada do OpenAPI do JSON Schema são removidas, em troca dos recursos das versões recentes do JSON Schema, portanto, todas essas pequenas diferenças são reduzidas. No entanto, a UI do Swagger atualmente não oferece suporte a OpenAPI 3.1.0, então, por enquanto, é melhor continuar usando as opções acima. |
@ -0,0 +1,153 @@ |
|||
# Body - Обновления |
|||
|
|||
## Полное обновление с помощью `PUT` |
|||
|
|||
Для полного обновления элемента можно воспользоваться операцией <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a>. |
|||
|
|||
Вы можете использовать функцию `jsonable_encoder` для преобразования входных данных в JSON, так как нередки случаи, когда работать можно только с простыми типами данных (например, для хранения в NoSQL-базе данных). |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="28-33" |
|||
{!> ../../../docs_src/body_updates/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="30-35" |
|||
{!> ../../../docs_src/body_updates/tutorial001_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="30-35" |
|||
{!> ../../../docs_src/body_updates/tutorial001.py!} |
|||
``` |
|||
|
|||
`PUT` используется для получения данных, которые должны полностью заменить существующие данные. |
|||
|
|||
### Предупреждение о замене |
|||
|
|||
Это означает, что если вы хотите обновить элемент `bar`, используя `PUT` с телом, содержащим: |
|||
|
|||
```Python |
|||
{ |
|||
"name": "Barz", |
|||
"price": 3, |
|||
"description": None, |
|||
} |
|||
``` |
|||
|
|||
поскольку оно не включает уже сохраненный атрибут `"tax": 20.2`, входная модель примет значение по умолчанию `"tax": 10.5`. |
|||
|
|||
И данные будут сохранены с этим "новым" `tax`, равным `10,5`. |
|||
|
|||
## Частичное обновление с помощью `PATCH` |
|||
|
|||
Также можно использовать <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> операцию для *частичного* обновления данных. |
|||
|
|||
Это означает, что можно передавать только те данные, которые необходимо обновить, оставляя остальные нетронутыми. |
|||
|
|||
!!! note "Технические детали" |
|||
`PATCH` менее распространен и известен, чем `PUT`. |
|||
|
|||
А многие команды используют только `PUT`, даже для частичного обновления. |
|||
|
|||
Вы можете **свободно** использовать их как угодно, **FastAPI** не накладывает никаких ограничений. |
|||
|
|||
Но в данном руководстве более или менее понятно, как они должны использоваться. |
|||
|
|||
### Использование параметра `exclude_unset` в Pydantic |
|||
|
|||
Если необходимо выполнить частичное обновление, то очень полезно использовать параметр `exclude_unset` в методе `.dict()` модели Pydantic. |
|||
|
|||
Например, `item.dict(exclude_unset=True)`. |
|||
|
|||
В результате будет сгенерирован словарь, содержащий только те данные, которые были заданы при создании модели `item`, без учета значений по умолчанию. Затем вы можете использовать это для создания словаря только с теми данными, которые были установлены (отправлены в запросе), опуская значения по умолчанию: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="32" |
|||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="34" |
|||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="34" |
|||
{!> ../../../docs_src/body_updates/tutorial002.py!} |
|||
``` |
|||
|
|||
### Использование параметра `update` в Pydantic |
|||
|
|||
Теперь можно создать копию существующей модели, используя `.copy()`, и передать параметр `update` с `dict`, содержащим данные для обновления. |
|||
|
|||
Например, `stored_item_model.copy(update=update_data)`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="33" |
|||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="35" |
|||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="35" |
|||
{!> ../../../docs_src/body_updates/tutorial002.py!} |
|||
``` |
|||
|
|||
### Кратко о частичном обновлении |
|||
|
|||
В целом, для применения частичных обновлений необходимо: |
|||
|
|||
* (Опционально) использовать `PATCH` вместо `PUT`. |
|||
* Извлечь сохранённые данные. |
|||
* Поместить эти данные в Pydantic модель. |
|||
* Сгенерировать `dict` без значений по умолчанию из входной модели (с использованием `exclude_unset`). |
|||
* Таким образом, можно обновлять только те значения, которые действительно установлены пользователем, вместо того чтобы переопределять значения, уже сохраненные в модели по умолчанию. |
|||
* Создать копию хранимой модели, обновив ее атрибуты полученными частичными обновлениями (с помощью параметра `update`). |
|||
* Преобразовать скопированную модель в то, что может быть сохранено в вашей БД (например, с помощью `jsonable_encoder`). |
|||
* Это сравнимо с повторным использованием метода модели `.dict()`, но при этом происходит проверка (и преобразование) значений в типы данных, которые могут быть преобразованы в JSON, например, `datetime` в `str`. |
|||
* Сохранить данные в своей БД. |
|||
* Вернуть обновленную модель. |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="28-35" |
|||
{!> ../../../docs_src/body_updates/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="30-37" |
|||
{!> ../../../docs_src/body_updates/tutorial002_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="30-37" |
|||
{!> ../../../docs_src/body_updates/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! tip "Подсказка" |
|||
Эту же технику можно использовать и для операции HTTP `PUT`. |
|||
|
|||
Но в приведенном примере используется `PATCH`, поскольку он был создан именно для таких случаев использования. |
|||
|
|||
!!! note "Технические детали" |
|||
Обратите внимание, что входная модель по-прежнему валидируется. |
|||
|
|||
Таким образом, если вы хотите получать частичные обновления, в которых могут быть опущены все атрибуты, вам необходимо иметь модель, в которой все атрибуты помечены как необязательные (со значениями по умолчанию или `None`). |
|||
|
|||
Чтобы отличить модели со всеми необязательными значениями для **обновления** от моделей с обязательными значениями для **создания**, можно воспользоваться идеями, описанными в [Дополнительные модели](extra-models.md){.internal-link target=_blank}. |
@ -0,0 +1,42 @@ |
|||
# JSON кодировщик |
|||
|
|||
В некоторых случаях может потребоваться преобразование типа данных (например, Pydantic-модели) в тип, совместимый с JSON (например, `dict`, `list` и т.д.). |
|||
|
|||
Например, если необходимо хранить его в базе данных. |
|||
|
|||
Для этого **FastAPI** предоставляет функцию `jsonable_encoder()`. |
|||
|
|||
## Использование `jsonable_encoder` |
|||
|
|||
Представим, что у вас есть база данных `fake_db`, которая принимает только JSON-совместимые данные. |
|||
|
|||
Например, он не принимает объекты `datetime`, так как они не совместимы с JSON. |
|||
|
|||
В таком случае объект `datetime` следует преобразовать в строку соответствующую <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">формату ISO</a>. |
|||
|
|||
Точно так же эта база данных не может принять Pydantic модель (объект с атрибутами), а только `dict`. |
|||
|
|||
Для этого можно использовать функцию `jsonable_encoder`. |
|||
|
|||
Она принимает объект, например, модель Pydantic, и возвращает его версию, совместимую с JSON: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="4 21" |
|||
{!> ../../../docs_src/encoder/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```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>. |
|||
|
|||
Функция не возвращает большой `str`, содержащий данные в формате JSON (в виде строки). Она возвращает стандартную структуру данных Python (например, `dict`) со значениями и подзначениями, которые совместимы с JSON. |
|||
|
|||
!!! note "Технические детали" |
|||
`jsonable_encoder` фактически используется **FastAPI** внутри системы для преобразования данных. Однако он полезен и во многих других сценариях. |
@ -0,0 +1,261 @@ |
|||
# Обработка ошибок |
|||
|
|||
Существует множество ситуаций, когда необходимо сообщить об ошибке клиенту, использующему ваш API. |
|||
|
|||
Таким клиентом может быть браузер с фронтендом, чужой код, IoT-устройство и т.д. |
|||
|
|||
Возможно, вам придется сообщить клиенту о следующем: |
|||
|
|||
* Клиент не имеет достаточных привилегий для выполнения данной операции. |
|||
* Клиент не имеет доступа к данному ресурсу. |
|||
* Элемент, к которому клиент пытался получить доступ, не существует. |
|||
* и т.д. |
|||
|
|||
В таких случаях обычно возвращается **HTTP-код статуса ответа** в диапазоне **400** (от 400 до 499). |
|||
|
|||
Они похожи на двухсотые HTTP статус-коды (от 200 до 299), которые означают, что запрос обработан успешно. |
|||
|
|||
Четырёхсотые статус-коды означают, что ошибка произошла по вине клиента. |
|||
|
|||
Помните ли ошибки **"404 Not Found "** (и шутки) ? |
|||
|
|||
## Использование `HTTPException` |
|||
|
|||
Для возврата клиенту HTTP-ответов с ошибками используется `HTTPException`. |
|||
|
|||
### Импортируйте `HTTPException` |
|||
|
|||
```Python hl_lines="1" |
|||
{!../../../docs_src/handling_errors/tutorial001.py!} |
|||
``` |
|||
|
|||
### Вызовите `HTTPException` в своем коде |
|||
|
|||
`HTTPException` - это обычное исключение Python с дополнительными данными, актуальными для API. |
|||
|
|||
Поскольку это исключение Python, то его не `возвращают`, а `вызывают`. |
|||
|
|||
Это также означает, что если вы находитесь внутри функции, которая вызывается внутри вашей *функции операции пути*, и вы поднимаете `HTTPException` внутри этой функции, то она не будет выполнять остальной код в *функции операции пути*, а сразу завершит запрос и отправит HTTP-ошибку из `HTTPException` клиенту. |
|||
|
|||
О том, насколько выгоднее `вызывать` исключение, чем `возвращать` значение, будет рассказано в разделе, посвященном зависимостям и безопасности. |
|||
|
|||
В данном примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`: |
|||
|
|||
```Python hl_lines="11" |
|||
{!../../../docs_src/handling_errors/tutorial001.py!} |
|||
``` |
|||
|
|||
### Возвращаемый ответ |
|||
|
|||
Если клиент запросит `http://example.com/items/foo` (`item_id` `"foo"`), то он получит статус-код 200 и ответ в формате JSON: |
|||
|
|||
```JSON |
|||
{ |
|||
"item": "The Foo Wrestlers" |
|||
} |
|||
``` |
|||
|
|||
Но если клиент запросит `http://example.com/items/bar` (несуществующий `item_id` `"bar"`), то он получит статус-код 404 (ошибка "не найдено") и JSON-ответ в виде: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": "Item not found" |
|||
} |
|||
``` |
|||
|
|||
!!! tip "Подсказка" |
|||
При вызове `HTTPException` в качестве параметра `detail` можно передавать любое значение, которое может быть преобразовано в JSON, а не только `str`. |
|||
|
|||
Вы можете передать `dict`, `list` и т.д. |
|||
|
|||
Они автоматически обрабатываются **FastAPI** и преобразуются в JSON. |
|||
|
|||
## Добавление пользовательских заголовков |
|||
|
|||
В некоторых ситуациях полезно иметь возможность добавлять пользовательские заголовки к ошибке HTTP. Например, для некоторых типов безопасности. |
|||
|
|||
Скорее всего, вам не потребуется использовать его непосредственно в коде. |
|||
|
|||
Но в случае, если это необходимо для продвинутого сценария, можно добавить пользовательские заголовки: |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/handling_errors/tutorial002.py!} |
|||
``` |
|||
|
|||
## Установка пользовательских обработчиков исключений |
|||
|
|||
Вы можете добавить пользовательские обработчики исключений с помощью <a href="https://www.starlette.io/exceptions/" class="external-link" target="_blank">то же самое исключение - утилиты от Starlette</a>. |
|||
|
|||
Допустим, у вас есть пользовательское исключение `UnicornException`, которое вы (или используемая вами библиотека) можете `вызвать`. |
|||
|
|||
И вы хотите обрабатывать это исключение глобально с помощью FastAPI. |
|||
|
|||
Можно добавить собственный обработчик исключений с помощью `@app.exception_handler()`: |
|||
|
|||
```Python hl_lines="5-7 13-18 24" |
|||
{!../../../docs_src/handling_errors/tutorial003.py!} |
|||
``` |
|||
|
|||
Здесь, если запросить `/unicorns/yolo`, то *операция пути* вызовет `UnicornException`. |
|||
|
|||
Но оно будет обработано `unicorn_exception_handler`. |
|||
|
|||
Таким образом, вы получите чистую ошибку с кодом состояния HTTP `418` и содержимым JSON: |
|||
|
|||
```JSON |
|||
{"message": "Oops! yolo did something. There goes a rainbow..."} |
|||
``` |
|||
|
|||
!!! note "Технические детали" |
|||
Также можно использовать `from starlette.requests import Request` и `from starlette.responses import JSONResponse`. |
|||
|
|||
**FastAPI** предоставляет тот же `starlette.responses`, что и `fastapi.responses`, просто для удобства разработчика. Однако большинство доступных ответов поступает непосредственно из Starlette. То же самое касается и `Request`. |
|||
|
|||
## Переопределение стандартных обработчиков исключений |
|||
|
|||
**FastAPI** имеет некоторые обработчики исключений по умолчанию. |
|||
|
|||
Эти обработчики отвечают за возврат стандартных JSON-ответов при `вызове` `HTTPException` и при наличии в запросе недопустимых данных. |
|||
|
|||
Вы можете переопределить эти обработчики исключений на свои собственные. |
|||
|
|||
### Переопределение исключений проверки запроса |
|||
|
|||
Когда запрос содержит недопустимые данные, **FastAPI** внутренне вызывает ошибку `RequestValidationError`. |
|||
|
|||
А также включает в себя обработчик исключений по умолчанию. |
|||
|
|||
Чтобы переопределить его, импортируйте `RequestValidationError` и используйте его с `@app.exception_handler(RequestValidationError)` для создания обработчика исключений. |
|||
|
|||
Обработчик исключения получит объект `Request` и исключение. |
|||
|
|||
```Python hl_lines="2 14-16" |
|||
{!../../../docs_src/handling_errors/tutorial004.py!} |
|||
``` |
|||
|
|||
Теперь, если перейти к `/items/foo`, то вместо стандартной JSON-ошибки с: |
|||
|
|||
```JSON |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"loc": [ |
|||
"path", |
|||
"item_id" |
|||
], |
|||
"msg": "value is not a valid integer", |
|||
"type": "type_error.integer" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
вы получите текстовую версию: |
|||
|
|||
``` |
|||
1 validation error |
|||
path -> item_id |
|||
value is not a valid integer (type=type_error.integer) |
|||
``` |
|||
|
|||
#### `RequestValidationError` или `ValidationError` |
|||
|
|||
!!! warning "Внимание" |
|||
Это технические детали, которые можно пропустить, если они не важны для вас сейчас. |
|||
|
|||
`RequestValidationError` является подклассом Pydantic <a href="https://pydantic-docs.helpmanual.io/usage/models/#error-handling" class="external-link" target="_blank">`ValidationError`</a>. |
|||
|
|||
**FastAPI** использует его для того, чтобы, если вы используете Pydantic-модель в `response_model`, и ваши данные содержат ошибку, вы увидели ошибку в журнале. |
|||
|
|||
Но клиент/пользователь этого не увидит. Вместо этого клиент получит сообщение "Internal Server Error" с кодом состояния HTTP `500`. |
|||
|
|||
Так и должно быть, потому что если в вашем *ответе* или где-либо в вашем коде (не в *запросе* клиента) возникает Pydantic `ValidationError`, то это действительно ошибка в вашем коде. |
|||
|
|||
И пока вы не устраните ошибку, ваши клиенты/пользователи не должны иметь доступа к внутренней информации о ней, так как это может привести к уязвимости в системе безопасности. |
|||
|
|||
### Переопределите обработчик ошибок `HTTPException` |
|||
|
|||
Аналогичным образом можно переопределить обработчик `HTTPException`. |
|||
|
|||
Например, для этих ошибок можно вернуть обычный текстовый ответ вместо JSON: |
|||
|
|||
```Python hl_lines="3-4 9-11 22" |
|||
{!../../../docs_src/handling_errors/tutorial004.py!} |
|||
``` |
|||
|
|||
!!! note "Технические детали" |
|||
Можно также использовать `from starlette.responses import PlainTextResponse`. |
|||
|
|||
**FastAPI** предоставляет тот же `starlette.responses`, что и `fastapi.responses`, просто для удобства разработчика. Однако большинство доступных ответов поступает непосредственно из Starlette. |
|||
|
|||
### Используйте тело `RequestValidationError` |
|||
|
|||
Ошибка `RequestValidationError` содержит полученное `тело` с недопустимыми данными. |
|||
|
|||
Вы можете использовать его при разработке приложения для регистрации тела и его отладки, возврата пользователю и т.д. |
|||
|
|||
```Python hl_lines="14" |
|||
{!../../../docs_src/handling_errors/tutorial005.py!} |
|||
``` |
|||
|
|||
Теперь попробуйте отправить недействительный элемент, например: |
|||
|
|||
```JSON |
|||
{ |
|||
"title": "towel", |
|||
"size": "XL" |
|||
} |
|||
``` |
|||
|
|||
Вы получите ответ о том, что данные недействительны, содержащий следующее тело: |
|||
|
|||
```JSON hl_lines="12-15" |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"loc": [ |
|||
"body", |
|||
"size" |
|||
], |
|||
"msg": "value is not a valid integer", |
|||
"type": "type_error.integer" |
|||
} |
|||
], |
|||
"body": { |
|||
"title": "towel", |
|||
"size": "XL" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### `HTTPException` в FastAPI или в Starlette |
|||
|
|||
**FastAPI** имеет собственный `HTTPException`. |
|||
|
|||
Класс ошибок **FastAPI** `HTTPException` наследует от класса ошибок Starlette `HTTPException`. |
|||
|
|||
Единственное отличие заключается в том, что `HTTPException` от **FastAPI** позволяет добавлять заголовки, которые будут включены в ответ. |
|||
|
|||
Он необходим/используется внутри системы для OAuth 2.0 и некоторых утилит безопасности. |
|||
|
|||
Таким образом, вы можете продолжать вызывать `HTTPException` от **FastAPI** как обычно в своем коде. |
|||
|
|||
Но когда вы регистрируете обработчик исключений, вы должны зарегистрировать его для `HTTPException` от Starlette. |
|||
|
|||
Таким образом, если какая-либо часть внутреннего кода Starlette, расширение или плагин Starlette вызовет исключение Starlette `HTTPException`, ваш обработчик сможет перехватить и обработать его. |
|||
|
|||
В данном примере, чтобы иметь возможность использовать оба `HTTPException` в одном коде, исключения Starlette переименованы в `StarletteHTTPException`: |
|||
|
|||
```Python |
|||
from starlette.exceptions import HTTPException as StarletteHTTPException |
|||
``` |
|||
|
|||
### Переиспользование обработчиков исключений **FastAPI** |
|||
|
|||
Если вы хотите использовать исключение вместе с теми же обработчиками исключений по умолчанию из **FastAPI**, вы можете импортировать и повторно использовать обработчики исключений по умолчанию из `fastapi.exception_handlers`: |
|||
|
|||
```Python hl_lines="2-5 15 21" |
|||
{!../../../docs_src/handling_errors/tutorial006.py!} |
|||
``` |
|||
|
|||
В этом примере вы просто `выводите в терминал` ошибку с очень выразительным сообщением, но идея вам понятна. Вы можете использовать исключение, а затем просто повторно использовать стандартные обработчики исключений. |
@ -0,0 +1,232 @@ |
|||
# Безопасность - первые шаги |
|||
|
|||
Представим, что у вас есть свой **бэкенд** API на некотором домене. |
|||
|
|||
И у вас есть **фронтенд** на другом домене или на другом пути того же домена (или в мобильном приложении). |
|||
|
|||
И вы хотите иметь возможность аутентификации фронтенда с бэкендом, используя **имя пользователя** и **пароль**. |
|||
|
|||
Мы можем использовать **OAuth2** для создания такой системы с помощью **FastAPI**. |
|||
|
|||
Но давайте избавим вас от необходимости читать всю длинную спецификацию, чтобы найти те небольшие кусочки информации, которые вам нужны. |
|||
|
|||
Для работы с безопасностью воспользуемся средствами, предоставленными **FastAPI**. |
|||
|
|||
## Как это выглядит |
|||
|
|||
Давайте сначала просто воспользуемся кодом и посмотрим, как он работает, а затем детально разберём, что происходит. |
|||
|
|||
## Создание `main.py` |
|||
|
|||
Скопируйте пример в файл `main.py`: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python |
|||
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python |
|||
{!> ../../../docs_src/security/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Предпочтительнее использовать версию с аннотацией, если это возможно. |
|||
|
|||
```Python |
|||
{!> ../../../docs_src/security/tutorial001.py!} |
|||
``` |
|||
|
|||
|
|||
## Запуск |
|||
|
|||
!!! info "Дополнительная информация" |
|||
Вначале, установите библиотеку <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>. |
|||
|
|||
А именно: `pip install python-multipart`. |
|||
|
|||
Это связано с тем, что **OAuth2** использует "данные формы" для передачи `имени пользователя` и `пароля`. |
|||
|
|||
Запустите ваш сервер: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uvicorn main:app --reload |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## Проверка |
|||
|
|||
Перейдите к интерактивной документации по адресу: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. |
|||
|
|||
Вы увидите примерно следующее: |
|||
|
|||
<img src="/img/tutorial/security/image01.png"> |
|||
|
|||
!!! check "Кнопка авторизации!" |
|||
У вас уже появилась новая кнопка "Authorize". |
|||
|
|||
А у *операции пути* теперь появился маленький замочек в правом верхнем углу, на который можно нажать. |
|||
|
|||
При нажатии на нее появляется небольшая форма авторизации, в которую нужно ввести `имя пользователя` и `пароль` (и другие необязательные поля): |
|||
|
|||
<img src="/img/tutorial/security/image02.png"> |
|||
|
|||
!!! note "Технические детали" |
|||
Неважно, что вы введете в форму, она пока не будет работать. Но мы к этому еще придем. |
|||
|
|||
Конечно, это не фронтенд для конечных пользователей, но это отличный автоматический инструмент для интерактивного документирования всех ваших API. |
|||
|
|||
Он может использоваться командой фронтенда (которой можете быть и вы сами). |
|||
|
|||
Он может быть использован сторонними приложениями и системами. |
|||
|
|||
Кроме того, его можно использовать самостоятельно для отладки, проверки и тестирования одного и того же приложения. |
|||
|
|||
## Аутентификация по паролю |
|||
|
|||
Теперь давайте вернемся немного назад и разберемся, что же это такое. |
|||
|
|||
Аутентификация по паролю является одним из способов, определенных в OAuth2, для обеспечения безопасности и аутентификации. |
|||
|
|||
OAuth2 был разработан для того, чтобы бэкэнд или API были независимы от сервера, который аутентифицирует пользователя. |
|||
|
|||
Но в нашем случае одно и то же приложение **FastAPI** будет работать с API и аутентификацией. |
|||
|
|||
Итак, рассмотрим его с этой упрощенной точки зрения: |
|||
|
|||
* Пользователь вводит на фронтенде `имя пользователя` и `пароль` и нажимает `Enter`. |
|||
* Фронтенд (работающий в браузере пользователя) отправляет эти `имя пользователя` и `пароль` на определенный URL в нашем API (объявленный с помощью параметра `tokenUrl="token"`). |
|||
* API проверяет эти `имя пользователя` и `пароль` и выдает в ответ "токен" (мы еще не реализовали ничего из этого). |
|||
* "Токен" - это просто строка с некоторым содержимым, которое мы можем использовать позже для верификации пользователя. |
|||
* Обычно срок действия токена истекает через некоторое время. |
|||
* Таким образом, пользователю придется снова войти в систему в какой-то момент времени. |
|||
* И если токен будет украден, то риск будет меньше, так как он не похож на постоянный ключ, который будет работать вечно (в большинстве случаев). |
|||
* Фронтенд временно хранит этот токен в каком-то месте. |
|||
* Пользователь щелкает мышью на фронтенде, чтобы перейти в другой раздел на фронтенде. |
|||
* Фронтенду необходимо получить дополнительные данные из API. |
|||
* Но для этого необходима аутентификация для конкретной конечной точки. |
|||
* Поэтому для аутентификации в нашем API он посылает заголовок `Authorization` со значением `Bearer` плюс сам токен. |
|||
* Если токен содержит `foobar`, то содержание заголовка `Authorization` будет таким: `Bearer foobar`. |
|||
|
|||
## Класс `OAuth2PasswordBearer` в **FastAPI** |
|||
|
|||
**FastAPI** предоставляет несколько средств на разных уровнях абстракции для реализации этих функций безопасности. |
|||
|
|||
В данном примере мы будем использовать **OAuth2**, с аутентификацией по паролю, используя токен **Bearer**. Для этого мы используем класс `OAuth2PasswordBearer`. |
|||
|
|||
!!! info "Дополнительная информация" |
|||
Токен "bearer" - не единственный вариант, но для нашего случая он является наилучшим. |
|||
|
|||
И это может быть лучшим вариантом для большинства случаев использования, если только вы не являетесь экспертом в области OAuth2 и точно знаете, почему вам лучше подходит какой-то другой вариант. |
|||
|
|||
В этом случае **FastAPI** также предоставляет инструменты для его реализации. |
|||
|
|||
При создании экземпляра класса `OAuth2PasswordBearer` мы передаем в него параметр `tokenUrl`. Этот параметр содержит URL, который клиент (фронтенд, работающий в браузере пользователя) будет использовать для отправки `имени пользователя` и `пароля` с целью получения токена. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="8" |
|||
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../../docs_src/security/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Предпочтительнее использовать версию с аннотацией, если это возможно. |
|||
|
|||
```Python hl_lines="6" |
|||
{!> ../../../docs_src/security/tutorial001.py!} |
|||
``` |
|||
|
|||
!!! tip "Подсказка" |
|||
Здесь `tokenUrl="token"` ссылается на относительный URL `token`, который мы еще не создали. Поскольку это относительный URL, он эквивалентен `./token`. |
|||
|
|||
Поскольку мы используем относительный URL, если ваш API расположен по адресу `https://example.com/`, то он будет ссылаться на `https://example.com/token`. Если же ваш API расположен по адресу `https://example.com/api/v1/`, то он будет ссылаться на `https://example.com/api/v1/token`. |
|||
|
|||
Использование относительного URL важно для того, чтобы ваше приложение продолжало работать даже в таких сложных случаях, как оно находится [за прокси-сервером](../../advanced/behind-a-proxy.md){.internal-link target=_blank}. |
|||
|
|||
Этот параметр не создает конечную точку / *операцию пути*, а объявляет, что URL `/token` будет таким, который клиент должен использовать для получения токена. Эта информация используется в OpenAPI, а затем в интерактивных системах документации API. |
|||
|
|||
Вскоре мы создадим и саму операцию пути. |
|||
|
|||
!!! info "Дополнительная информация" |
|||
Если вы очень строгий "питонист", то вам может не понравиться стиль названия параметра `tokenUrl` вместо `token_url`. |
|||
|
|||
Это связано с тем, что тут используется то же имя, что и в спецификации OpenAPI. Таким образом, если вам необходимо более подробно изучить какую-либо из этих схем безопасности, вы можете просто использовать копирование/вставку, чтобы найти дополнительную информацию о ней. |
|||
|
|||
Переменная `oauth2_scheme` является экземпляром `OAuth2PasswordBearer`, но она также является "вызываемой". |
|||
|
|||
Ее можно вызвать следующим образом: |
|||
|
|||
```Python |
|||
oauth2_scheme(some, parameters) |
|||
``` |
|||
|
|||
Поэтому ее можно использовать вместе с `Depends`. |
|||
|
|||
### Использование |
|||
|
|||
Теперь вы можете передать ваш `oauth2_scheme` в зависимость с помощью `Depends`. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="11" |
|||
{!> ../../../docs_src/security/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ без Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Предпочтительнее использовать версию с аннотацией, если это возможно. |
|||
|
|||
```Python hl_lines="10" |
|||
{!> ../../../docs_src/security/tutorial001.py!} |
|||
``` |
|||
|
|||
Эта зависимость будет предоставлять `строку`, которая присваивается параметру `token` в *функции операции пути*. |
|||
|
|||
**FastAPI** будет знать, что он может использовать эту зависимость для определения "схемы безопасности" в схеме OpenAPI (и автоматической документации по API). |
|||
|
|||
!!! info "Технические детали" |
|||
**FastAPI** будет знать, что он может использовать класс `OAuth2PasswordBearer` (объявленный в зависимости) для определения схемы безопасности в OpenAPI, поскольку он наследуется от `fastapi.security.oauth2.OAuth2`, который, в свою очередь, наследуется от `fastapi.security.base.SecurityBase`. |
|||
|
|||
Все утилиты безопасности, интегрируемые в OpenAPI (и автоматическая документация по API), наследуются от `SecurityBase`, поэтому **FastAPI** может знать, как интегрировать их в OpenAPI. |
|||
|
|||
## Что он делает |
|||
|
|||
Он будет искать в запросе заголовок `Authorization` и проверять, содержит ли он значение `Bearer` с некоторым токеном, и возвращать токен в виде `строки`. |
|||
|
|||
Если он не видит заголовка `Authorization` или значение не имеет токена `Bearer`, то в ответ будет выдана ошибка с кодом состояния 401 (`UNAUTHORIZED`). |
|||
|
|||
Для возврата ошибки даже не нужно проверять, существует ли токен. Вы можете быть уверены, что если ваша функция будет выполнилась, то в этом токене есть `строка`. |
|||
|
|||
Проверить это можно уже сейчас в интерактивной документации: |
|||
|
|||
<img src="/img/tutorial/security/image03.png"> |
|||
|
|||
Мы пока не проверяем валидность токена, но для начала неплохо. |
|||
|
|||
## Резюме |
|||
|
|||
Таким образом, всего за 3-4 дополнительные строки вы получаете некую примитивную форму защиты. |
@ -0,0 +1,3 @@ |
|||
# Hakkında |
|||
|
|||
FastAPI, tasarımı, ilham kaynağı ve daha fazlası hakkında. 🤓 |
@ -0,0 +1,409 @@ |
|||
# Alternatifler, İlham Kaynakları ve Karşılaştırmalar |
|||
|
|||
**FastAPI**'ya neler ilham verdi? Diğer alternatiflerle karşılaştırıldığında farkları neler? **FastAPI** diğer alternatiflerinden neler öğrendi? |
|||
|
|||
## Giriş |
|||
|
|||
Başkalarının daha önceki çalışmaları olmasaydı, **FastAPI** var olmazdı. |
|||
|
|||
Geçmişte oluşturulan pek çok araç **FastAPI**'a ilham kaynağı olmuştur. |
|||
|
|||
Yıllardır yeni bir framework oluşturmaktan kaçınıyordum. Başlangıçta **FastAPI**'ın çözdüğü sorunları çözebilmek için pek çok farklı framework, <abbr title="Eklenti: Plug-In">eklenti</abbr> ve araç kullanmayı denedim. |
|||
|
|||
Ancak bir noktada, geçmişteki diğer araçlardan en iyi fikirleri alarak bütün bu çözümleri kapsayan, ayrıca bütün bunları Python'ın daha önce mevcut olmayan özelliklerini (Python 3.6+ ile gelen <abbr title="Tip belirteçleri: Type Hints">tip belirteçleri</abbr>) kullanarak yapan bir şey üretmekten başka seçenek kalmamıştı. |
|||
|
|||
## Daha Önce Geliştirilen Araçlar |
|||
|
|||
### <a href="https://www.djangoproject.com/" class="external-link" target="_blank">Django</a> |
|||
|
|||
Django geniş çapta güvenilen, Python ekosistemindeki en popüler web framework'üdür. Instagram gibi sistemleri geliştirmede kullanılmıştır. |
|||
|
|||
MySQL ve PostgreSQL gibi ilişkisel veritabanlarıyla nispeten sıkı bir şekilde bağlantılıdır. Bu nedenle Couchbase, MongoDB ve Cassandra gibi NoSQL veritabanlarını ana veritabanı motoru olarak kullanmak pek de kolay değil. |
|||
|
|||
Modern ön uçlarda (React, Vue.js ve Angular gibi) veya diğer sistemler (örneğin <abbr title="Nesnelerin interneti: IoT (Internet of Things)">nesnelerin interneti</abbr> cihazları) tarafından kullanılan API'lar yerine arka uçta HTML üretmek için oluşturuldu. |
|||
|
|||
### <a href="https://www.django-rest-framework.org/" class="external-link" target="_blank">Django REST Framework</a> |
|||
|
|||
Django REST framework'ü, Django'nun API kabiliyetlerini arttırmak için arka planda Django kullanan esnek bir araç grubu olarak oluşturuldu. |
|||
|
|||
Üstelik Mozilla, Red Hat ve Eventbrite gibi pek çok şirket tarafından kullanılıyor. |
|||
|
|||
**Otomatik API dökümantasyonu**nun ilk örneklerinden biri olduğu için, **FastAPI** arayışına ilham veren ilk fikirlerden biri oldu. |
|||
|
|||
!!! note "Not" |
|||
Django REST Framework'ü, aynı zamanda **FastAPI**'ın dayandığı Starlette ve Uvicorn'un da yaratıcısı olan Tom Christie tarafından geliştirildi. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham verdi?" |
|||
Kullanıcılar için otomatik API dökümantasyonu sunan bir web arayüzüne sahip olmalı. |
|||
|
|||
### <a href="https://flask.palletsprojects.com" class="external-link" target="_blank">Flask</a> |
|||
|
|||
Flask bir <abbr title="Mikro Framework: Micro Framework">mikro framework</abbr> olduğundan Django gibi framework'lerin aksine veritabanı entegrasyonu gibi Django ile gelen pek çok özelliği direkt barındırmaz. |
|||
|
|||
Sağladığı basitlik ve esneklik NoSQL veritabanlarını ana veritabanı sistemi olarak kullanmak gibi şeyler yapmaya olanak sağlar. |
|||
|
|||
Yapısı oldukça basit olduğundan öğrenmesi de nispeten basittir, tabii dökümantasyonu bazı noktalarda biraz teknik hale geliyor. |
|||
|
|||
Ayrıca Django ile birlikte gelen veritabanı, kullanıcı yönetimi ve diğer pek çok özelliğe ihtiyaç duymayan uygulamalarda da yaygın olarak kullanılıyor. Ancak bu tür özelliklerin pek çoğu <abbr title="Eklentiler: Plug-Ins">eklentiler</abbr> ile eklenebiliyor. |
|||
|
|||
Uygulama parçalarının böyle ayrılıyor oluşu ve istenilen özelliklerle genişletilebilecek bir <abbr title="Mikro Framework: Micro Framework">mikro framework</abbr> olmak tam da benim istediğim bir özellikti. |
|||
|
|||
Flask'ın basitliği göz önünde bulundurulduğu zaman, API geliştirmek için iyi bir seçim gibi görünüyordu. Sıradaki şey ise Flask için bir "Django REST Framework"! |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham verdi?" |
|||
Gereken araçları ve parçaları birleştirip eşleştirmeyi kolaylaştıracak bir mikro framework olmalı. |
|||
|
|||
Basit ve kullanması kolay bir <abbr title="Yönlendirme: Routing">yönlendirme sistemine</abbr> sahip olmalı. |
|||
|
|||
### <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a> |
|||
|
|||
**FastAPI** aslında **Requests**'in bir alternatifi değil. İkisininde kapsamı oldukça farklı. |
|||
|
|||
Aslında Requests'i bir FastAPI uygulamasının *içinde* kullanmak daha olağan olurdu. |
|||
|
|||
Ama yine de, FastAPI, Requests'ten oldukça ilham aldı. |
|||
|
|||
**Requests**, <abbr title="API (Application Programming Interface): Uygulama Programlama Arayüzü">API'lar</abbr> ile bir istemci olarak *etkileşime geçmeyi* sağlayan bir kütüphaneyken **FastAPI** bir sunucu olarak <abbr title="API (Application Programming Interface): Uygulama Programlama Arayüzü">API'lar</abbr> oluşturmaya yarar. |
|||
|
|||
Öyle ya da böyle zıt uçlarda olmalarına rağmen birbirlerini tamamlıyorlar. |
|||
|
|||
Requests oldukça basit ve sezgisel bir tasarıma sahip, kullanması da mantıklı varsayılan değerlerle oldukça kolay. Ama aynı zamanda çok güçlü ve gayet özelleştirilebilir. |
|||
|
|||
Bu yüzden resmi web sitede de söylendiği gibi: |
|||
|
|||
> Requests, tüm zamanların en çok indirilen Python <abbr title="Paket: Package">paketlerinden</abbr> biridir. |
|||
|
|||
Kullanım şekli oldukça basit. Örneğin bir `GET` isteği yapmak için aşağıdaki yeterli: |
|||
|
|||
```Python |
|||
response = requests.get("http://example.com/some/url") |
|||
``` |
|||
|
|||
Bunun FastAPI'deki API <abbr title="Yol İşlemi: Path Operation">*yol işlemi*</abbr> şöyle görünür: |
|||
|
|||
```Python hl_lines="1" |
|||
@app.get("/some/url") |
|||
def read_url(): |
|||
return {"message": "Hello World!"} |
|||
``` |
|||
|
|||
`requests.get(...)` ile `@app.get(...)` arasındaki benzerliklere bakın. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham verdi?" |
|||
* Basit ve sezgisel bir API'ya sahip olmalı. |
|||
* HTTP metot isimlerini (işlemlerini) anlaşılır olacak bir şekilde, direkt kullanmalı. |
|||
* Mantıklı varsayılan değerlere ve buna rağmen güçlü bir özelleştirme desteğine sahip olmalı. |
|||
|
|||
### <a href="https://swagger.io/" class="external-link" target="_blank">Swagger</a> / <a href="https://github.com/OAI/OpenAPI-Specification/" class="external-link" target="_blank">OpenAPI</a> |
|||
|
|||
Benim Django REST Framework'ünden istediğim ana özellik otomatik API dökümantasyonuydu. |
|||
|
|||
Daha sonra API'ları dökümanlamak için Swagger adında JSON (veya JSON'un bir uzantısı olan YAML'ı) kullanan bir standart olduğunu buldum. |
|||
|
|||
Üstelik Swagger API'ları için zaten halihazırda oluşturulmuş bir web arayüzü vardı. Yani, bir API için Swagger dökümantasyonu oluşturmak bu arayüzü otomatik olarak kullanabilmek demekti. |
|||
|
|||
Swagger bir noktada Linux Foundation'a verildi ve adı OpenAPI olarak değiştirildi. |
|||
|
|||
İşte bu yüzden versiyon 2.0 hakkında konuşurken "Swagger", versiyon 3 ve üzeri için ise "OpenAPI" adını kullanmak daha yaygın. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham verdi?" |
|||
API spesifikasyonları için özel bir şema yerine bir <abbr title="Open Standard: Açık Standart, Açık kaynak olarak yayınlanan standart">açık standart</abbr> benimseyip kullanmalı. |
|||
|
|||
Ayrıca standarda bağlı kullanıcı arayüzü araçlarını entegre etmeli: |
|||
|
|||
* <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> |
|||
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> |
|||
|
|||
Yukarıdaki ikisi oldukça popüler ve istikrarlı olduğu için seçildi, ancak hızlı bir araştırma yaparak **FastAPI** ile kullanabileceğiniz pek çok OpenAPI alternatifi arayüz bulabilirsiniz. |
|||
|
|||
### Flask REST framework'leri |
|||
|
|||
Pek çok Flask REST framework'ü var, fakat bunları biraz araştırdıktan sonra pek çoğunun artık geliştirilmediğini ve göze batan bazı sorunlarının olduğunu gördüm. |
|||
|
|||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> |
|||
|
|||
API sistemlerine gereken ana özelliklerden biri de koddan veriyi alıp ağ üzerinde gönderilebilecek bir şeye çevirmek, yani veri <abbr title="Dönüşüm: serialization, marshalling olarak da biliniyor">dönüşümü</abbr>. Bu işleme veritabanındaki veriyi içeren bir objeyi JSON objesine çevirmek, `datetime` objelerini metinlere çevirmek gibi örnekler verilebilir. |
|||
|
|||
API'lara gereken bir diğer büyük özellik ise veri doğrulamadır, yani verinin çeşitli parametrelere bağlı olarak doğru ve tutarlı olduğundan emin olmaktır. Örneğin bir alanın `int` olmasına karar verdiniz, daha sonra rastgele bir metin değeri almasını istemezsiniz. Bu özellikle sisteme dışarıdan gelen veri için kullanışlı bir özellik oluyor. |
|||
|
|||
Bir veri doğrulama sistemi olmazsa bütün bu kontrolleri koda dökerek kendiniz yapmak zorunda kalırdınız. |
|||
|
|||
Marshmallow bu özellikleri sağlamak için geliştirilmişti. Benim de geçmişte oldukça sık kullandığım harika bir kütüphanedir. |
|||
|
|||
Ama... Python'un tip belirteçleri gelmeden önce oluşturulmuştu. Yani her <abbr title="Verilerin nasıl oluşturulması gerektiğinin tanımı">şemayı</abbr> tanımlamak için Marshmallow'un sunduğu spesifik araçları ve sınıfları kullanmanız gerekiyordu. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham verdi?" |
|||
Kod kullanarak otomatik olarak veri tipini ve veri doğrulamayı belirten "şemalar" tanımlamalı. |
|||
|
|||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> |
|||
|
|||
API'ların ihtiyacı olan bir diğer önemli özellik ise gelen isteklerdeki verileri Python objelerine <abbr title="Parsing: dönüştürmek, ayrıştırmak, çözümlemek">ayrıştırabilmektir</abbr>. |
|||
|
|||
Webargs, Flask gibi bir kaç framework'ün üzerinde bunu sağlamak için geliştirilen bir araçtır. |
|||
|
|||
Veri doğrulama için arka planda Marshmallow kullanıyor, hatta aynı geliştiriciler tarafından oluşturuldu. |
|||
|
|||
Webargs da harika bir araç ve onu da geçmişte henüz **FastAPI** yokken çok kullandım. |
|||
|
|||
!!! info "Bilgi" |
|||
Webargs aynı Marshmallow geliştirileri tarafından oluşturuldu. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham verdi?" |
|||
Gelen istek verisi için otomatik veri doğrulamaya sahip olmalı. |
|||
|
|||
### <a href="https://apispec.readthedocs.io/en/stable/" class="external-link" target="_blank">APISpec</a> |
|||
|
|||
Marshmallow ve Webargs <abbr title="Eklenti: Plug-In">eklentiler</abbr> olarak; veri doğrulama, ayrıştırma ve dönüştürmeyi sağlıyor. |
|||
|
|||
Ancak dökümantasyondan hala ses seda yok. Daha sonrasında ise APISpec geldi. |
|||
|
|||
APISpec pek çok framework için bir <abbr title="Eklenti: Plug-In">eklenti</abbr> olarak kullanılıyor (Starlette için de bir <abbr title="Eklenti: Plug-In">eklentisi</abbr> var). |
|||
|
|||
Şemanın tanımını <abbr title="Route: HTTP isteğinin gittiği yol">yol</abbr>'a istek geldiğinde çalışan her bir fonksiyonun <abbr title="Döküman dizesi: docstring">döküman dizesinin</abbr> içine YAML formatında olacak şekilde yazıyorsunuz o da OpenAPI şemaları üretiyor. |
|||
|
|||
Flask, Starlette, Responder ve benzerlerinde bu şekilde çalışıyor. |
|||
|
|||
Fakat sonrasında yine mikro sözdizimi problemiyle karşılaşıyoruz. Python metinlerinin içinde koskoca bir YAML oluyor. |
|||
|
|||
Editör bu konuda pek yardımcı olamaz. Üstelik eğer parametreleri ya da Marshmallow şemalarını değiştirip YAML kodunu güncellemeyi unutursak artık döküman geçerliliğini yitiriyor. |
|||
|
|||
!!! info "Bilgi" |
|||
APISpec de aynı Marshmallow geliştiricileri tarafından oluşturuldu. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham verdi?" |
|||
API'lar için açık standart desteği olmalı (OpenAPI gibi). |
|||
|
|||
### <a href="https://flask-apispec.readthedocs.io/en/latest/" class="external-link" target="_blank">Flask-apispec</a> |
|||
|
|||
Flask-apispec ise Webargs, Marshmallow ve APISpec'i birbirine bağlayan bir Flask <abbr title="Eklenti: Plug-In">eklentisi</abbr>. |
|||
|
|||
Webargs ve Marshmallow'daki bilgiyi APISpec ile otomatik OpenAPI şemaları üretmek için kullanıyor. |
|||
|
|||
Hak ettiği değeri görmeyen, harika bir araç. Piyasadaki çoğu Flask <abbr title="Eklenti: Plug-In">eklentisinden</abbr> çok daha popüler olmalı. Hak ettiği değeri görmüyor oluşunun sebebi ise dökümantasyonun çok kısa ve soyut olması olabilir. |
|||
|
|||
Böylece Flask-apispec, Python döküman dizilerine YAML gibi farklı bir syntax yazma sorununu çözmüş oldu. |
|||
|
|||
**FastAPI**'ı geliştirene dek benim favori arka uç kombinasyonum Flask'in yanında Marshmallow ve Webargs ile birlikte Flask-apispec idi. |
|||
|
|||
Bunu kullanmak, bir kaç <abbr title="full-stack: Hem ön uç hem de arka uç geliştirme">full-stack</abbr> Flask projesi oluşturucusunun yaratılmasına yol açtı. Bunlar benim (ve bir kaç harici ekibin de) şimdiye kadar kullandığı asıl <abbr title="stack: Projeyi geliştirirken kullanılan araçlar dizisi">stack</abbr>ler: |
|||
|
|||
* <a href="https://github.com/tiangolo/full-stack" class="external-link" target="_blank">https://github.com/tiangolo/full-stack</a> |
|||
* <a href="https://github.com/tiangolo/full-stack-flask-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchbase</a> |
|||
* <a href="https://github.com/tiangolo/full-stack-flask-couchdb" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-flask-couchdb</a> |
|||
|
|||
Aynı full-stack üreticiler [**FastAPI** Proje Üreticileri](project-generation.md){.internal-link target=_blank}'nin de temelini oluşturdu. |
|||
|
|||
!!! info "Bilgi" |
|||
Flask-apispec de aynı Marshmallow geliştiricileri tarafından üretildi. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham oldu?" |
|||
Veri dönüşümü ve veri doğrulamayı tanımlayan kodu kullanarak otomatik olarak OpenAPI şeması oluşturmalı. |
|||
|
|||
### <a href="https://nestjs.com/" class="external-link" target="_blank">NestJS</a> (and <a href="https://angular.io/" class="external-link" target="_blank">Angular</a>) |
|||
|
|||
Bu Python teknolojisi bile değil. NestJS, Angulardan ilham almış olan bir JavaScript (TypeScript) NodeJS framework'ü. |
|||
|
|||
Flask-apispec ile yapılabileceklere nispeten benzeyen bir şey elde ediyor. |
|||
|
|||
Angular 2'den ilham alan, içine gömülü bir <abbr title="Bağımlılık enjeksiyonu: Dependency Injection">bağımlılık enjeksiyonu</abbr> sistemi var. Bildiğim diğer tüm bağımlılık enjeksiyonu sistemlerinde olduğu gibi"<abbr title="Injectable: dependency injection sistemi tarafından enjekte edilecek dependency (bağımlılık)">bağımlılık</abbr>"ları önceden kaydetmenizi gerektiriyor. Böylece projeyi daha detaylı hale getiriyor ve kod tekrarını da arttırıyor. |
|||
|
|||
Parametreler TypeScript tipleri (Python tip belirteçlerine benzer) ile açıklandığından editör desteği oldukça iyi. |
|||
|
|||
Ama TypeScript verileri kod JavaScript'e derlendikten sonra korunmadığından, bunlara dayanarak aynı anda veri doğrulaması, veri dönüşümü ve dökümantasyon tanımlanamıyor. Bundan ve bazı tasarım tercihlerinden dolayı veri doğrulaması, dönüşümü ve otomatik şema üretimi için pek çok yere dekorator eklemek gerekiyor. Bu da projeyi oldukça detaylandırıyor. |
|||
|
|||
İç içe geçen derin modelleri pek iyi işleyemiyor. Yani eğer istekteki JSON gövdesi derin bir JSON objesiyse düzgün bir şekilde dökümante edilip doğrulanamıyor. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham oldu?" |
|||
Güzel bir editör desteği için Python tiplerini kullanmalı. |
|||
|
|||
Güçlü bir bağımlılık enjeksiyon sistemine sahip olmalı. Kod tekrarını minimuma indirecek bir yol bulmalı. |
|||
|
|||
### <a href="https://sanic.readthedocs.io/en/latest/" class="external-link" target="_blank">Sanic</a> |
|||
|
|||
Sanic, `asyncio`'ya dayanan son derece hızlı Python kütüphanelerinden biriydi. Flask'a epey benzeyecek şekilde geliştirilmişti. |
|||
|
|||
!!! note "Teknik detaylar" |
|||
İçerisinde standart Python `asyncio` döngüsü yerine <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> kullanıldı. Hızının asıl kaynağı buydu. |
|||
|
|||
Uvicorn ve Starlette'e ilham kaynağı olduğu oldukça açık, şu anda ikisi de açık karşılaştırmalarda Sanicten daha hızlı gözüküyor. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham oldu?" |
|||
Uçuk performans sağlayacak bir yol bulmalı. |
|||
|
|||
Tam da bu yüzden **FastAPI** Starlette'e dayanıyor, çünkü Starlette şu anda kullanılabilir en hızlı framework. (üçüncü parti karşılaştırmalı testlerine göre) |
|||
|
|||
### <a href="https://falconframework.org/" class="external-link" target="_blank">Falcon</a> |
|||
|
|||
Falcon ise bir diğer yüksek performanslı Python framework'ü. Minimal olacak şekilde Hug gibi diğer framework'lerin temeli olabilmek için tasarlandı. |
|||
|
|||
İki parametre kabul eden fonksiyonlar şeklinde tasarlandı, biri "istek" ve diğeri ise "cevap". Sonra isteğin çeşitli kısımlarını **okuyor**, cevaba ise bir şeyler **yazıyorsunuz**. Bu tasarımdan dolayı istek parametrelerini ve gövdelerini standart Python tip belirteçlerini kullanarak fonksiyon parametreleriyle belirtmek mümkün değil. |
|||
|
|||
Yani veri doğrulama, veri dönüştürme ve dökümantasyonun hepsi kodda yer almalı, otomatik halledemiyoruz. Ya da Falcon üzerine bir framework olarak uygulanmaları gerekiyor, aynı Hug'da olduğu gibi. Bu ayrım Falcon'un tasarımından esinlenen, istek ve cevap objelerini parametre olarak işleyen diğer kütüphanelerde de yer alıyor. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham oldu?" |
|||
Harika bir performans'a sahip olmanın yollarını bulmalı. |
|||
|
|||
Hug ile birlikte (Hug zaten Falcon'a dayandığından) **FastAPI**'ın fonksiyonlarda `cevap` parametresi belirtmesinde ilham kaynağı oldu. |
|||
|
|||
FastAPI'da opsiyonel olmasına rağmen, daha çok header'lar, çerezler ve alternatif durum kodları belirlemede kullanılıyor. |
|||
|
|||
### <a href="https://moltenframework.com/" class="external-link" target="_blank">Molten</a> |
|||
|
|||
**FastAPI**'ı geliştirmenin ilk aşamalarında Molten'ı keşfettim. Pek çok ortak fikrimiz vardı: |
|||
|
|||
* Python'daki tip belirteçlerini baz alıyordu. |
|||
* Bunlara bağlı olarak veri doğrulaması ve dökümantasyon sağlıyordu. |
|||
* Bir <abbr title="Bağımlılık enjeksiyonu: Dependency Injection">bağımlılık enjeksiyonu</abbr> sistemi vardı. |
|||
|
|||
Veri doğrulama, veri dönüştürme ve dökümantasyon için Pydantic gibi bir üçüncü parti kütüphane kullanmıyor, kendi içerisinde bunlara sahip. Yani bu veri tipi tanımlarını tekrar kullanmak pek de kolay değil. |
|||
|
|||
Biraz daha detaylı ayarlamalara gerek duyuyor. Ayrıca <abbr title="ASGI (Asynchronous Server Gateway Interface): Asenkron Sunucu Ağ Geçidi Arabirimi, asenkron Python web uygulamaları geliştirmek için yeni standart.">ASGI</abbr> yerine <abbr title="WSGI (Web Server Gateway Interface): Web Sunucusu Ağ Geçidi Arabirimi, Pythonda senkron web uygulamaları geliştirmek için eski standart.">WSGI</abbr>'a dayanıyor. Yani Uvicorn, Starlette ve Sanic gibi araçların yüksek performansından faydalanacak şekilde tasarlanmamış. |
|||
|
|||
<abbr title="Bağımlılık enjeksiyonu: Dependency Injection">Bağımlılık enjeksiyonu</abbr> sistemi bağımlılıkların önceden kaydedilmesini ve sonrasında belirlenen veri tiplerine göre çözülmesini gerektiriyor. Yani spesifik bir tip, birden fazla bileşen ile belirlenemiyor. |
|||
|
|||
<abbr title="Route: HTTP isteğinin gittiği yol">Yol</abbr>'lar fonksiyonun üstünde endpoint'i işleyen dekoratörler yerine farklı yerlerde tanımlanan fonksiyonlarla belirlenir. Bu Flask (ve Starlette) yerine daha çok Django'nun yaklaşımına daha yakın bir metot. Bu, kodda nispeten birbiriyle sıkı ilişkili olan şeyleri ayırmaya sebep oluyor. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham oldu?" |
|||
Model özelliklerinin "standart" değerlerini kullanarak veri tipleri için ekstra veri doğrulama koşulları tanımlamalı. Bu editör desteğini geliştiriyor ve daha önceden Pydantic'te yoktu. |
|||
|
|||
Bu aslında Pydantic'in de aynı doğrulama stiline geçmesinde ilham kaynağı oldu. Şu anda bütün bu özellikler Pydantic'in yapısında yer alıyor. |
|||
|
|||
### <a href="https://www.hug.rest/" class="external-link" target="_blank">Hug</a> |
|||
|
|||
Hug, Python tip belirteçlerini kullanarak API parametrelerinin tipini belirlemeyi uygulayan ilk framework'lerdendi. Bu, diğer araçlara da ilham kaynağı olan harika bir fikirdi. |
|||
|
|||
Tip belirlerken standart Python veri tipleri yerine kendi özel tiplerini kullandı, yine de bu ileriye dönük devasa bir adımdı. |
|||
|
|||
Hug ayrıca tüm API'ı JSON ile ifade eden özel bir şema oluşturan ilk framework'lerdendir. |
|||
|
|||
OpenAPI veya JSON Şeması gibi bir standarda bağlı değildi. Yani Swagger UI gibi diğer araçlarla entegre etmek kolay olmazdı. Ama yine de, bu oldukça yenilikçi bir fikirdi. |
|||
|
|||
Ayrıca ilginç ve çok rastlanmayan bir özelliği vardı: aynı framework'ü kullanarak hem API'lar hem de <abbr title="Command Line Tool (CLI): Komut satırı aracı">CLI</abbr>'lar oluşturmak mümkündü. |
|||
|
|||
Senkron çalışan Python web framework'lerinin standardına (WSGI) dayandığından dolayı Websocket'leri ve diğer şeyleri işleyemiyor, ancak yine de yüksek performansa sahip. |
|||
|
|||
!!! info "Bilgi" |
|||
Hug, Python dosyalarında bulunan dahil etme satırlarını otomatik olarak sıralayan harika bir araç olan <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>'un geliştiricisi Timothy Crosley tarafından geliştirildi. |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham oldu?" |
|||
Hug, APIStar'ın çeşitli kısımlarında esin kaynağı oldu ve APIStar'la birlikte en umut verici bulduğum araçlardan biriydi. |
|||
|
|||
**FastAPI**, Python tip belirteçlerini kullanarak parametre belirlemede ve API'ı otomatık tanımlayan bir şema üretmede de Hug'a esinlendi. |
|||
|
|||
**FastAPI**'ın header ve çerez tanımlamak için fonksiyonlarda `response` parametresini belirtmesinde de Hug'dan ilham alındı. |
|||
|
|||
### <a href="https://github.com/encode/apistar" class="external-link" target="_blank">APIStar</a> (<= 0.5) |
|||
|
|||
**FastAPI**'ı geliştirmeye başlamadan hemen önce **APIStar** sunucusunu buldum. Benim aradığım şeylerin neredeyse hepsine sahipti ve harika bir tasarımı vardı. |
|||
|
|||
Benim şimdiye kadar gördüğüm Python tip belirteçlerini kullanarak parametre ve istekler belirlemeyi uygulayan ilk framework'lerdendi (Molten ve NestJS'den önce). APIStar'ı da aşağı yukarı Hug ile aynı zamanlarda buldum. Fakat APIStar OpenAPI standardını kullanıyordu. |
|||
|
|||
Farklı yerlerdeki tip belirteçlerine bağlı olarak otomatik veri doğrulama, veri dönüştürme ve OpenAPI şeması oluşturma desteği sunuyordu. |
|||
|
|||
Gövde şema tanımları Pydantic ile aynı Python tip belirteçlerini kullanmıyordu, biraz daha Marsmallow'a benziyordu. Dolayısıyla editör desteği de o kadar iyi olmazdı ama APIStar eldeki en iyi seçenekti. |
|||
|
|||
O dönemlerde karşılaştırmalarda en iyi performansa sahipti (yalnızca Starlette'e kaybediyordu). |
|||
|
|||
Başlangıçta otomatik API dökümantasyonu sunan bir web arayüzü yoktu, ama ben ona Swagger UI ekleyebileceğimi biliyordum. |
|||
|
|||
Bağımlılık enjeksiyon sistemi vardı. Yukarıda bahsettiğim diğer araçlar gibi bundaki sistem de bileşenlerin önceden kaydedilmesini gerektiriyordu. Yine de harika bir özellikti. |
|||
|
|||
Güvenlik entegrasyonu olmadığından dolayı APIStar'ı hiç bir zaman tam bir projede kullanamadım. Bu yüzden Flask-apispec'e bağlı full-stack proje üreticilerde sahip olduğum özellikleri tamamen değiştiremedim. Bu güvenlik entegrasyonunu ekleyen bir <abbr title="Pull request (PR): Git sistemlerinde projenin bir branch'ine yapılan değişikliğin sistemde diğer kullanıcılara ifade edilmesi">PR</abbr> oluşturmak da projelerim arasında yer alıyordu. |
|||
|
|||
Sonrasında ise projenin odağı değişti. |
|||
|
|||
Geliştiricinin Starlette'e odaklanması gerekince proje de artık bir API web framework'ü olmayı bıraktı. |
|||
|
|||
Artık APIStar, OpenAPI özelliklerini doğrulamak için bir dizi araç sunan bir proje haline geldi. |
|||
|
|||
!!! info "Bilgi" |
|||
APIStar, aşağıdaki projeleri de üreten Tom Christie tarafından geliştirildi: |
|||
|
|||
* Django REST Framework |
|||
* **FastAPI**'ın da dayandığı Starlette |
|||
* Starlette ve **FastAPI** tarafından da kullanılan Uvicorn |
|||
|
|||
!!! check "**FastAPI**'a nasıl ilham oldu?" |
|||
Var oldu. |
|||
|
|||
Aynı Python veri tipleriyle birden fazla şeyi belirleme (veri doğrulama, veri dönüştürme ve dökümantasyon), bir yandan da harika bir editör desteği sunma, benim muhteşem bulduğum bir fikirdi. |
|||
|
|||
Uzunca bir süre boyunca benzer bir framework arayıp pek çok farklı alternatifi denedikten sonra, APIStar en iyi seçenekti. |
|||
|
|||
Sonra APIStar bir sunucu olmayı bıraktı ve Starlette oluşturuldu. Starlette, böyle bir sunucu sistemi için daha iyi bir temel sunuyordu. Bu da **FastAPI**'ın son esin kaynağıydı. |
|||
|
|||
Ben bu önceki araçlardan öğrendiklerime dayanarak **FastAPI**'ın özelliklerini arttırıp geliştiriyor, <abbr title="Tip desteği (typing support): kod yapısında parametrelere, argümanlara ve objelerin özelliklerine veri tipi atamak">tip desteği</abbr> sistemi ve diğer kısımları iyileştiriyorum ancak yine de **FastAPI**'ı APIStar'ın "ruhani varisi" olarak görüyorum. |
|||
|
|||
## **FastAPI** Tarafından Kullanılanlar |
|||
|
|||
### <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> |
|||
|
|||
Pydantic Python tip belirteçlerine dayanan; veri doğrulama, veri dönüştürme ve dökümantasyon tanımlamak (JSON Şema kullanarak) için bir kütüphanedir. |
|||
|
|||
Tip belirteçleri kullanıyor olması onu aşırı sezgisel yapıyor. |
|||
|
|||
Marshmallow ile karşılaştırılabilir. Ancak karşılaştırmalarda Marshmallowdan daha hızlı görünüyor. Aynı Python tip belirteçlerine dayanıyor ve editör desteği de harika. |
|||
|
|||
!!! check "**FastAPI** nerede kullanıyor?" |
|||
Bütün veri doğrulama, veri dönüştürme ve JSON Şemasına bağlı otomatik model dökümantasyonunu halletmek için! |
|||
|
|||
**FastAPI** yaptığı her şeyin yanı sıra bu JSON Şema verisini alıp daha sonra OpenAPI'ya yerleştiriyor. |
|||
|
|||
### <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> |
|||
|
|||
Starlette hafif bir <abbr title="ASGI (Asynchronous Server Gateway Interface): Asenkron Sunucu Ağ Geçidi Arabirimi, asenkron Python web uygulamaları geliştirmek için yeni standart.">ASGI</abbr> framework'ü ve yüksek performanslı asyncio servisleri oluşturmak için ideal. |
|||
|
|||
Kullanımı çok kolay ve sezgisel, kolaylıkla genişletilebilecek ve modüler bileşenlere sahip olacak şekilde tasarlandı. |
|||
|
|||
Sahip olduğu bir kaç özellik: |
|||
|
|||
* Cidden etkileyici bir performans. |
|||
* WebSocket desteği. |
|||
* İşlem-içi arka plan görevleri. |
|||
* Başlatma ve kapatma olayları. |
|||
* HTTPX ile geliştirilmiş bir test istemcisi. |
|||
* CORS, GZip, Static Files ve Streaming cevapları desteği. |
|||
* Session ve çerez desteği. |
|||
* Kodun %100'ü test kapsamında. |
|||
* Kodun %100'ü tip belirteçleriyle desteklenmiştir. |
|||
* Yalnızca bir kaç zorunlu bağımlılığa sahip. |
|||
|
|||
Starlette şu anda test edilen en hızlı Python framework'ü. Yalnızca bir sunucu olan Uvicorn'a yeniliyor, o da zaten bir framework değil. |
|||
|
|||
Starlette bütün temel web mikro framework işlevselliğini sağlıyor. |
|||
|
|||
Ancak otomatik veri doğrulama, veri dönüştürme ve dökümantasyon sağlamyor. |
|||
|
|||
Bu, **FastAPI**'ın onun üzerine tamamen Python tip belirteçlerine bağlı olarak eklediği (Pydantic ile) ana şeylerden biri. **FastAPI** bunun yanında artı olarak bağımlılık enjeksiyonu sistemi, güvenlik araçları, OpenAPI şema üretimi ve benzeri özellikler de ekliyor. |
|||
|
|||
!!! note "Teknik Detaylar" |
|||
ASGI, Django'nun ana ekibi tarafından geliştirilen yeni bir "standart". Bir "Python standardı" (PEP) olma sürecinde fakat henüz bir standart değil. |
|||
|
|||
Bununla birlikte, halihazırda birçok araç tarafından bir "standart" olarak kullanılmakta. Bu, Uvicorn'u farklı ASGI sunucularıyla (Daphne veya Hypercorn gibi) değiştirebileceğiniz veya `python-socketio` gibi ASGI ile uyumlu araçları ekleyebileciğiniz için birlikte çalışılabilirliği büyük ölçüde arttırıyor. |
|||
|
|||
!!! check "**FastAPI** nerede kullanıyor?" |
|||
|
|||
Tüm temel web kısımlarında üzerine özellikler eklenerek kullanılmakta. |
|||
|
|||
`FastAPI` sınıfının kendisi direkt olarak `Starlette` sınıfını miras alıyor! |
|||
|
|||
Yani, Starlette ile yapabileceğiniz her şeyi, Starlette'in bir nevi güçlendirilmiş hali olan **FastAPI** ile doğrudan yapabilirsiniz. |
|||
|
|||
### <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a> |
|||
|
|||
Uvicorn, uvlook ile httptools üzerine kurulu ışık hzında bir ASGI sunucusudur. |
|||
|
|||
Bir web framework'ünden ziyade bir sunucudur, yani yollara bağlı yönlendirme yapmanızı sağlayan araçları yoktur. Bu daha çok Starlette (ya da **FastAPI**) gibi bir framework'ün sunucuya ek olarak sağladığı bir şeydir. |
|||
|
|||
Starlette ve **FastAPI** için tavsiye edilen sunucu Uvicorndur. |
|||
|
|||
!!! check "**FastAPI** neden tavsiye ediyor?" |
|||
**FastAPI** uygulamalarını çalıştırmak için ana web sunucusu Uvicorn! |
|||
|
|||
Gunicorn ile birleştirdiğinizde asenkron ve çoklu işlem destekleyen bir sunucu elde ediyorsunuz! |
|||
|
|||
Daha fazla detay için [Deployment](deployment/index.md){.internal-link target=_blank} bölümünü inceleyebilirsiniz. |
|||
|
|||
## Karşılaştırma ve Hız |
|||
|
|||
Uvicorn, Starlette ve FastAPI arasındakı farkı daha iyi anlamak ve karşılaştırma yapmak için [Kıyaslamalar](benchmarks.md){.internal-link target=_blank} bölümüne bakın! |
@ -1,34 +1,34 @@ |
|||
# Kıyaslamalar |
|||
|
|||
Bağımsız TechEmpower kıyaslamaları gösteriyor ki Uvicorn'la beraber çalışan **FastAPI** uygulamaları <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'un en hızlı frameworklerinden birisi </a>, sadece Starlette ve Uvicorn'dan daha düşük sıralamada (FastAPI bu frameworklerin üzerine kurulu). (*) |
|||
Bağımsız TechEmpower kıyaslamaları gösteriyor ki <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">en hızlı Python frameworklerinden birisi</a> olan Uvicorn ile çalıştırılan **FastAPI** uygulamaları, sadece Starlette ve Uvicorn'dan daha düşük sıralamada (FastAPI bu frameworklerin üzerine kurulu) yer alıyor. (*) |
|||
|
|||
Fakat kıyaslamaları ve karşılaştırmaları incelerken şunları aklınızda bulundurmalısınız. |
|||
|
|||
## Kıyaslamalar ve hız |
|||
## Kıyaslamalar ve Hız |
|||
|
|||
Kıyaslamaları incelediğinizde, farklı özelliklere sahip birçok araçların eşdeğer olarak karşılaştırıldığını görmek yaygındır. |
|||
Kıyaslamaları incelediğinizde, farklı özelliklere sahip araçların eşdeğer olarak karşılaştırıldığını yaygın bir şekilde görebilirsiniz. |
|||
|
|||
Özellikle, Uvicorn, Starlette ve FastAPI'ın birlikte karşılaştırıldığını görmek için (diğer birçok araç arasında). |
|||
Özellikle, (diğer birçok araç arasında) Uvicorn, Starlette ve FastAPI'ın birlikte karşılaştırıldığını görebilirsiniz. |
|||
|
|||
Araç tarafından çözülen sorun ne kadar basitse, o kadar iyi performans alacaktır. Ve kıyaslamaların çoğu, araç tarafından sağlanan ek özellikleri test etmez. |
|||
Aracın çözdüğü problem ne kadar basitse, performansı o kadar iyi olacaktır. Ancak kıyaslamaların çoğu, aracın sağladığı ek özellikleri test etmez. |
|||
|
|||
Hiyerarşi şöyledir: |
|||
|
|||
* **Uvicorn**: bir ASGI sunucusu |
|||
* **Starlette**: (Uvicorn'u kullanır) bir web microframeworkü |
|||
* **FastAPI**: (Starlette'i kullanır) data validation vb. ile API'lar oluşturmak için çeşitli ek özelliklere sahip bir API frameworkü |
|||
* **Starlette**: (Uvicorn'u kullanır) bir web mikroframeworkü |
|||
* **FastAPI**: (Starlette'i kullanır) veri doğrulama vb. çeşitli ek özelliklere sahip, API oluşturmak için kullanılan bir API mikroframeworkü |
|||
|
|||
* **Uvicorn**: |
|||
* Sunucunun kendisi dışında ekstra bir kod içermediği için en iyi performansa sahip olacaktır |
|||
* Direkt olarak Uvicorn'da bir uygulama yazmazsınız. Bu, en azından Starlette tarafından sağlanan tüm kodu (veya **FastAPI**) az çok içermesi gerektiği anlamına gelir. Ve eğer bunu yaptıysanız, son uygulamanız bir framework kullanmak ve uygulama kodlarını ve bugları en aza indirmekle aynı ek yüke sahip olacaktır. |
|||
* Sunucunun kendisi dışında ekstra bir kod içermediği için en iyi performansa sahip olacaktır. |
|||
* Doğrudan Uvicorn ile bir uygulama yazmazsınız. Bu, yazdığınız kodun en azından Starlette tarafından sağlanan tüm kodu (veya **FastAPI**) az çok içermesi gerektiği anlamına gelir. Eğer bunu yaptıysanız, son uygulamanız bir framework kullanmak ve uygulama kodlarını ve hataları en aza indirmekle aynı ek yüke sahip olacaktır. |
|||
* Eğer Uvicorn'u karşılaştırıyorsanız, Daphne, Hypercorn, uWSGI, vb. uygulama sunucuları ile karşılaştırın. |
|||
* **Starlette**: |
|||
* Uvicorn'dan sonraki en iyi performansa sahip olacak. Aslında, Starlette çalışmak için Uvicorn'u kullanıyor. Dolayısıyla, muhtemelen daha fazla kod çalıştırmak zorunda kaldığında Uvicorn'dan sadece "daha yavaş" olabilir. |
|||
* Ancak routing based on paths ile vb. basit web uygulamaları oluşturmak için araçlar sağlar. |
|||
* Eğer Starlette'i karşılaştırıyorsanız, Sanic, Flask, Django, vb. frameworkler (veya microframeworkler) ile karşılaştırın. |
|||
* Uvicorn'dan sonraki en iyi performansa sahip olacaktır. İşin aslı, Starlette çalışmak için Uvicorn'u kullanıyor. Dolayısıyla, daha fazla kod çalıştırmaası gerektiğinden muhtemelen Uvicorn'dan sadece "daha yavaş" olabilir. |
|||
* Ancak yol bazlı yönlendirme vb. basit web uygulamaları oluşturmak için araçlar sağlar. |
|||
* Eğer Starlette'i karşılaştırıyorsanız, Sanic, Flask, Django, vb. frameworkler (veya mikroframeworkler) ile karşılaştırın. |
|||
* **FastAPI**: |
|||
* Starlette'in Uvicorn'u kullandığı ve ondan daha hızlı olamayacağı gibi, **FastAPI** da Starlette'i kullanır, bu yüzden ondan daha hızlı olamaz. |
|||
* FastAPI, Starlette'e ek olarak daha fazla özellik sunar. Data validation ve serialization gibi API'lar oluştururken neredeyse ve her zaman ihtiyaç duyduğunuz özellikler. Ve bunu kullanarak, ücretsiz olarak otomatik dokümantasyon elde edersiniz (otomatik dokümantasyon çalışan uygulamalara ek yük getirmez, başlangıçta oluşturulur). |
|||
* FastAPI'ı kullanmadıysanız ve Starlette'i doğrudan kullandıysanız (veya başka bir araç, Sanic, Flask, Responder, vb.) tüm data validation'ı ve serialization'ı kendiniz sağlamanız gerekir. Dolayısıyla, son uygulamanız FastAPI kullanılarak oluşturulmuş gibi hâlâ aynı ek yüke sahip olacaktır. Çoğu durumda, uygulamalarda yazılan kodun büyük çoğunluğunu data validation ve serialization oluşturur. |
|||
* Dolayısıyla, FastAPI'ı kullanarak geliştirme süresinden, buglardan, kod satırlarından tasarruf edersiniz ve muhtemelen kullanmasaydınız aynı performansı (veya daha iyisini) elde edersiniz. (hepsini kodunuza uygulamak zorunda kalacağınız gibi) |
|||
* Eğer FastAPI'ı karşılaştırıyorsanız, Flask-apispec, NestJS, Molten, vb. gibi data validation, serialization ve dokümantasyon sağlayan bir web uygulaması frameworkü ile (veya araç setiyle) karşılaştırın. Entegre otomatik data validation, serialization ve dokümantasyon içeren frameworkler. |
|||
* Starlette'in Uvicorn'u kullandığı ve ondan daha hızlı olamayacağı gibi, **FastAPI**'da Starlette'i kullanır, dolayısıyla ondan daha hızlı olamaz. |
|||
* FastAPI, Starlette'e ek olarak daha fazla özellik sunar. Bunlar veri doğrulama ve <abbr title="Dönüşüm: serialization, parsing, marshalling olarak da biliniyor">dönüşümü</abbr> gibi API'lar oluştururken neredeyse ve her zaman ihtiyaç duyduğunuz özelliklerdir. Ve bunu kullanarak, ücretsiz olarak otomatik dokümantasyon elde edersiniz (otomatik dokümantasyon çalışan uygulamalara ek yük getirmez, başlangıçta oluşturulur). |
|||
* FastAPI'ı kullanmadıysanız ve Starlette'i doğrudan kullandıysanız (veya başka bir araç, Sanic, Flask, Responder, vb.) tüm veri doğrulama ve dönüştürme araçlarını kendiniz geliştirmeniz gerekir. Dolayısıyla, son uygulamanız FastAPI kullanılarak oluşturulmuş gibi hâlâ aynı ek yüke sahip olacaktır. Çoğu durumda, uygulamalarda yazılan kodun büyük bir kısmını veri doğrulama ve dönüştürme kodları oluşturur. |
|||
* Dolayısıyla, FastAPI'ı kullanarak geliştirme süresinden, hatalardan, kod satırlarından tasarruf edersiniz ve kullanmadığınız durumda (birçok özelliği geliştirmek zorunda kalmakla birlikte) muhtemelen aynı performansı (veya daha iyisini) elde ederdiniz. |
|||
* Eğer FastAPI'ı karşılaştırıyorsanız, Flask-apispec, NestJS, Molten, vb. gibi veri doğrulama, dönüştürme ve dokümantasyon sağlayan bir web uygulaması frameworkü ile (veya araç setiyle) karşılaştırın. |
|||
|
@ -0,0 +1,3 @@ |
|||
# Yardım |
|||
|
|||
Yardım alın, yardım edin, katkıda bulunun, dahil olun. 🤝 |
@ -0,0 +1,79 @@ |
|||
# Geçmişi, Tasarımı ve Geleceği |
|||
|
|||
Bir süre önce, <a href="https://github.com/tiangolo/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">bir **FastAPI** kullanıcısı sordu</a>: |
|||
|
|||
> Bu projenin geçmişi nedir? Birkaç hafta içinde hiçbir yerden harika bir şeye dönüşmüş gibi görünüyor [...] |
|||
|
|||
İşte o geçmişin bir kısmı. |
|||
|
|||
## Alternatifler |
|||
|
|||
Bir süredir karmaşık gereksinimlere sahip API'lar oluşturuyor (Makine Öğrenimi, dağıtık sistemler, asenkron işler, NoSQL veritabanları vb.) ve farklı geliştirici ekiplerini yönetiyorum. |
|||
|
|||
Bu süreçte birçok alternatifi araştırmak, test etmek ve kullanmak zorunda kaldım. |
|||
|
|||
**FastAPI**'ın geçmişi, büyük ölçüde önceden geliştirilen araçların geçmişini kapsıyor. |
|||
|
|||
[Alternatifler](alternatives.md){.internal-link target=_blank} bölümünde belirtildiği gibi: |
|||
|
|||
<blockquote markdown="1"> |
|||
|
|||
Başkalarının daha önceki çalışmaları olmasaydı, **FastAPI** var olmazdı. |
|||
|
|||
Geçmişte oluşturulan pek çok araç **FastAPI**'a ilham kaynağı olmuştur. |
|||
|
|||
Yıllardır yeni bir framework oluşturmaktan kaçınıyordum. Başlangıçta **FastAPI**'ın çözdüğü sorunları çözebilmek için pek çok farklı framework, <abbr title="Eklenti: Plug-In">eklenti</abbr> ve araç kullanmayı denedim. |
|||
|
|||
Ancak bir noktada, geçmişteki diğer araçlardan en iyi fikirleri alarak bütün bu çözümleri kapsayan, ayrıca bütün bunları Python'ın daha önce mevcut olmayan özelliklerini (Python 3.6+ ile gelen <abbr title="Tip belirteçleri: Type Hints">tip belirteçleri</abbr>) kullanarak yapan bir şey üretmekten başka bir seçenek kalmamıştı. |
|||
|
|||
</blockquote> |
|||
|
|||
## Araştırma |
|||
|
|||
Önceki alternatifleri kullanarak hepsinden bir şeyler öğrenip, fikirler alıp, bunları kendim ve çalıştığım geliştirici ekipler için en iyi şekilde birleştirebilme şansım oldu. |
|||
|
|||
Mesela, ideal olarak standart Python tip belirteçlerine dayanması gerektiği açıktı. |
|||
|
|||
Ayrıca, en iyi yaklaşım zaten mevcut olan standartları kullanmaktı. |
|||
|
|||
Sonuç olarak, **FastAPI**'ı kodlamaya başlamadan önce, birkaç ay boyunca OpenAPI, JSON Schema, OAuth2 ve benzerlerinin tanımlamalarını inceledim. İlişkilerini, örtüştükleri noktaları ve farklılıklarını anlamaya çalıştım. |
|||
|
|||
## Tasarım |
|||
|
|||
Sonrasında, (**FastAPI** kullanan bir geliştirici olarak) sahip olmak istediğim "API"ı tasarlamak için biraz zaman harcadım. |
|||
|
|||
Çeşitli fikirleri en popüler Python editörlerinde test ettim: PyCharm, VS Code, Jedi tabanlı editörler. |
|||
|
|||
Bu test, en son <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">Python Developer Survey</a>'ine göre, kullanıcıların yaklaşık %80'inin kullandığı editörleri kapsıyor. |
|||
|
|||
Bu da demek oluyor ki **FastAPI**, Python geliştiricilerinin %80'inin kullandığı editörlerle test edildi. Ve diğer editörlerin çoğu benzer şekilde çalıştığından, avantajları neredeyse tüm editörlerde çalışacaktır. |
|||
|
|||
Bu şekilde, kod tekrarını mümkün olduğunca azaltmak, her yerde <abbr title="Otomatik Tamamlama: auto-complete, autocompletion, IntelliSense">otomatik tamamlama</abbr>, tip ve hata kontrollerine sahip olmak için en iyi yolları bulabildim. |
|||
|
|||
Hepsi, tüm geliştiriciler için en iyi geliştirme deneyimini sağlayacak şekilde. |
|||
|
|||
## Gereksinimler |
|||
|
|||
Çeşitli alternatifleri test ettikten sonra, avantajlarından dolayı <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">**Pydantic**</a>'i kullanmaya karar verdim. |
|||
|
|||
Sonra, JSON Schema ile tamamen uyumlu olmasını sağlamak, kısıtlama bildirimlerini tanımlamanın farklı yollarını desteklemek ve birkaç editördeki testlere dayanarak editör desteğini (tip kontrolleri, otomatik tamamlama) geliştirmek için katkıda bulundum. |
|||
|
|||
Geliştirme sırasında, diğer ana gereksinim olan <a href="https://www.starlette.io/" class="external-link" target="_blank">**Starlette**</a>'e de katkıda bulundum. |
|||
|
|||
## Geliştirme |
|||
|
|||
**FastAPI**'ı oluşturmaya başladığımda, parçaların çoğu zaten yerindeydi, tasarım tanımlanmıştı, gereksinimler ve araçlar hazırdı, standartlar ve tanımlamalar hakkındaki bilgi net ve tazeydi. |
|||
|
|||
## Gelecek |
|||
|
|||
Şimdiye kadar, **FastAPI**'ın fikirleriyle birçok kişiye faydalı olduğu apaçık ortada. |
|||
|
|||
Birçok kullanım durumuna daha iyi uyduğu için, önceki alternatiflerin yerine seçiliyor. |
|||
|
|||
Ben ve ekibim dahil, birçok geliştirici ve ekip projelerinde **FastAPI**'ya bağlı. |
|||
|
|||
Tabi, geliştirilecek birçok özellik ve iyileştirme mevcut. |
|||
|
|||
**FastAPI**'ın önünde harika bir gelecek var. |
|||
|
|||
[Yardımlarınız](help-fastapi.md){.internal-link target=_blank} çok değerlidir. |
@ -0,0 +1,11 @@ |
|||
# Nasıl Yapılır - Tarifler |
|||
|
|||
Burada çeşitli konular hakkında farklı tarifler veya "nasıl yapılır" kılavuzları yer alıyor. |
|||
|
|||
Bu fikirlerin büyük bir kısmı aşağı yukarı **bağımsız** olacaktır, çoğu durumda bunları sadece **projenize** hitap ediyorsa incelemelisiniz. |
|||
|
|||
Projeniz için ilginç ve yararlı görünen bir şey varsa devam edin ve inceleyin, aksi halde bunları atlayabilirsiniz. |
|||
|
|||
!!! tip "İpucu" |
|||
|
|||
**FastAPI**'ı düzgün (ve önerilen) şekilde öğrenmek istiyorsanız [Öğretici - Kullanıcı Rehberi](../tutorial/index.md){.internal-link target=_blank}'ni bölüm bölüm okuyun. |
@ -0,0 +1,5 @@ |
|||
# Öğren |
|||
|
|||
**FastAPI** öğrenmek için giriş bölümleri ve öğreticiler burada yer alıyor. |
|||
|
|||
Burayı, bir **kitap**, bir **kurs**, ve FastAPI öğrenmenin **resmi** ve önerilen yolu olarak düşünülebilirsiniz. 😎 |
@ -0,0 +1,3 @@ |
|||
# Kaynaklar |
|||
|
|||
Ek kaynaklar, dış bağlantılar, makaleler ve daha fazlası. ✈️ |
@ -0,0 +1,71 @@ |
|||
# 高级依赖项 |
|||
|
|||
## 参数化的依赖项 |
|||
|
|||
我们之前看到的所有依赖项都是写死的函数或类。 |
|||
|
|||
但也可以为依赖项设置参数,避免声明多个不同的函数或类。 |
|||
|
|||
假设要创建校验查询参数 `q` 是否包含固定内容的依赖项。 |
|||
|
|||
但此处要把待检验的固定内容定义为参数。 |
|||
|
|||
## **可调用**实例 |
|||
|
|||
Python 可以把类实例变为**可调用项**。 |
|||
|
|||
这里说的不是类本身(类本就是可调用项),而是类实例。 |
|||
|
|||
为此,需要声明 `__call__` 方法: |
|||
|
|||
```Python hl_lines="10" |
|||
{!../../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
本例中,**FastAPI** 使用 `__call__` 检查附加参数及子依赖项,稍后,还要调用它向*路径操作函数*传递值。 |
|||
|
|||
## 参数化实例 |
|||
|
|||
接下来,使用 `__init__` 声明用于**参数化**依赖项的实例参数: |
|||
|
|||
```Python hl_lines="7" |
|||
{!../../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
本例中,**FastAPI** 不使用 `__init__`,我们要直接在代码中使用。 |
|||
|
|||
## 创建实例 |
|||
|
|||
使用以下代码创建类实例: |
|||
|
|||
```Python hl_lines="16" |
|||
{!../../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
这样就可以**参数化**依赖项,它包含 `checker.fixed_content` 的属性 - `"bar"`。 |
|||
|
|||
## 把实例作为依赖项 |
|||
|
|||
然后,不要再在 `Depends(checker)` 中使用 `Depends(FixedContentQueryChecker)`, 而是要使用 `checker`,因为依赖项是类实例 - `checker`,不是类。 |
|||
|
|||
处理依赖项时,**FastAPI** 以如下方式调用 `checker`: |
|||
|
|||
```Python |
|||
checker(q="somequery") |
|||
``` |
|||
|
|||
……并用*路径操作函数*的参数 `fixed_content_included` 返回依赖项的值: |
|||
|
|||
```Python hl_lines="20" |
|||
{!../../../docs_src/dependencies/tutorial011.py!} |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
本章示例有些刻意,也看不出有什么用处。 |
|||
|
|||
这个简例只是为了说明高级依赖项的运作机制。 |
|||
|
|||
在有关安全的章节中,工具函数将以这种方式实现。 |
|||
|
|||
只要能理解本章内容,就能理解安全工具背后的运行机制。 |
@ -0,0 +1,351 @@ |
|||
# 使用代理 |
|||
|
|||
有些情况下,您可能要使用 Traefik 或 Nginx 等**代理**服务器,并添加应用不能识别的附加路径前缀配置。 |
|||
|
|||
此时,要使用 `root_path` 配置应用。 |
|||
|
|||
`root_path` 是 ASGI 规范提供的机制,FastAPI 就是基于此规范开发的(通过 Starlette)。 |
|||
|
|||
`root_path` 用于处理这些特定情况。 |
|||
|
|||
在挂载子应用时,也可以在内部使用。 |
|||
|
|||
## 移除路径前缀的代理 |
|||
|
|||
本例中,移除路径前缀的代理是指在代码中声明路径 `/app`,然后在应用顶层添加代理,把 **FastAPI** 应用放在 `/api/v1` 路径下。 |
|||
|
|||
本例的原始路径 `/app` 实际上是在 `/api/v1/app` 提供服务。 |
|||
|
|||
哪怕所有代码都假设只有 `/app`。 |
|||
|
|||
代理只在把请求传送给 Uvicorn 之前才会**移除路径前缀**,让应用以为它是在 `/app` 提供服务,因此不必在代码中加入前缀 `/api/v1`。 |
|||
|
|||
但之后,在(前端)打开 API 文档时,代理会要求在 `/openapi.json`,而不是 `/api/v1/openapi.json` 中提取 OpenAPI 概图。 |
|||
|
|||
因此, (运行在浏览器中的)前端会尝试访问 `/openapi.json`,但没有办法获取 OpenAPI 概图。 |
|||
|
|||
这是因为应用使用了以 `/api/v1` 为路径前缀的代理,前端要从 `/api/v1/openapi.json` 中提取 OpenAPI 概图。 |
|||
|
|||
```mermaid |
|||
graph LR |
|||
|
|||
browser("Browser") |
|||
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"] |
|||
server["Server on http://127.0.0.1:8000/app"] |
|||
|
|||
browser --> proxy |
|||
proxy --> server |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
IP `0.0.0.0` 常用于指程序监听本机或服务器上的所有有效 IP。 |
|||
|
|||
API 文档还需要 OpenAPI 概图声明 API `server` 位于 `/api/v1`(使用代理时的 URL)。例如: |
|||
|
|||
```JSON hl_lines="4-8" |
|||
{ |
|||
"openapi": "3.0.2", |
|||
// More stuff here |
|||
"servers": [ |
|||
{ |
|||
"url": "/api/v1" |
|||
} |
|||
], |
|||
"paths": { |
|||
// More stuff here |
|||
} |
|||
} |
|||
``` |
|||
|
|||
本例中的 `Proxy` 是 **Traefik**,`server` 是运行 FastAPI 应用的 **Uvicorn**。 |
|||
|
|||
### 提供 `root_path` |
|||
|
|||
为此,要以如下方式使用命令行选项 `--root-path`: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uvicorn main:app --root-path /api/v1 |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Hypercorn 也支持 `--root-path `选项。 |
|||
|
|||
!!! note "技术细节" |
|||
|
|||
ASGI 规范定义的 `root_path` 就是为了这种用例。 |
|||
|
|||
并且 `--root-path` 命令行选项支持 `root_path`。 |
|||
|
|||
### 查看当前的 `root_path` |
|||
|
|||
获取应用为每个请求使用的当前 `root_path`,这是 `scope` 字典的内容(也是 ASGI 规范的内容)。 |
|||
|
|||
我们在这里的信息里包含 `roo_path` 只是为了演示。 |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/behind_a_proxy/tutorial001.py!} |
|||
``` |
|||
|
|||
然后,用以下命令启动 Uvicorn: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uvicorn main:app --root-path /api/v1 |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
返回的响应如下: |
|||
|
|||
```JSON |
|||
{ |
|||
"message": "Hello World", |
|||
"root_path": "/api/v1" |
|||
} |
|||
``` |
|||
|
|||
### 在 FastAPI 应用里设置 `root_path` |
|||
|
|||
还有一种方案,如果不能提供 `--root-path` 或等效的命令行选项,则在创建 FastAPI 应用时要设置 `root_path` 参数。 |
|||
|
|||
```Python hl_lines="3" |
|||
{!../../../docs_src/behind_a_proxy/tutorial002.py!} |
|||
``` |
|||
|
|||
传递 `root_path` 给 `FastAPI` 与传递 `--root-path` 命令行选项给 Uvicorn 或 Hypercorn 一样。 |
|||
|
|||
### 关于 `root_path` |
|||
|
|||
注意,服务器(Uvicorn)只是把 `root_path` 传递给应用。 |
|||
|
|||
在浏览器中输入 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app 时能看到标准响应:</a> |
|||
|
|||
```JSON |
|||
{ |
|||
"message": "Hello World", |
|||
"root_path": "/api/v1" |
|||
} |
|||
``` |
|||
|
|||
它不要求访问 `http://127.0.0.1:800/api/v1/app`。 |
|||
|
|||
Uvicorn 预期代理在 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在顶部添加 `/api/v1` 前缀是代理要做的事情。 |
|||
|
|||
## 关于移除路径前缀的代理 |
|||
|
|||
注意,移除路径前缀的代理只是配置代理的方式之一。 |
|||
|
|||
大部分情况下,代理默认都不会移除路径前缀。 |
|||
|
|||
(未移除路径前缀时)代理监听 `https://myawesomeapp.com` 等对象,如果浏览器跳转到 `https://myawesomeapp.com/api/v1/app`,且服务器(例如 Uvicorn)监听 `http://127.0.0.1:8000` 代理(未移除路径前缀) 会在同样的路径:`http://127.0.0.1:8000/api/v1/app` 访问 Uvicorn。 |
|||
|
|||
## 本地测试 Traefik |
|||
|
|||
您可以轻易地在本地使用 <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a> 运行移除路径前缀的试验。 |
|||
|
|||
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">下载 Traefik</a>,这是一个二进制文件,需要解压文件,并在 Terminal 中直接运行。 |
|||
|
|||
然后创建包含如下内容的 `traefik.toml` 文件: |
|||
|
|||
```TOML hl_lines="3" |
|||
[entryPoints] |
|||
[entryPoints.http] |
|||
address = ":9999" |
|||
|
|||
[providers] |
|||
[providers.file] |
|||
filename = "routes.toml" |
|||
``` |
|||
|
|||
这个文件把 Traefik 监听端口设置为 `9999`,并设置要使用另一个文件 `routes.toml`。 |
|||
|
|||
!!! tip "提示" |
|||
|
|||
使用端口 9999 代替标准的 HTTP 端口 80,这样就不必使用管理员权限运行(`sudo`)。 |
|||
|
|||
接下来,创建 `routes.toml`: |
|||
|
|||
```TOML hl_lines="5 12 20" |
|||
[http] |
|||
[http.middlewares] |
|||
|
|||
[http.middlewares.api-stripprefix.stripPrefix] |
|||
prefixes = ["/api/v1"] |
|||
|
|||
[http.routers] |
|||
|
|||
[http.routers.app-http] |
|||
entryPoints = ["http"] |
|||
service = "app" |
|||
rule = "PathPrefix(`/api/v1`)" |
|||
middlewares = ["api-stripprefix"] |
|||
|
|||
[http.services] |
|||
|
|||
[http.services.app] |
|||
[http.services.app.loadBalancer] |
|||
[[http.services.app.loadBalancer.servers]] |
|||
url = "http://127.0.0.1:8000" |
|||
``` |
|||
|
|||
这个文件配置 Traefik 使用路径前缀 `/api/v1`。 |
|||
|
|||
然后,它把请求重定位到运行在 `http://127.0.0.1:8000` 上的 Uvicorn。 |
|||
|
|||
现在,启动 Traefik: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ ./traefik --configFile=traefik.toml |
|||
|
|||
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
接下来,使用 Uvicorn 启动应用,并使用 `--root-path` 选项: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uvicorn main:app --root-path /api/v1 |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
### 查看响应 |
|||
|
|||
访问含 Uvicorn 端口的 URL:<a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app,就能看到标准响应:</a> |
|||
|
|||
```JSON |
|||
{ |
|||
"message": "Hello World", |
|||
"root_path": "/api/v1" |
|||
} |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
注意,就算访问 `http://127.0.0.1:8000/app`,也显示从选项 `--root-path` 中提取的 `/api/v1`,这是 `root_path` 的值。 |
|||
|
|||
打开含 Traefik 端口的 URL,包含路径前缀:<a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app。</a> |
|||
|
|||
得到同样的响应: |
|||
|
|||
```JSON |
|||
{ |
|||
"message": "Hello World", |
|||
"root_path": "/api/v1" |
|||
} |
|||
``` |
|||
|
|||
但这一次 URL 包含了代理提供的路径前缀:`/api/v1`。 |
|||
|
|||
当然,这是通过代理访问应用的方式,因此,路径前缀 `/app/v1` 版本才是**正确**的。 |
|||
|
|||
而不带路径前缀的版本(`http://127.0.0.1:8000/app`),则由 Uvicorn 直接提供,专供*代理*(Traefik)访问。 |
|||
|
|||
这演示了代理(Traefik)如何使用路径前缀,以及服务器(Uvicorn)如何使用选项 `--root-path` 中的 `root_path`。 |
|||
|
|||
### 查看文档 |
|||
|
|||
但这才是有趣的地方 ✨ |
|||
|
|||
访问应用的**官方**方式是通过含路径前缀的代理。因此,不出所料,如果没有在 URL 中添加路径前缀,直接访问通过 Uvicorn 运行的 API 文档,不能正常访问,因为需要通过代理才能访问。 |
|||
|
|||
输入 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs 查看 API 文档:</a> |
|||
|
|||
<img src="/img/tutorial/behind-a-proxy/image01.png"> |
|||
|
|||
但输入**官方**链接 `/api/v1/docs`,并使用端口 `9999` 访问 API 文档,就能正常运行了!🎉 |
|||
|
|||
输入 <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs 查看文档:</a> |
|||
|
|||
<img src="/img/tutorial/behind-a-proxy/image02.png"> |
|||
|
|||
一切正常。 ✔️ |
|||
|
|||
这是因为 FastAPI 在 OpenAPI 里使用 `root_path` 提供的 URL 创建默认 `server`。 |
|||
|
|||
## 附加的服务器 |
|||
|
|||
!!! warning "警告" |
|||
|
|||
此用例较难,可以跳过。 |
|||
|
|||
默认情况下,**FastAPI** 使用 `root_path` 的链接在 OpenAPI 概图中创建 `server`。 |
|||
|
|||
但也可以使用其它备选 `servers`,例如,需要同一个 API 文档与 staging 和生产环境交互。 |
|||
|
|||
如果传递自定义 `servers` 列表,并有 `root_path`( 因为 API 使用了代理),**FastAPI** 会在列表开头使用这个 `root_path` 插入**服务器**。 |
|||
|
|||
例如: |
|||
|
|||
```Python hl_lines="4-7" |
|||
{!../../../docs_src/behind_a_proxy/tutorial003.py!} |
|||
``` |
|||
|
|||
这段代码生产如下 OpenAPI 概图: |
|||
|
|||
```JSON hl_lines="5-7" |
|||
{ |
|||
"openapi": "3.0.2", |
|||
// More stuff here |
|||
"servers": [ |
|||
{ |
|||
"url": "/api/v1" |
|||
}, |
|||
{ |
|||
"url": "https://stag.example.com", |
|||
"description": "Staging environment" |
|||
}, |
|||
{ |
|||
"url": "https://prod.example.com", |
|||
"description": "Production environment" |
|||
} |
|||
], |
|||
"paths": { |
|||
// More stuff here |
|||
} |
|||
} |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
注意,自动生成服务器时,`url` 的值 `/api/v1` 提取自 `roog_path`。 |
|||
|
|||
<a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs 的 API 文档所示如下:</a> |
|||
|
|||
<img src="/img/tutorial/behind-a-proxy/image03.png"> |
|||
|
|||
!!! tip "提示" |
|||
|
|||
API 文档与所选的服务器进行交互。 |
|||
|
|||
### 从 `root_path` 禁用自动服务器 |
|||
|
|||
如果不想让 **FastAPI** 包含使用 `root_path` 的自动服务器,则要使用参数 `root_path_in_servers=False`: |
|||
|
|||
```Python hl_lines="9" |
|||
{!../../../docs_src/behind_a_proxy/tutorial004.py!} |
|||
``` |
|||
|
|||
这样,就不会在 OpenAPI 概图中包含服务器了。 |
|||
|
|||
## 挂载子应用 |
|||
|
|||
如需挂载子应用(详见 [子应用 - 挂载](./sub-applications.md){.internal-link target=_blank}),也要通过 `root_path` 使用代理,这与正常应用一样,别无二致。 |
|||
|
|||
FastAPI 在内部使用 `root_path`,因此子应用也可以正常运行。✨ |
@ -0,0 +1,51 @@ |
|||
# 事件:启动 - 关闭 |
|||
|
|||
**FastAPI** 支持定义在应用启动前,或应用关闭后执行的事件处理器(函数)。 |
|||
|
|||
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。 |
|||
|
|||
!!! warning "警告" |
|||
|
|||
**FastAPI** 只执行主应用中的事件处理器,不执行[子应用 - 挂载](./sub-applications.md){.internal-link target=_blank}中的事件处理器。 |
|||
|
|||
## `startup` 事件 |
|||
|
|||
使用 `startup` 事件声明 `app` 启动前运行的函数: |
|||
|
|||
```Python hl_lines="8" |
|||
{!../../../docs_src/events/tutorial001.py!} |
|||
``` |
|||
|
|||
本例中,`startup` 事件处理器函数为项目数据库(只是**字典**)提供了一些初始值。 |
|||
|
|||
**FastAPI** 支持多个事件处理器函数。 |
|||
|
|||
只有所有 `startup` 事件处理器运行完毕,**FastAPI** 应用才开始接收请求。 |
|||
|
|||
## `shutdown` 事件 |
|||
|
|||
使用 `shutdown` 事件声明 `app` 关闭时运行的函数: |
|||
|
|||
```Python hl_lines="6" |
|||
{!../../../docs_src/events/tutorial002.py!} |
|||
``` |
|||
|
|||
此处,`shutdown` 事件处理器函数在 `log.txt` 中写入一行文本 `Application shutdown`。 |
|||
|
|||
!!! info "说明" |
|||
|
|||
`open()` 函数中,`mode="a"` 指的是**追加**。因此这行文本会添加在文件已有内容之后,不会覆盖之前的内容。 |
|||
|
|||
!!! tip "提示" |
|||
|
|||
注意,本例使用 Python `open()` 标准函数与文件交互。 |
|||
|
|||
这个函数执行 I/O(输入/输出)操作,需要等待内容写进磁盘。 |
|||
|
|||
但 `open()` 函数不支持使用 `async` 与 `await`。 |
|||
|
|||
因此,声明事件处理函数要使用 `def`,不能使用 `asnyc def`。 |
|||
|
|||
!!! info "说明" |
|||
|
|||
有关事件处理器的详情,请参阅 <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette 官档 - 事件</a>。 |
@ -0,0 +1,97 @@ |
|||
# 测试数据库 |
|||
|
|||
您还可以使用[测试依赖项](testing-dependencies.md){.internal-link target=_blank}中的覆盖依赖项方法变更测试的数据库。 |
|||
|
|||
实现设置其它测试数据库、在测试后回滚数据、或预填测试数据等操作。 |
|||
|
|||
本章的主要思路与上一章完全相同。 |
|||
|
|||
## 为 SQL 应用添加测试 |
|||
|
|||
为了使用测试数据库,我们要升级 [SQL 关系型数据库](../tutorial/sql-databases.md){.internal-link target=_blank} 一章中的示例。 |
|||
|
|||
应用的所有代码都一样,直接查看那一章的示例代码即可。 |
|||
|
|||
本章只是新添加了测试文件。 |
|||
|
|||
正常的依赖项 `get_db()` 返回数据库会话。 |
|||
|
|||
测试时使用覆盖依赖项返回自定义数据库会话代替正常的依赖项。 |
|||
|
|||
本例中,要创建仅用于测试的临时数据库。 |
|||
|
|||
## 文件架构 |
|||
|
|||
创建新文件 `sql_app/tests/test_sql_app.py`。 |
|||
|
|||
因此,新的文件架构如下: |
|||
|
|||
``` hl_lines="9-11" |
|||
. |
|||
└── sql_app |
|||
├── __init__.py |
|||
├── crud.py |
|||
├── database.py |
|||
├── main.py |
|||
├── models.py |
|||
├── schemas.py |
|||
└── tests |
|||
├── __init__.py |
|||
└── test_sql_app.py |
|||
``` |
|||
|
|||
## 创建新的数据库会话 |
|||
|
|||
首先,为新建数据库创建新的数据库会话。 |
|||
|
|||
测试时,使用 `test.db` 替代 `sql_app.db`。 |
|||
|
|||
但其余的会话代码基本上都是一样的,只要复制就可以了。 |
|||
|
|||
```Python hl_lines="8-13" |
|||
{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
为减少代码重复,最好把这段代码写成函数,在 `database.py` 与 `tests/test_sql_app.py`中使用。 |
|||
|
|||
为了把注意力集中在测试代码上,本例只是复制了这段代码。 |
|||
|
|||
## 创建数据库 |
|||
|
|||
因为现在是想在新文件中使用新数据库,所以要使用以下代码创建数据库: |
|||
|
|||
```Python |
|||
Base.metadata.create_all(bind=engine) |
|||
``` |
|||
|
|||
一般是在 `main.py` 中调用这行代码,但在 `main.py` 里,这行代码用于创建 `sql_app.db`,但是现在要为测试创建 `test.db`。 |
|||
|
|||
因此,要在测试代码中添加这行代码创建新的数据库文件。 |
|||
|
|||
```Python hl_lines="16" |
|||
{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} |
|||
``` |
|||
|
|||
## 覆盖依赖项 |
|||
|
|||
接下来,创建覆盖依赖项,并为应用添加覆盖内容。 |
|||
|
|||
```Python hl_lines="19-24 27" |
|||
{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
`overrider_get_db()` 与 `get_db` 的代码几乎完全一样,只是 `overrider_get_db` 中使用测试数据库的 `TestingSessionLocal`。 |
|||
|
|||
## 测试应用 |
|||
|
|||
然后,就可以正常测试了。 |
|||
|
|||
```Python hl_lines="32-47" |
|||
{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} |
|||
``` |
|||
|
|||
测试期间,所有在数据库中所做的修改都在 `test.db` 里,不会影响主应用的 `sql_app.db`。 |
@ -0,0 +1,7 @@ |
|||
# 测试事件:启动 - 关闭 |
|||
|
|||
使用 `TestClient` 和 `with` 语句,在测试中运行事件处理器(`startup` 与 `shutdown`)。 |
|||
|
|||
```Python hl_lines="9-12 20-24" |
|||
{!../../../docs_src/app_testing/tutorial003.py!} |
|||
``` |
@ -0,0 +1,13 @@ |
|||
# 测试 WebSockets |
|||
|
|||
测试 WebSockets 也使用 `TestClient`。 |
|||
|
|||
为此,要在 `with` 语句中使用 `TestClient` 连接 WebSocket。 |
|||
|
|||
```Python hl_lines="27-31" |
|||
{!../../../docs_src/app_testing/tutorial002.py!} |
|||
``` |
|||
|
|||
!!! note "笔记" |
|||
|
|||
更多细节详见 <a href="https://www.starlette.io/testclient/#testing-websocket-sessions" class="external-link" target="_blank">Starlette 官档 - 测试 WebSockets</a>。 |
@ -0,0 +1,323 @@ |
|||
# 部署概念 |
|||
|
|||
在部署 **FastAPI** 应用程序或任何类型的 Web API 时,有几个概念值得了解,通过掌握这些概念您可以找到**最合适的**方法来**部署您的应用程序**。 |
|||
|
|||
一些重要的概念是: |
|||
|
|||
* 安全性 - HTTPS |
|||
* 启动时运行 |
|||
* 重新启动 |
|||
* 复制(运行的进程数) |
|||
* 内存 |
|||
* 开始前的先前步骤 |
|||
|
|||
我们接下来了解它们将如何影响**部署**。 |
|||
|
|||
我们的最终目标是能够以**安全**的方式**为您的 API 客户端**提供服务,同时要**避免中断**,并且尽可能高效地利用**计算资源**( 例如服务器CPU资源)。 🚀 |
|||
|
|||
我将在这里告诉您更多关于这些**概念**的信息,希望能给您提供**直觉**来决定如何在非常不同的环境中部署 API,甚至在是尚不存在的**未来**的环境里。 |
|||
|
|||
通过考虑这些概念,您将能够**评估和设计**部署**您自己的 API**的最佳方式。 |
|||
|
|||
在接下来的章节中,我将为您提供更多部署 FastAPI 应用程序的**具体方法**。 |
|||
|
|||
但现在,让我们仔细看一下这些重要的**概念**。 这些概念也适用于任何其他类型的 Web API。 💡 |
|||
|
|||
## 安全性 - HTTPS |
|||
|
|||
在[上一章有关 HTTPS](./https.md){.internal-link target=_blank} 中,我们了解了 HTTPS 如何为您的 API 提供加密。 |
|||
|
|||
我们还看到,HTTPS 通常由应用程序服务器的**外部**组件(**TLS 终止代理**)提供。 |
|||
|
|||
并且必须有某个东西负责**更新 HTTPS 证书**,它可以是相同的组件,也可以是不同的组件。 |
|||
|
|||
|
|||
### HTTPS 示例工具 |
|||
|
|||
您可以用作 TLS 终止代理的一些工具包括: |
|||
|
|||
* Traefik |
|||
* 自动处理证书更新 ✨ |
|||
* Caddy |
|||
* 自动处理证书更新 ✨ |
|||
* Nginx |
|||
* 使用 Certbot 等外部组件进行证书更新 |
|||
* HAProxy |
|||
* 使用 Certbot 等外部组件进行证书更新 |
|||
* 带有 Ingress Controller(如Nginx) 的 Kubernetes |
|||
* 使用诸如 cert-manager 之类的外部组件来进行证书更新 |
|||
* 由云服务商内部处理,作为其服务的一部分(请阅读下文👇) |
|||
|
|||
另一种选择是您可以使用**云服务**来完成更多工作,包括设置 HTTPS。 它可能有一些限制或向您收取更多费用等。但在这种情况下,您不必自己设置 TLS 终止代理。 |
|||
|
|||
我将在接下来的章节中向您展示一些具体示例。 |
|||
|
|||
--- |
|||
|
|||
接下来要考虑的概念都是关于运行实际 API 的程序(例如 Uvicorn)。 |
|||
|
|||
## 程序和进程 |
|||
|
|||
我们将讨论很多关于正在运行的“**进程**”的内容,因此弄清楚它的含义以及与“**程序**”这个词有什么区别是很有用的。 |
|||
|
|||
### 什么是程序 |
|||
|
|||
**程序**这个词通常用来描述很多东西: |
|||
|
|||
* 您编写的 **代码**,**Python 文件**。 |
|||
* 操作系统可以**执行**的**文件**,例如:`python`、`python.exe`或`uvicorn`。 |
|||
* 在操作系统上**运行**、使用CPU 并将内容存储在内存上的特定程序。 这也被称为**进程**。 |
|||
|
|||
### 什么是进程 |
|||
|
|||
**进程** 这个词通常以更具体的方式使用,仅指在操作系统中运行的东西(如上面的最后一点): |
|||
|
|||
* 在操作系统上**运行**的特定程序。 |
|||
* 这不是指文件,也不是指代码,它**具体**指的是操作系统正在**执行**和管理的东西。 |
|||
* 任何程序,任何代码,**只有在执行时才能做事**。 因此,是当有**进程正在运行**时。 |
|||
* 该进程可以由您或操作系统**终止**(或“杀死”)。 那时,它停止运行/被执行,并且它可以**不再做事情**。 |
|||
* 您计算机上运行的每个应用程序背后都有一些进程,每个正在运行的程序,每个窗口等。并且通常在计算机打开时**同时**运行许多进程。 |
|||
* **同一程序**可以有**多个进程**同时运行。 |
|||
|
|||
如果您检查操作系统中的“任务管理器”或“系统监视器”(或类似工具),您将能够看到许多正在运行的进程。 |
|||
|
|||
例如,您可能会看到有多个进程运行同一个浏览器程序(Firefox、Chrome、Edge 等)。 他们通常每个tab运行一个进程,再加上一些其他额外的进程。 |
|||
|
|||
<img class="shadow" src="/img/deployment/concepts/image01.png"> |
|||
|
|||
--- |
|||
|
|||
现在我们知道了术语“进程”和“程序”之间的区别,让我们继续讨论部署。 |
|||
|
|||
## 启动时运行 |
|||
|
|||
在大多数情况下,当您创建 Web API 时,您希望它**始终运行**、不间断,以便您的客户端始终可以访问它。 这是当然的,除非您有特定原因希望它仅在某些情况下运行,但大多数时候您希望它不断运行并且**可用**。 |
|||
|
|||
### 在远程服务器中 |
|||
|
|||
当您设置远程服务器(云服务器、虚拟机等)时,您可以做的最简单的事情就是手动运行 Uvicorn(或类似的),就像本地开发时一样。 |
|||
|
|||
它将会在**开发过程中**发挥作用并发挥作用。 |
|||
|
|||
但是,如果您与服务器的连接丢失,**正在运行的进程**可能会终止。 |
|||
|
|||
如果服务器重新启动(例如更新后或从云提供商迁移后),您可能**不会注意到它**。 因此,您甚至不知道必须手动重新启动该进程。 所以,你的 API 将一直处于挂掉的状态。 😱 |
|||
|
|||
|
|||
### 启动时自动运行 |
|||
|
|||
一般来说,您可能希望服务器程序(例如 Uvicorn)在服务器启动时自动启动,并且不需要任何**人为干预**,让进程始终与您的 API 一起运行(例如 Uvicorn 运行您的 FastAPI 应用程序) 。 |
|||
|
|||
### 单独的程序 |
|||
|
|||
为了实现这一点,您通常会有一个**单独的程序**来确保您的应用程序在启动时运行。 在许多情况下,它还可以确保其他组件或应用程序也运行,例如数据库。 |
|||
|
|||
### 启动时运行的示例工具 |
|||
|
|||
可以完成这项工作的工具的一些示例是: |
|||
|
|||
* Docker |
|||
* Kubernetes |
|||
* Docker Compose |
|||
* Docker in Swarm Mode |
|||
* Systemd |
|||
* Supervisor |
|||
* 作为其服务的一部分由云提供商内部处理 |
|||
* 其他的... |
|||
|
|||
我将在接下来的章节中为您提供更具体的示例。 |
|||
|
|||
|
|||
## 重新启动 |
|||
|
|||
与确保应用程序在启动时运行类似,您可能还想确保它在挂掉后**重新启动**。 |
|||
|
|||
### 我们会犯错误 |
|||
|
|||
作为人类,我们总是会犯**错误**。 软件几乎*总是*在不同的地方隐藏着**bug**。 🐛 |
|||
|
|||
作为开发人员,当我们发现这些bug并实现新功能(也可能添加新bug😅)时,我们会不断改进代码。 |
|||
|
|||
### 自动处理小错误 |
|||
|
|||
使用 FastAPI 构建 Web API 时,如果我们的代码中存在错误,FastAPI 通常会将其包含到触发错误的单个请求中。 🛡 |
|||
|
|||
对于该请求,客户端将收到 **500 内部服务器错误**,但应用程序将继续处理下一个请求,而不是完全崩溃。 |
|||
|
|||
### 更大的错误 - 崩溃 |
|||
|
|||
尽管如此,在某些情况下,我们编写的一些代码可能会导致整个应用程序崩溃,从而导致 Uvicorn 和 Python 崩溃。 💥 |
|||
|
|||
尽管如此,您可能不希望应用程序因为某个地方出现错误而保持死机状态,您可能希望它**继续运行**,至少对于未破坏的*路径操作*。 |
|||
|
|||
### 崩溃后重新启动 |
|||
|
|||
但在那些严重错误导致正在运行的**进程**崩溃的情况下,您需要一个外部组件来负责**重新启动**进程,至少尝试几次...... |
|||
|
|||
!!! tip |
|||
|
|||
...尽管如果整个应用程序只是**立即崩溃**,那么永远重新启动它可能没有意义。 但在这些情况下,您可能会在开发过程中注意到它,或者至少在部署后立即注意到它。 |
|||
|
|||
因此,让我们关注主要情况,在**未来**的某些特定情况下,它可能会完全崩溃,但重新启动它仍然有意义。 |
|||
|
|||
您可能希望让这个东西作为 **外部组件** 负责重新启动您的应用程序,因为到那时,使用 Uvicorn 和 Python 的同一应用程序已经崩溃了,因此同一应用程序的相同代码中没有东西可以对此做出什么。 |
|||
|
|||
### 自动重新启动的示例工具 |
|||
|
|||
在大多数情况下,用于**启动时运行程序**的同一工具也用于处理自动**重新启动**。 |
|||
|
|||
例如,可以通过以下方式处理: |
|||
|
|||
* Docker |
|||
* Kubernetes |
|||
* Docker Compose |
|||
* Docker in Swarm mode |
|||
* Systemd |
|||
* Supervisor |
|||
* 作为其服务的一部分由云提供商内部处理 |
|||
* 其他的... |
|||
|
|||
## 复制 - 进程和内存 |
|||
|
|||
对于 FastAPI 应用程序,使用像 Uvicorn 这样的服务器程序,在**一个进程**中运行一次就可以同时为多个客户端提供服务。 |
|||
|
|||
但在许多情况下,您会希望同时运行多个工作进程。 |
|||
|
|||
### 多进程 - Workers |
|||
|
|||
如果您的客户端数量多于单个进程可以处理的数量(例如,如果虚拟机不是太大),并且服务器的 CPU 中有 **多个核心**,那么您可以让 **多个进程** 运行 同时处理同一个应用程序,并在它们之间分发所有请求。 |
|||
|
|||
当您运行同一 API 程序的**多个进程**时,它们通常称为 **workers**。 |
|||
|
|||
### 工作进程和端口 |
|||
|
|||
还记得文档 [About HTTPS](./https.md){.internal-link target=_blank} 中只有一个进程可以侦听服务器中的端口和 IP 地址的一种组合吗? |
|||
|
|||
现在仍然是对的。 |
|||
|
|||
因此,为了能够同时拥有**多个进程**,必须有一个**单个进程侦听端口**,然后以某种方式将通信传输到每个工作进程。 |
|||
|
|||
### 每个进程的内存 |
|||
|
|||
现在,当程序将内容加载到内存中时,例如,将机器学习模型加载到变量中,或者将大文件的内容加载到变量中,所有这些都会消耗服务器的一点内存 (RAM) 。 |
|||
|
|||
多个进程通常**不共享任何内存**。 这意味着每个正在运行的进程都有自己的东西、变量和内存。 如果您的代码消耗了大量内存,**每个进程**将消耗等量的内存。 |
|||
|
|||
### 服务器内存 |
|||
|
|||
例如,如果您的代码加载 **1 GB 大小**的机器学习模型,则当您使用 API 运行一个进程时,它将至少消耗 1 GB RAM。 如果您启动 **4 个进程**(4 个工作进程),每个进程将消耗 1 GB RAM。 因此,您的 API 总共将消耗 **4 GB RAM**。 |
|||
|
|||
如果您的远程服务器或虚拟机只有 3 GB RAM,尝试加载超过 4 GB RAM 将导致问题。 🚨 |
|||
|
|||
|
|||
### 多进程 - 一个例子 |
|||
|
|||
在此示例中,有一个 **Manager Process** 启动并控制两个 **Worker Processes**。 |
|||
|
|||
该管理器进程可能是监听 IP 中的 **端口** 的进程。 它将所有通信传输到工作进程。 |
|||
|
|||
这些工作进程将是运行您的应用程序的进程,它们将执行主要计算以接收 **请求** 并返回 **响应**,并且它们将加载您放入 RAM 中的变量中的任何内容。 |
|||
|
|||
<img src="/img/deployment/concepts/process-ram.svg"> |
|||
|
|||
当然,除了您的应用程序之外,同一台机器可能还运行**其他进程**。 |
|||
|
|||
一个有趣的细节是,随着时间的推移,每个进程使用的 **CPU 百分比可能会发生很大变化,但内存 (RAM) 通常会或多或少保持稳定**。 |
|||
|
|||
如果您有一个每次执行相当数量的计算的 API,并且您有很多客户端,那么 **CPU 利用率** 可能也会保持稳定(而不是不断快速上升和下降)。 |
|||
|
|||
### 复制工具和策略示例 |
|||
|
|||
可以通过多种方法来实现这一目标,我将在接下来的章节中向您详细介绍具体策略,例如在谈论 Docker 和容器时。 |
|||
|
|||
要考虑的主要限制是必须有一个**单个**组件来处理**公共IP**中的**端口**。 然后它必须有一种方法将通信**传输**到复制的**进程/worker**。 |
|||
|
|||
以下是一些可能的组合和策略: |
|||
|
|||
* **Gunicorn** 管理 **Uvicorn workers** |
|||
* Gunicorn 将是监听 **IP** 和 **端口** 的 **进程管理器**,复制将通过 **多个 Uvicorn 工作进程** 进行 |
|||
* **Uvicorn** 管理 **Uvicorn workers** |
|||
* 一个 Uvicorn **进程管理器** 将监听 **IP** 和 **端口**,并且它将启动 **多个 Uvicorn 工作进程** |
|||
* **Kubernetes** 和其他分布式 **容器系统** |
|||
* **Kubernetes** 层中的某些东西将侦听 **IP** 和 **端口**。 复制将通过拥有**多个容器**,每个容器运行**一个 Uvicorn 进程** |
|||
* **云服务** 为您处理此问题 |
|||
* 云服务可能**为您处理复制**。 它可能会让您定义 **要运行的进程**,或要使用的 **容器映像**,在任何情况下,它很可能是 **单个 Uvicorn 进程**,并且云服务将负责复制它。 |
|||
|
|||
|
|||
|
|||
!!! tip |
|||
|
|||
如果这些关于 **容器**、Docker 或 Kubernetes 的内容还没有多大意义,请不要担心。 |
|||
|
|||
我将在以后的章节中向您详细介绍容器镜像、Docker、Kubernetes 等:[容器中的 FastAPI - Docker](./docker.md){.internal-link target=_blank}。 |
|||
|
|||
## 启动之前的步骤 |
|||
|
|||
在很多情况下,您希望在**启动**应用程序之前执行一些步骤。 |
|||
|
|||
例如,您可能想要运行**数据库迁移**。 |
|||
|
|||
但在大多数情况下,您只想执行这些步骤**一次**。 |
|||
|
|||
因此,在启动应用程序之前,您将需要一个**单个进程**来执行这些**前面的步骤**。 |
|||
|
|||
而且您必须确保它是运行前面步骤的单个进程, *即使*之后您为应用程序本身启动**多个进程**(多个worker)。 如果这些步骤由**多个进程**运行,它们会通过在**并行**运行来**重复**工作,并且如果这些步骤像数据库迁移一样需要小心处理,它们可能会导致每个进程和其他进程发生冲突。 |
|||
|
|||
当然,也有一些情况,多次运行前面的步骤也没有问题,这样的话就好办多了。 |
|||
|
|||
!!! tip |
|||
|
|||
另外,请记住,根据您的设置,在某些情况下,您在开始应用程序之前**可能甚至不需要任何先前的步骤**。 |
|||
|
|||
在这种情况下,您就不必担心这些。 🤷 |
|||
|
|||
|
|||
### 前面步骤策略的示例 |
|||
|
|||
这将在**很大程度上取决于您部署系统的方式**,并且可能与您启动程序、处理重启等的方式有关。 |
|||
|
|||
以下是一些可能的想法: |
|||
|
|||
* Kubernetes 中的“Init Container”在应用程序容器之前运行 |
|||
* 一个 bash 脚本,运行前面的步骤,然后启动您的应用程序 |
|||
* 您仍然需要一种方法来启动/重新启动 bash 脚本、检测错误等。 |
|||
|
|||
!!! tip |
|||
|
|||
我将在以后的章节中为您提供使用容器执行此操作的更具体示例:[容器中的 FastAPI - Docker](./docker.md){.internal-link target=_blank}。 |
|||
|
|||
## 资源利用率 |
|||
|
|||
您的服务器是一个**资源**,您可以通过您的程序消耗或**利用**CPU 上的计算时间以及可用的 RAM 内存。 |
|||
|
|||
您想要消耗/利用多少系统资源? 您可能很容易认为“不多”,但实际上,您可能希望在不崩溃的情况下**尽可能多地消耗**。 |
|||
|
|||
如果您支付了 3 台服务器的费用,但只使用了它们的一点点 RAM 和 CPU,那么您可能**浪费金钱** 💸,并且可能 **浪费服务器电力** 🌎,等等。 |
|||
|
|||
在这种情况下,最好只拥有 2 台服务器并使用更高比例的资源(CPU、内存、磁盘、网络带宽等)。 |
|||
|
|||
另一方面,如果您有 2 台服务器,并且正在使用 **100% 的 CPU 和 RAM**,则在某些时候,一个进程会要求更多内存,并且服务器将不得不使用磁盘作为“内存” (这可能会慢数千倍),甚至**崩溃**。 或者一个进程可能需要执行一些计算,并且必须等到 CPU 再次空闲。 |
|||
|
|||
在这种情况下,最好购买**一台额外的服务器**并在其上运行一些进程,以便它们都有**足够的 RAM 和 CPU 时间**。 |
|||
|
|||
由于某种原因,您的 API 的使用量也有可能出现**激增**。 也许它像病毒一样传播开来,或者也许其他一些服务或机器人开始使用它。 在这些情况下,您可能需要额外的资源来保证安全。 |
|||
|
|||
您可以将一个**任意数字**设置为目标,例如,资源利用率**在 50% 到 90%** 之间。 重点是,这些可能是您想要衡量和用来调整部署的主要内容。 |
|||
|
|||
您可以使用“htop”等简单工具来查看服务器中使用的 CPU 和 RAM 或每个进程使用的数量。 或者您可以使用更复杂的监控工具,这些工具可能分布在服务器等上。 |
|||
|
|||
|
|||
## 回顾 |
|||
|
|||
您在这里阅读了一些在决定如何部署应用程序时可能需要牢记的主要概念: |
|||
|
|||
* 安全性 - HTTPS |
|||
* 启动时运行 |
|||
* 重新启动 |
|||
* 复制(运行的进程数) |
|||
* 内存 |
|||
* 开始前的先前步骤 |
|||
|
|||
了解这些想法以及如何应用它们应该会给您足够的直觉在配置和调整部署时做出任何决定。 🤓 |
|||
|
|||
在接下来的部分中,我将为您提供更具体的示例,说明您可以遵循的可能策略。 🚀 |
@ -0,0 +1,244 @@ |
|||
# 在 Deta 上部署 FastAPI |
|||
|
|||
本节介绍如何使用 <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> 免费方案部署 **FastAPI** 应用。🎁 |
|||
|
|||
部署操作需要大约 10 分钟。 |
|||
|
|||
!!! info "说明" |
|||
|
|||
<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> 是 **FastAPI** 的赞助商。 🎉 |
|||
|
|||
## 基础 **FastAPI** 应用 |
|||
|
|||
* 创建应用文件夹,例如 `./fastapideta/`,进入文件夹 |
|||
|
|||
### FastAPI 代码 |
|||
|
|||
* 创建包含如下代码的 `main.py`: |
|||
|
|||
```Python |
|||
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): |
|||
return {"item_id": item_id} |
|||
``` |
|||
|
|||
### 需求项 |
|||
|
|||
在文件夹里新建包含如下内容的 `requirements.txt` 文件: |
|||
|
|||
```text |
|||
fastapi |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
在 Deta 上部署时无需安装 Uvicorn,虽然在本地测试应用时需要安装。 |
|||
|
|||
### 文件夹架构 |
|||
|
|||
`./fastapideta/` 文件夹中现在有两个文件: |
|||
|
|||
``` |
|||
. |
|||
└── main.py |
|||
└── requirements.txt |
|||
``` |
|||
|
|||
## 创建免费 Deta 账号 |
|||
|
|||
创建<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">免费的 Deta 账号</a>,只需要电子邮件和密码。 |
|||
|
|||
甚至不需要信用卡。 |
|||
|
|||
## 安装 CLI |
|||
|
|||
创建账号后,安装 Deta <abbr title="Command Line Interface application">CLI</abbr>: |
|||
|
|||
=== "Linux, macOS" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ curl -fsSL https://get.deta.dev/cli.sh | sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ iwr https://get.deta.dev/cli.ps1 -useb | iex |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
安装完 CLI 后,打开新的 Terminal,就能检测到刚安装的 CLI。 |
|||
|
|||
在新的 Terminal 里,用以下命令确认 CLI 是否正确安装: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta --help |
|||
|
|||
Deta command line interface for managing deta micros. |
|||
Complete documentation available at https://docs.deta.sh |
|||
|
|||
Usage: |
|||
deta [flags] |
|||
deta [command] |
|||
|
|||
Available Commands: |
|||
auth Change auth settings for a deta micro |
|||
|
|||
... |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip "提示" |
|||
|
|||
安装 CLI 遇到问题时,请参阅 <a href="https://docs.deta.sh/docs/micros/getting_started?ref=fastapi" class="external-link" target="_blank">Deta 官档</a>。 |
|||
|
|||
## 使用 CLI 登录 |
|||
|
|||
现在,使用 CLI 登录 Deta: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta login |
|||
|
|||
Please, log in from the web page. Waiting.. |
|||
Logged in successfully. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
这个命令会打开浏览器并自动验证身份。 |
|||
|
|||
## 使用 Deta 部署 |
|||
|
|||
接下来,使用 Deta CLI 部署应用: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta new |
|||
|
|||
Successfully created a new micro |
|||
|
|||
// Notice the "endpoint" 🔍 |
|||
|
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
|
|||
Adding dependencies... |
|||
|
|||
|
|||
---> 100% |
|||
|
|||
|
|||
Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
您会看到如下 JSON 信息: |
|||
|
|||
```JSON hl_lines="4" |
|||
{ |
|||
"name": "fastapideta", |
|||
"runtime": "python3.7", |
|||
"endpoint": "https://qltnci.deta.dev", |
|||
"visor": "enabled", |
|||
"http_auth": "enabled" |
|||
} |
|||
``` |
|||
|
|||
!!! tip "提示" |
|||
|
|||
您部署时的 `"endpoint"` URL 可能会有所不同。 |
|||
|
|||
## 查看效果 |
|||
|
|||
打开浏览器,跳转到 `endpoint` URL。本例中是 `https://qltnci.deta.dev`,但您的链接可能与此不同。 |
|||
|
|||
FastAPI 应用会返回如下 JSON 响应: |
|||
|
|||
```JSON |
|||
{ |
|||
"Hello": "World" |
|||
} |
|||
``` |
|||
|
|||
接下来,跳转到 API 文档 `/docs`,本例中是 `https://qltnci.deta.dev/docs`。 |
|||
|
|||
文档显示如下: |
|||
|
|||
<img src="/img/deployment/deta/image01.png"> |
|||
|
|||
## 启用公开访问 |
|||
|
|||
默认情况下,Deta 使用您的账号 Cookies 处理身份验证。 |
|||
|
|||
应用一切就绪之后,使用如下命令让公众也能看到您的应用: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deta auth disable |
|||
|
|||
Successfully disabled http auth |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
现在,就可以把 URL 分享给大家,他们就能访问您的 API 了。🚀 |
|||
|
|||
## HTTPS |
|||
|
|||
恭喜!您已经在 Deta 上部署了 FastAPI 应用!🎉 🍰 |
|||
|
|||
还要注意,Deta 能够正确处理 HTTPS,因此您不必操心 HTTPS,您的客户端肯定能有安全加密的连接。 ✅ 🔒 |
|||
|
|||
## 查看 Visor |
|||
|
|||
从 API 文档(URL 是 `https://gltnci.deta.dev/docs`)发送请求至*路径操作* `/items/{item_id}`。 |
|||
|
|||
例如,ID `5`。 |
|||
|
|||
现在跳转至 <a href="https://web.deta.sh/" class="external-link" target="_blank">https://web.deta.sh。</a> |
|||
|
|||
左边栏有个 <abbr title="it comes from Micro(server)">"Micros"</abbr> 标签,里面是所有的应用。 |
|||
|
|||
还有一个 **Details** 和 **Visor** 标签,跳转到 **Visor** 标签。 |
|||
|
|||
在这里查看最近发送给应用的请求。 |
|||
|
|||
您可以编辑或重新使用这些请求。 |
|||
|
|||
<img src="/img/deployment/deta/image02.png"> |
|||
|
|||
## 更多内容 |
|||
|
|||
如果要持久化保存应用数据,可以使用提供了**免费方案**的 <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">Deta Base</a>。 |
|||
|
|||
详见 <a href="https://docs.deta.sh?ref=fastapi" class="external-link" target="_blank">Deta 官档</a>。 |
@ -0,0 +1,728 @@ |
|||
# 容器中的 FastAPI - Docker |
|||
|
|||
部署 FastAPI 应用程序时,常见的方法是构建 **Linux 容器镜像**。 通常使用 <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> 完成。 然后,你可以通过几种可能的方式之一部署该容器镜像。 |
|||
|
|||
使用 Linux 容器有几个优点,包括**安全性**、**可复制性**、**简单性**等。 |
|||
|
|||
!!! tip |
|||
赶时间并且已经知道这些东西了? 跳转到下面的 [`Dockerfile` 👇](#为-fastapi-构建-docker-镜像)。 |
|||
|
|||
|
|||
<details> |
|||
<summary>Dockerfile Preview 👀</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> |
|||
|
|||
## 什么是容器 |
|||
|
|||
容器(主要是 Linux 容器)是一种非常**轻量级**的打包应用程序的方式,其包括所有依赖项和必要的文件,同时它们可以和同一系统中的其他容器(或者其他应用程序/组件)相互隔离。 |
|||
|
|||
Linux 容器使用宿主机(如物理服务器、虚拟机、云服务器等)的Linux 内核运行。 这意味着它们非常轻量(与模拟整个操作系统的完整虚拟机相比)。 |
|||
|
|||
通过这样的方式,容器消耗**很少的资源**,与直接运行进程相当(虚拟机会消耗更多)。 |
|||
|
|||
容器的进程(通常只有一个)、文件系统和网络都运行在隔离的环境,这简化了部署、安全、开发等。 |
|||
|
|||
## 什么是容器镜像 |
|||
|
|||
**容器**是从**容器镜像**运行的。 |
|||
|
|||
容器镜像是容器中文件、环境变量和默认命令/程序的**静态**版本。 **静态**这里的意思是容器**镜像**还没有运行,只是打包的文件和元数据。 |
|||
|
|||
与存储静态内容的“**容器镜像**”相反,“**容器**”通常指正在运行的实例,即正在**执行的**。 |
|||
|
|||
当**容器**启动并运行时(从**容器镜像**启动),它可以创建或更改文件、环境变量等。这些更改将仅存在于该容器中,而不会持久化到底层的容器镜像中(不会保存到磁盘)。 |
|||
|
|||
容器镜像相当于**程序**和文件,例如 `python`命令 和某些文件 如`main.py`。 |
|||
|
|||
而**容器**本身(与**容器镜像**相反)是镜像的实际运行实例,相当于**进程**。 事实上,容器仅在有**进程运行**时才运行(通常它只是一个单独的进程)。 当容器中没有进程运行时,容器就会停止。 |
|||
|
|||
|
|||
|
|||
## 容器镜像 |
|||
|
|||
Docker 一直是创建和管理**容器镜像**和**容器**的主要工具之一。 |
|||
|
|||
还有一个公共 <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a> ,其中包含预制的 **官方容器镜像**, 适用于许多工具、环境、数据库和应用程序。 |
|||
|
|||
例如,有一个官方的 <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Python 镜像</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>, etc. |
|||
|
|||
|
|||
通过使用预制的容器镜像,可以非常轻松地**组合**并使用不同的工具。 例如,尝试一个新的数据库。 在大多数情况下,你可以使用**官方镜像**,只需为其配置环境变量即可。 |
|||
|
|||
这样,在许多情况下,你可以了解容器和 Docker,并通过许多不同的工具和组件重复使用这些知识。 |
|||
|
|||
因此,你可以运行带有不同内容的**多个容器**,例如数据库、Python 应用程序、带有 React 前端应用程序的 Web 服务器,并通过内部网络将它们连接在一起。 |
|||
|
|||
所有容器管理系统(如 Docker 或 Kubernetes)都集成了这些网络功能。 |
|||
|
|||
## 容器和进程 |
|||
|
|||
**容器镜像**通常在其元数据中包含启动**容器**时应运行的默认程序或命令以及要传递给该程序的参数。 与在命令行中的情况非常相似。 |
|||
|
|||
当 **容器** 启动时,它将运行该命令/程序(尽管你可以覆盖它并使其运行不同的命令/程序)。 |
|||
|
|||
只要**主进程**(命令或程序)在运行,容器就在运行。 |
|||
|
|||
容器通常有一个**单个进程**,但也可以从主进程启动子进程,这样你就可以在同一个容器中拥有**多个进程**。 |
|||
|
|||
但是,如果没有**至少一个正在运行的进程**,就不可能有一个正在运行的容器。 如果主进程停止,容器也会停止。 |
|||
|
|||
|
|||
## 为 FastAPI 构建 Docker 镜像 |
|||
|
|||
好吧,让我们现在构建一些东西! 🚀 |
|||
|
|||
我将向你展示如何基于 **官方 Python** 镜像 **从头开始** 为 FastAPI 构建 **Docker 镜像**。 |
|||
|
|||
这是你在**大多数情况**下想要做的,例如: |
|||
|
|||
* 使用 **Kubernetes** 或类似工具 |
|||
* 在 **Raspberry Pi** 上运行时 |
|||
* 使用可为你运行容器镜像的云服务等。 |
|||
|
|||
### 依赖项 |
|||
|
|||
你通常会在某个文件中包含应用程序的**依赖项**。 |
|||
|
|||
具体做法取决于你**安装**这些依赖时所使用的工具。 |
|||
|
|||
最常见的方法是创建一个`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> |
|||
|
|||
!!! info |
|||
还有其他文件格式和工具来定义和安装依赖项。 |
|||
|
|||
我将在下面的部分中向你展示一个使用 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 |
|||
|
|||
现在在相同的project目录创建一个名为`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. 从官方Python基础镜像开始。 |
|||
|
|||
2. 将当前工作目录设置为`/code`。 |
|||
|
|||
这是我们放置`requirements.txt`文件和`app`目录的位置。 |
|||
|
|||
3. 将符合要求的文件复制到`/code`目录中。 |
|||
|
|||
首先仅复制requirements.txt文件,而不复制其余代码。 |
|||
|
|||
由于此文件**不经常更改**,Docker 将检测到它并在这一步中使用**缓存**,从而为下一步启用缓存。 |
|||
|
|||
4. 安装需求文件中的包依赖项。 |
|||
|
|||
`--no-cache-dir` 选项告诉 `pip` 不要在本地保存下载的包,因为只有当 `pip` 再次运行以安装相同的包时才会这样,但在与容器一起工作时情况并非如此。 |
|||
|
|||
!!! 笔记 |
|||
`--no-cache-dir` 仅与 `pip` 相关,与 Docker 或容器无关。 |
|||
|
|||
`--upgrade` 选项告诉 `pip` 升级软件包(如果已经安装)。 |
|||
|
|||
因为上一步复制文件可以被 **Docker 缓存** 检测到,所以此步骤也将 **使用 Docker 缓存**(如果可用)。 |
|||
|
|||
在开发过程中一次又一次构建镜像时,在此步骤中使用缓存将为你节省大量**时间**,而不是**每次**都**下载和安装**所有依赖项。 |
|||
|
|||
|
|||
5. 将“./app”目录复制到“/code”目录中。 |
|||
|
|||
由于其中包含**更改最频繁**的所有代码,因此 Docker **缓存**不会轻易用于此操作或任何**后续步骤**。 |
|||
|
|||
因此,将其放在`Dockerfile`**接近最后**的位置非常重要,以优化容器镜像的构建时间。 |
|||
|
|||
6. 设置**命令**来运行 `uvicorn` 服务器。 |
|||
|
|||
`CMD` 接受一个字符串列表,每个字符串都是你在命令行中输入的内容,并用空格分隔。 |
|||
|
|||
该命令将从 **当前工作目录** 运行,即你上面使用`WORKDIR /code`设置的同一`/code`目录。 |
|||
|
|||
因为程序将从`/code`启动,并且其中包含你的代码的目录`./app`,所以**Uvicorn**将能够从`app.main`中查看并**import**`app`。 |
|||
|
|||
!!! tip |
|||
通过单击代码中的每个数字气泡来查看每行的作用。 👆 |
|||
|
|||
你现在应该具有如下目录结构: |
|||
``` |
|||
. |
|||
├── 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"] |
|||
``` |
|||
|
|||
#### Docker 缓存 |
|||
|
|||
这个`Dockerfile`中有一个重要的技巧,我们首先只单独复制**包含依赖项的文件**,而不是其余代码。 让我来告诉你这是为什么。 |
|||
|
|||
```Dockerfile |
|||
COPY ./requirements.txt /code/requirements.txt |
|||
``` |
|||
|
|||
Docker之类的构建工具是通过**增量**的方式来构建这些容器镜像的。具体做法是从`Dockerfile`顶部开始,每一条指令生成的文件都是镜像的“一层”,同过把这些“层”一层一层地叠加到基础镜像上,最后我们就得到了最终的镜像。 |
|||
|
|||
Docker 和类似工具在构建镜像时也会使用**内部缓存**,如果自上次构建容器镜像以来文件没有更改,那么它将**重新使用上次创建的同一层**,而不是再次复制文件并从头开始创建新层。 |
|||
|
|||
仅仅避免文件的复制不一定会有太多速度提升,但是如果在这一步使用了缓存,那么才可以**在下一步中使用缓存**。 例如,可以使用安装依赖项那条指令的缓存: |
|||
|
|||
```Dockerfile |
|||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt |
|||
``` |
|||
|
|||
|
|||
包含包依赖项的文件**不会频繁更改**。 只复制该文件(不复制其他的应用代码),Docker 才能在这一步**使用缓存**。 |
|||
|
|||
Docker 进而能**使用缓存进行下一步**,即下载并安装这些依赖项。 这才是我们**节省大量时间**的地方。 ✨ ...可以避免无聊的等待。 😪😆 |
|||
|
|||
下载和安装依赖项**可能需要几分钟**,但使用**缓存**最多**只需要几秒钟**。 |
|||
|
|||
由于你在开发过程中会一次又一次地构建容器镜像以检查代码更改是否有效,因此可以累计节省大量时间。 |
|||
|
|||
在`Dockerfile`末尾附近,我们再添加复制代码的指令。 由于代码是**更改最频繁的**,所以将其放在最后,因为这一步之后的内容基本上都是无法使用缓存的。 |
|||
|
|||
```Dockerfile |
|||
COPY ./app /code/app |
|||
``` |
|||
|
|||
### 构建 Docker 镜像 |
|||
|
|||
现在所有文件都已就位,让我们构建容器镜像。 |
|||
|
|||
* 转到项目目录(在`Dockerfile`所在的位置,包含`app`目录)。 |
|||
* 构建你的 FastAPI 镜像: |
|||
|
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ docker build -t myimage . |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
|
|||
!!! tip |
|||
注意最后的 `.`,它相当于`./`,它告诉 Docker 用于构建容器镜像的目录。 |
|||
|
|||
在本例中,它是相同的当前目录(`.`)。 |
|||
|
|||
### 启动 Docker 容器 |
|||
|
|||
* 根据你的镜像运行容器: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ docker run -d --name mycontainer -p 80:80 myimage |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## 检查一下 |
|||
|
|||
|
|||
你应该能在Docker容器的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> (或其他等价的,使用 Docker 主机). |
|||
|
|||
你会看到类似内容: |
|||
|
|||
```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> (或其他等价的,使用 Docker 主机)。 |
|||
|
|||
你将看到自动交互式 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> (或其他等价的,使用 Docker 主机)。 |
|||
|
|||
你将看到备选的自动文档(由 <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> 提供): |
|||
|
|||
 |
|||
|
|||
## 使用单文件 FastAPI 构建 Docker 镜像 |
|||
|
|||
如果你的 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 并告诉它从 `main` 导入 `app` 对象(而不是从 `app.main` 导入)。 |
|||
|
|||
然后调整Uvicorn命令使用新模块`main`而不是`app.main`来导入FastAPI 实例`app`。 |
|||
|
|||
## 部署概念 |
|||
|
|||
我们再谈谈容器方面的一些相同的[部署概念](./concepts.md){.internal-link target=_blank}。 |
|||
|
|||
容器主要是一种简化**构建和部署**应用程序的过程的工具,但它们并不强制执行特定的方法来处理这些**部署概念**,并且有几种可能的策略。 |
|||
|
|||
**好消息**是,对于每种不同的策略,都有一种方法可以涵盖所有部署概念。 🎉 |
|||
|
|||
让我们从容器的角度回顾一下这些**部署概念**: |
|||
|
|||
* HTTPS |
|||
* 启动时运行 |
|||
* 重新启动 |
|||
* 复制(运行的进程数) |
|||
* 内存 |
|||
* 开始前的先前步骤 |
|||
|
|||
|
|||
## HTTPS |
|||
|
|||
如果我们只关注 FastAPI 应用程序的 **容器镜像**(以及稍后运行的 **容器**),HTTPS 通常会由另一个工具在 **外部** 处理。 |
|||
|
|||
它可以是另一个容器,例如使用 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>,处理 **HTTPS** 和 **自动**获取**证书**。 |
|||
|
|||
!!! tip |
|||
Traefik可以与 Docker、Kubernetes 等集成,因此使用它为容器设置和配置 HTTPS 非常容易。 |
|||
|
|||
或者,HTTPS 可以由云服务商作为其服务之一进行处理(同时仍在容器中运行应用程序)。 |
|||
|
|||
## 在启动和重新启动时运行 |
|||
|
|||
通常还有另一个工具负责**启动和运行**你的容器。 |
|||
|
|||
它可以直接是**Docker**, 或者**Docker Compose**、**Kubernetes**、**云服务**等。 |
|||
|
|||
在大多数(或所有)情况下,有一个简单的选项可以在启动时运行容器并在失败时重新启动。 例如,在 Docker 中,它是命令行选项 `--restart`。 |
|||
|
|||
如果不使用容器,让应用程序在启动时运行并重新启动可能会很麻烦且困难。 但在大多数情况下,当**使用容器**时,默认情况下会包含该功能。 ✨ |
|||
|
|||
## 复制 - 进程数 |
|||
|
|||
如果你有一个 <abbr title="一组配置为以某种方式连接并协同工作的计算机。">集群</abbr>, 比如 **Kubernetes**、Docker Swarm、Nomad 或其他类似的复杂系统来管理多台机器上的分布式容器,那么你可能希望在**集群级别**处理复制**,而不是在每个容器中使用**进程管理器**(如带有Worker的 Gunicorn) 。 |
|||
|
|||
像 Kubernetes 这样的分布式容器管理系统通常有一些集成的方法来处理**容器的复制**,同时仍然支持传入请求的**负载均衡**。 全部都在**集群级别**。 |
|||
|
|||
在这些情况下,你可能希望从头开始构建一个 **Docker 镜像**,如[上面所解释](#dockerfile)的那样,安装依赖项并运行 **单个 Uvicorn 进程**,而不是运行 Gunicorn 和 Uvicorn workers这种。 |
|||
|
|||
|
|||
### 负载均衡器 |
|||
|
|||
使用容器时,通常会有一些组件**监听主端口**。 它可能是处理 **HTTPS** 的 **TLS 终止代理** 或一些类似的工具的另一个容器。 |
|||
|
|||
由于该组件将接受请求的**负载**并(希望)以**平衡**的方式在worker之间分配该请求,因此它通常也称为**负载均衡器**。 |
|||
|
|||
!!! tip |
|||
用于 HTTPS **TLS 终止代理** 的相同组件也可能是 **负载均衡器**。 |
|||
|
|||
当使用容器时,你用来启动和管理容器的同一系统已经具有内部工具来传输来自该**负载均衡器**(也可以是**TLS 终止代理**) 的**网络通信**(例如HTTP请求)到你的应用程序容器。 |
|||
|
|||
### 一个负载均衡器 - 多个worker容器 |
|||
|
|||
当使用 **Kubernetes** 或类似的分布式容器管理系统时,使用其内部网络机制将允许单个在主 **端口** 上侦听的 **负载均衡器** 将通信(请求)传输到可能的 **多个** 运行你应用程序的容器。 |
|||
|
|||
运行你的应用程序的每个容器通常**只有一个进程**(例如,运行 FastAPI 应用程序的 Uvicorn 进程)。 它们都是**相同的容器**,运行相同的东西,但每个容器都有自己的进程、内存等。这样你就可以在 CPU 的**不同核心**, 甚至在**不同的机器**充分利用**并行化(parallelization)**。 |
|||
|
|||
具有**负载均衡器**的分布式容器系统将**将请求轮流分配**给你的应用程序的每个容器。 因此,每个请求都可以由运行你的应用程序的多个**复制容器**之一来处理。 |
|||
|
|||
通常,这个**负载均衡器**能够处理发送到集群中的*其他*应用程序的请求(例如发送到不同的域,或在不同的 URL 路径前缀下),并正确地将该通信传输到在集群中运行的*其他*应用程序的对应容器。 |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
### 每个容器一个进程 |
|||
|
|||
在这种类型的场景中,你可能希望**每个容器有一个(Uvicorn)进程**,因为你已经在集群级别处理复制。 |
|||
|
|||
因此,在这种情况下,你**不会**希望拥有像 Gunicorn 和 Uvicorn worker一样的进程管理器,或者 Uvicorn 使用自己的 Uvicorn worker。 你可能希望每个容器(但可能有多个容器)只有一个**单独的 Uvicorn 进程**。 |
|||
|
|||
在容器内拥有另一个进程管理器(就像使用 Gunicorn 或 Uvicorn 管理 Uvicorn 工作线程一样)只会增加**不必要的复杂性**,而你很可能已经在集群系统中处理这些复杂性了。 |
|||
|
|||
### 具有多个进程的容器 |
|||
|
|||
当然,在某些**特殊情况**,你可能希望拥有 **一个容器**,其中包含 **Gunicorn 进程管理器**,并在其中启动多个 **Uvicorn worker进程**。 |
|||
|
|||
在这些情况下,你可以使用 **官方 Docker 镜像**,其中包含 **Gunicorn** 作为运行多个 **Uvicorn 工作进程** 的进程管理器,以及一些默认设置来根据当前情况调整工作进程数量 自动CPU核心。 我将在下面的 [Gunicorn - Uvicorn 官方 Docker 镜像](#official-docker-image-with-gunicorn-uvicorn) 中告诉你更多相关信息。 |
|||
|
|||
下面一些什么时候这种做法有意义的示例: |
|||
|
|||
|
|||
#### 一个简单的应用程序 |
|||
|
|||
如果你的应用程序**足够简单**,你不需要(至少现在不需要)过多地微调进程数量,并且你可以使用自动默认值,那么你可能需要容器中的进程管理器 (使用官方 Docker 镜像),并且你在**单个服务器**而不是集群上运行它。 |
|||
|
|||
#### Docker Compose |
|||
|
|||
你可以使用 **Docker Compose** 部署到**单个服务器**(而不是集群),因此你没有一种简单的方法来管理容器的复制(使用 Docker Compose),同时保留共享网络和 **负载均衡**。 |
|||
|
|||
然后,你可能希望拥有一个**单个容器**,其中有一个**进程管理器**,在其中启动**多个worker进程**。 |
|||
|
|||
#### Prometheus和其他原因 |
|||
|
|||
你还可能有**其他原因**,这将使你更容易拥有一个带有**多个进程**的**单个容器**,而不是拥有每个容器中都有**单个进程**的**多个容器**。 |
|||
|
|||
例如(取决于你的设置)你可以在同一个容器中拥有一些工具,例如 Prometheus exporter,该工具应该有权访问**每个请求**。 |
|||
|
|||
在这种情况下,如果你有**多个容器**,默认情况下,当 Prometheus 来**读取metrics**时,它每次都会获取**单个容器**的metrics(对于处理该特定请求的容器),而不是获取所有复制容器的**累积metrics**。 |
|||
|
|||
在这种情况, 这种做法会更加简单:让**一个容器**具有**多个进程**,并在同一个容器上使用本地工具(例如 Prometheus exporter)收集所有内部进程的 Prometheus 指标并公开单个容器上的这些指标。 |
|||
|
|||
--- |
|||
|
|||
要点是,这些都**不是**你必须盲目遵循的**一成不变的规则**。 你可以根据这些思路**评估你自己的场景**并决定什么方法是最适合你的的系统,考虑如何管理以下概念: |
|||
|
|||
* 安全性 - HTTPS |
|||
* 启动时运行 |
|||
* 重新启动 |
|||
* 复制(运行的进程数) |
|||
* 内存 |
|||
* 开始前的先前步骤 |
|||
|
|||
## 内存 |
|||
|
|||
如果你**每个容器运行一个进程**,那么每个容器所消耗的内存或多或少是定义明确的、稳定的且有限的(如果它们是复制的,则不止一个)。 |
|||
|
|||
然后,你可以在容器管理系统的配置中设置相同的内存限制和要求(例如在 **Kubernetes** 中)。 这样,它将能够在**可用机器**中**复制容器**,同时考虑容器所需的内存量以及集群中机器中的可用内存量。 |
|||
|
|||
如果你的应用程序很**简单**,这可能**不是问题**,并且你可能不需要指定内存限制。 但是,如果你**使用大量内存**(例如使用**机器学习**模型),则应该检查你消耗了多少内存并调整**每台机器**中运行的**容器数量**(也许可以向集群添加更多机器)。 |
|||
|
|||
如果你**每个容器运行多个进程**(例如使用官方 Docker 镜像),你必须确保启动的进程数量不会消耗比可用内存**更多的内存**。 |
|||
|
|||
## 启动之前的步骤和容器 |
|||
|
|||
如果你使用容器(例如 Docker、Kubernetes),那么你可以使用两种主要方法。 |
|||
|
|||
|
|||
### 多个容器 |
|||
|
|||
如果你有 **多个容器**,可能每个容器都运行一个 **单个进程**(例如,在 **Kubernetes** 集群中),那么你可能希望有一个 **单独的容器** 执行以下操作: 在单个容器中运行单个进程执行**先前步骤**,即运行复制的worker容器之前。 |
|||
|
|||
!!! info |
|||
如果你使用 Kubernetes,这可能是 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>。 |
|||
|
|||
如果在你的用例中,运行前面的步骤**并行多次**没有问题(例如,如果你没有运行数据库迁移,而只是检查数据库是否已准备好),那么你也可以将它们放在开始主进程之前在每个容器中。 |
|||
|
|||
### 单容器 |
|||
|
|||
如果你有一个简单的设置,使用一个**单个容器**,然后启动多个**工作进程**(或者也只是一个进程),那么你可以在启动进程之前在应用程序同一个容器中运行先前的步骤。 官方 Docker 镜像内部支持这一点。 |
|||
|
|||
## 带有 Gunicorn 的官方 Docker 镜像 - Uvicorn |
|||
|
|||
有一个官方 Docker 镜像,其中包含与 Uvicorn worker一起运行的 Gunicorn,如上一章所述:[服务器工作线程 - Gunicorn 与 Uvicorn](./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>. |
|||
|
|||
|
|||
!!! warning |
|||
你很有可能不需要此基础镜像或任何其他类似的镜像,最好从头开始构建镜像,如[上面所述:为 FastAPI 构建 Docker 镜像](#build-a-docker-image-for-fastapi)。 |
|||
|
|||
该镜像包含一个**自动调整**机制,用于根据可用的 CPU 核心设置**worker进程数**。 |
|||
|
|||
它具有**合理的默认值**,但你仍然可以使用**环境变量**或配置文件更改和更新所有配置。 |
|||
|
|||
它还支持通过一个脚本运行<a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#pre_start_path" class="external-link" target="_blank">**开始前的先前步骤** </a>。 |
|||
|
|||
!!! tip |
|||
要查看所有配置和选项,请转到 Docker 镜像页面: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank" >tiangolo/uvicorn-gunicorn-fastapi</a>。 |
|||
|
|||
### 官方 Docker 镜像上的进程数 |
|||
|
|||
此镜像上的**进程数**是根据可用的 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 |
|||
``` |
|||
|
|||
### 何时使用 |
|||
|
|||
如果你使用 **Kubernetes** (或其他)并且你已经在集群级别设置 **复制**,并且具有多个 **容器**。 在这些情况下,你最好按照上面的描述 **从头开始构建镜像**:[为 FastAPI 构建 Docker 镜像](#build-a-docker-image-for-fastapi)。 |
|||
|
|||
该镜像主要在[具有多个进程的容器和特殊情况](#containers-with-multiple-processes-and-special-cases)中描述的特殊情况下有用。 例如,如果你的应用程序**足够简单**,基于 CPU 设置默认进程数效果很好,你不想在集群级别手动配置复制,并且不会运行更多进程, 或者你使用 **Docker Compose** 进行部署,在单个服务器上运行等。 |
|||
|
|||
## 部署容器镜像 |
|||
|
|||
拥有容器(Docker)镜像后,有多种方法可以部署它。 |
|||
|
|||
例如: |
|||
|
|||
* 在单个服务器中使用 **Docker Compose** |
|||
* 使用 **Kubernetes** 集群 |
|||
* 使用 Docker Swarm 模式集群 |
|||
* 使用Nomad等其他工具 |
|||
* 使用云服务获取容器镜像并部署它 |
|||
|
|||
## Docker 镜像与Poetry |
|||
|
|||
如果你使用 <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> 来管理项目的依赖项,你可以使用 Docker 多阶段构建: |
|||
|
|||
|
|||
|
|||
```{ .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`对象。 |
|||
|
|||
!!! tip |
|||
单击气泡数字可查看每行的作用。 |
|||
|
|||
**Docker stage** 是 `Dockerfile` 的一部分,用作 **临时容器镜像**,仅用于生成一些稍后使用的文件。 |
|||
|
|||
第一阶段仅用于 **安装 Poetry** 并使用 Poetry 的 `pyproject.toml` 文件中的项目依赖项 **生成 `requirements.txt`**。 |
|||
|
|||
此`requirements.txt`文件将在**下一阶段**与`pip`一起使用。 |
|||
|
|||
在最终的容器镜像中**仅保留最后阶段**。 之前的阶段将被丢弃。 |
|||
|
|||
使用 Poetry 时,使用 **Docker 多阶段构建** 是有意义的,因为你实际上并不需要在最终的容器镜像中安装 Poetry 及其依赖项,你 **只需要** 生成用于安装项目依赖项的`requirements.txt`文件。 |
|||
|
|||
然后,在下一个(也是最后一个)阶段,你将或多或少地以与前面描述的相同的方式构建镜像。 |
|||
|
|||
### 在TLS 终止代理后面 - Poetry |
|||
|
|||
同样,如果你在 Nginx 或 Traefik 等 TLS 终止代理(负载均衡器)后面运行容器,请将选项`--proxy-headers`添加到命令中: |
|||
|
|||
|
|||
```Dockerfile |
|||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] |
|||
``` |
|||
|
|||
## 回顾 |
|||
|
|||
使用容器系统(例如使用**Docker**和**Kubernetes**),处理所有**部署概念**变得相当简单: |
|||
|
|||
* HTTPS |
|||
* 启动时运行 |
|||
* 重新启动 |
|||
* 复制(运行的进程数) |
|||
* 内存 |
|||
* 开始前的先前步骤 |
|||
|
|||
在大多数情况下,你可能不想使用任何基础镜像,而是基于官方 Python Docker 镜像 **从头开始构建容器镜像** 。 |
|||
|
|||
处理好`Dockerfile`和 **Docker 缓存**中指令的**顺序**,你可以**最小化构建时间**,从而最大限度地提高生产力(并避免无聊)。 😎 |
|||
|
|||
在某些特殊情况下,你可能需要使用 FastAPI 的官方 Docker 镜像。 🤓 |
@ -0,0 +1,78 @@ |
|||
# 历史、设计、未来 |
|||
|
|||
不久前,<a href="https://github.com/tiangolo/fastapi/issues/3#issuecomment-454956920" class="external-link" target="_blank">曾有 **FastAPI** 用户问过</a>: |
|||
|
|||
> 这个项目有怎样的历史?好像它只用了几周就从默默无闻变得众所周知…… |
|||
|
|||
在此,我们简单回顾一下 **FastAPI** 的历史。 |
|||
|
|||
## 备选方案 |
|||
|
|||
有那么几年,我曾领导数个开发团队为诸多复杂需求创建各种 API,这些需求包括机器学习、分布系统、异步任务、NoSQL 数据库等领域。 |
|||
|
|||
作为工作的一部分,我需要调研很多备选方案、还要测试并且使用这些备选方案。 |
|||
|
|||
**FastAPI** 其实只是延续了这些前辈的历史。 |
|||
|
|||
正如[备选方案](alternatives.md){.internal-link target=_blank}一章所述: |
|||
|
|||
<blockquote markdown="1"> |
|||
没有大家之前所做的工作,**FastAPI** 就不会存在。 |
|||
|
|||
以前创建的这些工具为它的出现提供了灵感。 |
|||
|
|||
在那几年中,我一直回避创建新的框架。首先,我尝试使用各种框架、插件、工具解决 **FastAPI** 现在的功能。 |
|||
|
|||
但到了一定程度之后,我别无选择,只能从之前的工具中汲取最优思路,并以尽量好的方式把这些思路整合在一起,使用之前甚至是不支持的语言特性(Python 3.6+ 的类型提示),从而创建一个能满足我所有需求的框架。 |
|||
|
|||
</blockquote> |
|||
|
|||
## 调研 |
|||
|
|||
通过使用之前所有的备选方案,我有机会从它们之中学到了很多东西,获取了很多想法,并以我和我的开发团队能想到的最好方式把这些思路整合成一体。 |
|||
|
|||
例如,大家都清楚,在理想状态下,它应该基于标准的 Python 类型提示。 |
|||
|
|||
而且,最好的方式是使用现有的标准。 |
|||
|
|||
因此,甚至在开发 **FastAPI** 前,我就花了几个月的时间研究 OpenAPI、JSON Schema、OAuth2 等规范。深入理解它们之间的关系、重叠及区别之处。 |
|||
|
|||
## 设计 |
|||
|
|||
然后,我又花了一些时间从用户角度(使用 FastAPI 的开发者)设计了开发者 **API**。 |
|||
|
|||
同时,我还在最流行的 Python 代码编辑器中测试了很多思路,包括 PyCharm、VS Code、基于 Jedi 的编辑器。 |
|||
|
|||
根据最新 <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">Python 开发者调研报告</a>显示,这几种编辑器覆盖了约 80% 的用户。 |
|||
|
|||
也就是说,**FastAPI** 针对差不多 80% 的 Python 开发者使用的编辑器进行了测试,而且其它大多数编辑器的工作方式也与之类似,因此,**FastAPI** 的优势几乎能在所有编辑器上体现。 |
|||
|
|||
通过这种方式,我就能找到尽可能减少代码重复的最佳方式,进而实现处处都有自动补全、类型提示与错误检查等支持。 |
|||
|
|||
所有这些都是为了给开发者提供最佳的开发体验。 |
|||
|
|||
## 需求项 |
|||
|
|||
经过测试多种备选方案,我最终决定使用 <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">**Pydantic**</a>,并充分利用它的优势。 |
|||
|
|||
我甚至为它做了不少贡献,让它完美兼容了 JSON Schema,支持多种方式定义约束声明,并基于多个编辑器,改进了它对编辑器支持(类型检查、自动补全)。 |
|||
|
|||
在开发期间,我还为 <a href="https://www.starlette.io/" class="external-link" target="_blank">**Starlette**</a> 做了不少贡献,这是另一个关键需求项。 |
|||
|
|||
## 开发 |
|||
|
|||
当我启动 **FastAPI** 开发的时候,绝大多数部件都已经就位,设计已经定义,需求项和工具也已经准备就绪,相关标准与规范的知识储备也非常清晰而新鲜。 |
|||
|
|||
## 未来 |
|||
|
|||
至此,**FastAPI** 及其理念已经为很多人所用。 |
|||
|
|||
对于很多用例,它比以前很多备选方案都更适用。 |
|||
|
|||
很多开发者和开发团队已经依赖 **FastAPI** 开发他们的项目(包括我和我的团队)。 |
|||
|
|||
但,**FastAPI** 仍有很多改进的余地,也还需要添加更多的功能。 |
|||
|
|||
总之,**FastAPI** 前景光明。 |
|||
|
|||
在此,我们衷心感谢[您的帮助](help-fastapi.md){.internal-link target=_blank}。 |
@ -0,0 +1,84 @@ |
|||
# 项目生成 - 模板 |
|||
|
|||
项目生成器一般都会提供很多初始设置、安全措施、数据库,甚至还准备好了第一个 API 端点,能帮助您快速上手。 |
|||
|
|||
项目生成器的设置通常都很主观,您可以按需更新或修改,但对于您的项目来说,它是非常好的起点。 |
|||
|
|||
## 全栈 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> |
|||
|
|||
### 全栈 FastAPI + PostgreSQL - 功能 |
|||
|
|||
* 完整的 **Docker** 集成(基于 Docker) |
|||
* Docker Swarm 开发模式 |
|||
* **Docker Compose** 本地开发集成与优化 |
|||
* **生产可用**的 Python 网络服务器,使用 Uvicorn 或 Gunicorn |
|||
* Python <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">**FastAPI**</a> 后端: |
|||
* * **速度快**:可与 **NodeJS** 和 **Go** 比肩的极高性能(归功于 Starlette 和 Pydantic) |
|||
* **直观**:强大的编辑器支持,处处皆可<abbr title="也叫自动完成、智能感知">自动补全</abbr>,减少调试时间 |
|||
* **简单**:易学、易用,阅读文档所需时间更短 |
|||
* **简短**:代码重复最小化,每次参数声明都可以实现多个功能 |
|||
* **健壮**: 生产级别的代码,还有自动交互文档 |
|||
* **基于标准**:完全兼容并基于 API 开放标准:<a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> 和 <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a> |
|||
* <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">**更多功能**</a>包括自动验证、序列化、交互文档、OAuth2 JWT 令牌身份验证等 |
|||
* **安全密码**,默认使用密码哈希 |
|||
* **JWT 令牌**身份验证 |
|||
* **SQLAlchemy** 模型(独立于 Flask 扩展,可直接用于 Celery Worker) |
|||
* 基础的用户模型(可按需修改或删除) |
|||
* **Alembic** 迁移 |
|||
* **CORS**(跨域资源共享) |
|||
* **Celery** Worker 可从后端其它部分有选择地导入并使用模型和代码 |
|||
* REST 后端测试基于 Pytest,并与 Docker 集成,可独立于数据库实现完整的 API 交互测试。因为是在 Docker 中运行,每次都可从头构建新的数据存储(使用 ElasticSearch、MongoDB、CouchDB 等数据库,仅测试 API 运行) |
|||
* Python 与 **Jupyter Kernels** 集成,用于远程或 Docker 容器内部开发,使用 Atom Hydrogen 或 Visual Studio Code 的 Jupyter 插件 |
|||
* **Vue** 前端: |
|||
* 由 Vue CLI 生成 |
|||
* **JWT 身份验证**处理 |
|||
* 登录视图 |
|||
* 登录后显示主仪表盘视图 |
|||
* 主仪表盘支持用户创建与编辑 |
|||
* 用户信息编辑 |
|||
* **Vuex** |
|||
* **Vue-router** |
|||
* **Vuetify** 美化组件 |
|||
* **TypeScript** |
|||
* 基于 **Nginx** 的 Docker 服务器(优化了 Vue-router 配置) |
|||
* Docker 多阶段构建,无需保存或提交编译的代码 |
|||
* 在构建时运行前端测试(可禁用) |
|||
* 尽量模块化,开箱即用,但仍可使用 Vue CLI 重新生成或创建所需项目,或复用所需内容 |
|||
* 使用 **PGAdmin** 管理 PostgreSQL 数据库,可轻松替换为 PHPMyAdmin 或 MySQL |
|||
* 使用 **Flower** 监控 Celery 任务 |
|||
* 使用 **Traefik** 处理前后端负载平衡,可把前后端放在同一个域下,按路径分隔,但在不同容器中提供服务 |
|||
* Traefik 集成,包括自动生成 Let's Encrypt **HTTPS** 凭证 |
|||
* GitLab **CI**(持续集成),包括前后端测试 |
|||
|
|||
## 全栈 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> |
|||
|
|||
⚠️ **警告** ⚠️ |
|||
|
|||
如果您想从头开始创建新项目,建议使用以下备选方案。 |
|||
|
|||
例如,项目生成器<a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" class="external-link" target="_blank">全栈 FastAPI + PostgreSQL </a>会更适用,这个项目的维护积极,用的人也多,还包括了所有新功能和改进内容。 |
|||
|
|||
当然,您也可以放心使用这个基于 Couchbase 的生成器,它也能正常使用。就算用它生成项目也没有任何问题(为了更好地满足需求,您可以自行更新这个项目)。 |
|||
|
|||
详见资源仓库中的文档。 |
|||
|
|||
## 全栈 FastAPI + MongoDB |
|||
|
|||
……敬请期待,得看我有没有时间做这个项目。😅 🎉 |
|||
|
|||
## FastAPI + spaCy 机器学习模型 |
|||
|
|||
GitHub:<a href="https://github.com/microsoft/cookiecutter-spacy-fastapi" class="external-link" target="_blank">https://github.com/microsoft/cookiecutter-spacy-fastapi</a> |
|||
|
|||
### FastAPI + spaCy 机器学习模型 - 功能 |
|||
|
|||
* 集成 **spaCy** NER 模型 |
|||
* 内置 **Azure 认知搜索**请求格式 |
|||
* **生产可用**的 Python 网络服务器,使用 Uvicorn 与 Gunicorn |
|||
* 内置 **Azure DevOps** Kubernetes (AKS) CI/CD 开发 |
|||
* **多语**支持,可在项目设置时选择 spaCy 内置的语言 |
|||
* 不仅局限于 spaCy,可**轻松扩展**至其它模型框架(Pytorch、TensorFlow) |
@ -0,0 +1,253 @@ |
|||
# 使用yield的依赖项 |
|||
|
|||
FastAPI支持在完成后执行一些<abbr title='有时也被称为“退出”("exit"),“清理”("cleanup"),“拆卸”("teardown"),“关闭”("close"),“上下文管理器”("context managers")。 ...'>额外步骤</abbr>的依赖项. |
|||
|
|||
为此,请使用 `yield` 而不是 `return`,然后再编写额外的步骤(代码)。 |
|||
|
|||
!!! 提示 |
|||
确保只使用一次 `yield` 。 |
|||
|
|||
!!! 注意 "技术细节" |
|||
|
|||
任何一个可以与以下内容一起使用的函数: |
|||
|
|||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> 或者 |
|||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a> |
|||
|
|||
都可以作为 **FastAPI** 的依赖项。 |
|||
|
|||
实际上,FastAPI内部就使用了这两个装饰器。 |
|||
|
|||
|
|||
## 使用 `yield` 的数据库依赖项 |
|||
|
|||
例如,您可以使用这种方式创建一个数据库会话,并在完成后关闭它。 |
|||
|
|||
在发送响应之前,只会执行 `yield` 语句及之前的代码: |
|||
|
|||
```Python hl_lines="2-4" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
|
|||
生成的值会注入到*路径操作*和其他依赖项中: |
|||
|
|||
```Python hl_lines="4" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
|
|||
"yield"语句后面的代码会在发送响应后执行:: |
|||
|
|||
```Python hl_lines="5-6" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
|
|||
!!! 提示 |
|||
|
|||
您可以使用 `async` 或普通函数。 |
|||
|
|||
**FastAPI** 会像处理普通依赖关系一样,对每个依赖关系做正确的处理。 |
|||
|
|||
## 同时包含了 `yield` 和 `try` 的依赖项 |
|||
|
|||
如果在带有 `yield` 的依赖关系中使用 `try` 代码块,就会收到使用依赖关系时抛出的任何异常。 |
|||
|
|||
例如,如果中间某个代码在另一个依赖中或在*路径操作*中使数据库事务 "回滚 "或产生任何其他错误,您就会在依赖中收到异常。 |
|||
|
|||
因此,你可以使用 `except SomeException` 在依赖关系中查找特定的异常。 |
|||
|
|||
同样,您也可以使用 `finally` 来确保退出步骤得到执行,无论是否存在异常。 |
|||
|
|||
```Python hl_lines="3 5" |
|||
{!../../../docs_src/dependencies/tutorial007.py!} |
|||
``` |
|||
## 使用`yield`的子依赖项 |
|||
|
|||
你可以拥有任意大小和形状的子依赖和子依赖的“树”,而且它们中的任何一个或所有的都可以使用`yield`。 |
|||
|
|||
**FastAPI** 会确保每个带有`yield`的依赖中的“退出代码”按正确顺序运行。 |
|||
|
|||
例如,`dependency_c` 可以依赖于 `dependency_b`,而 `dependency_b` 则依赖于 `dependency_a`。 |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="6 14 22" |
|||
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="5 13 21" |
|||
{!> ../../../docs_src/dependencies/tutorial008_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ non-Annotated" |
|||
|
|||
!!! tip |
|||
如果可能,请尽量使用“ Annotated”版本。 |
|||
|
|||
```Python hl_lines="4 12 20" |
|||
{!> ../../../docs_src/dependencies/tutorial008.py!} |
|||
``` |
|||
|
|||
所有这些依赖都可以使用`yield`。 |
|||
|
|||
在这种情况下,`dependency_c` 在执行其退出代码时需要`dependency_b`(此处称为 `dep_b`)的值仍然可用。 |
|||
|
|||
而`dependency_b` 反过来则需要`dependency_a`(此处称为 `dep_a`)的值在其退出代码中可用。 |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="18-19 26-27" |
|||
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="17-18 25-26" |
|||
{!> ../../../docs_src/dependencies/tutorial008_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ non-Annotated" |
|||
|
|||
!!! tip |
|||
如果可能,请尽量使用“ Annotated”版本。 |
|||
|
|||
```Python hl_lines="16-17 24-25" |
|||
{!> ../../../docs_src/dependencies/tutorial008.py!} |
|||
``` |
|||
|
|||
同样,你可以有混合了`yield`和`return`的依赖。 |
|||
|
|||
你也可以有一个单一的依赖需要多个其他带有`yield`的依赖,等等。 |
|||
|
|||
你可以拥有任何你想要的依赖组合。 |
|||
|
|||
**FastAPI** 将确保按正确的顺序运行所有内容。 |
|||
|
|||
!!! note "技术细节" |
|||
|
|||
这是由 Python 的<a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">上下文管理器</a>完成的。 |
|||
|
|||
**FastAPI** 在内部使用它们来实现这一点。 |
|||
|
|||
|
|||
## 使用 `yield` 和 `HTTPException` 的依赖项 |
|||
|
|||
您看到可以使用带有 `yield` 的依赖项,并且具有捕获异常的 `try` 块。 |
|||
|
|||
在 `yield` 后抛出 `HTTPException` 或类似的异常是很诱人的,但是**这不起作用**。 |
|||
|
|||
带有`yield`的依赖中的退出代码在响应发送之后执行,因此[异常处理程序](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}已经运行过。没有任何东西可以捕获退出代码(在`yield`之后)中的依赖抛出的异常。 |
|||
|
|||
所以,如果在`yield`之后抛出`HTTPException`,默认(或任何自定义)异常处理程序捕获`HTTPException`并返回HTTP 400响应的机制将不再能够捕获该异常。 |
|||
|
|||
这就是允许在依赖中设置的任何东西(例如数据库会话(DB session))可以被后台任务使用的原因。 |
|||
|
|||
后台任务在响应发送之后运行。因此,无法触发`HTTPException`,因为甚至没有办法更改*已发送*的响应。 |
|||
|
|||
但如果后台任务产生了数据库错误,至少你可以在带有`yield`的依赖中回滚或清理关闭会话,并且可能记录错误或将其报告给远程跟踪系统。 |
|||
|
|||
如果你知道某些代码可能会引发异常,那就做最“Pythonic”的事情,就是在代码的那部分添加一个`try`块。 |
|||
|
|||
如果你有自定义异常,希望在返回响应之前处理,并且可能修改响应甚至触发`HTTPException`,可以创建[自定义异常处理程序](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}。 |
|||
|
|||
!!! tip |
|||
|
|||
在`yield`之前仍然可以引发包括`HTTPException`在内的异常,但在`yield`之后则不行。 |
|||
|
|||
执行的顺序大致如下图所示。时间从上到下流动。每列都是相互交互或执行代码的其中一部分。 |
|||
|
|||
```mermaid |
|||
sequenceDiagram |
|||
|
|||
participant client as Client |
|||
participant handler as Exception handler |
|||
participant dep as Dep with yield |
|||
participant operation as Path Operation |
|||
participant tasks as Background tasks |
|||
|
|||
Note over client,tasks: Can raise exception for dependency, handled after response is sent |
|||
Note over client,operation: Can raise HTTPException and can change the response |
|||
client ->> dep: Start request |
|||
Note over dep: Run code up to yield |
|||
opt raise |
|||
dep -->> handler: Raise HTTPException |
|||
handler -->> client: HTTP error response |
|||
dep -->> dep: Raise other exception |
|||
end |
|||
dep ->> operation: Run dependency, e.g. DB session |
|||
opt raise |
|||
operation -->> dep: Raise HTTPException |
|||
dep -->> handler: Auto forward exception |
|||
handler -->> client: HTTP error response |
|||
operation -->> dep: Raise other exception |
|||
dep -->> handler: Auto forward exception |
|||
end |
|||
operation ->> client: Return response to client |
|||
Note over client,operation: Response is already sent, can't change it anymore |
|||
opt Tasks |
|||
operation -->> tasks: Send background tasks |
|||
end |
|||
opt Raise other exception |
|||
tasks -->> dep: Raise other exception |
|||
end |
|||
Note over dep: After yield |
|||
opt Handle other exception |
|||
dep -->> dep: Handle exception, can't change response. E.g. close DB session. |
|||
end |
|||
``` |
|||
|
|||
!!! info |
|||
只会向客户端发送**一次响应**,可能是一个错误响应之一,也可能是来自*路径操作*的响应。 |
|||
|
|||
在发送了其中一个响应之后,就无法再发送其他响应了。 |
|||
|
|||
!!! tip |
|||
这个图表展示了`HTTPException`,但你也可以引发任何其他你创建了[自定义异常处理程序](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}的异常。 |
|||
|
|||
如果你引发任何异常,它将传递给带有`yield`的依赖,包括`HTTPException`,然后**再次**传递给异常处理程序。如果没有针对该异常的异常处理程序,那么它将被默认的内部`ServerErrorMiddleware`处理,返回500 HTTP状态码,告知客户端服务器发生了错误。 |
|||
|
|||
## 上下文管理器 |
|||
|
|||
### 什么是“上下文管理器” |
|||
|
|||
“上下文管理器”是您可以在`with`语句中使用的任何Python对象。 |
|||
|
|||
例如,<a href="https://docs.python.org/zh-cn/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">您可以使用`with`读取文件</a>: |
|||
|
|||
```Python |
|||
with open("./somefile.txt") as f: |
|||
contents = f.read() |
|||
print(contents) |
|||
``` |
|||
|
|||
在底层,`open("./somefile.txt")`创建了一个被称为“上下文管理器”的对象。 |
|||
|
|||
当`with`块结束时,它会确保关闭文件,即使发生了异常也是如此。 |
|||
|
|||
当你使用`yield`创建一个依赖项时,**FastAPI**会在内部将其转换为上下文管理器,并与其他相关工具结合使用。 |
|||
|
|||
### 在依赖项中使用带有`yield`的上下文管理器 |
|||
|
|||
!!! warning |
|||
这是一个更为“高级”的想法。 |
|||
|
|||
如果您刚开始使用**FastAPI**,您可能暂时可以跳过它。 |
|||
|
|||
在Python中,你可以通过<a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">创建一个带有`__enter__()`和`__exit__()`方法的类</a>来创建上下文管理器。 |
|||
|
|||
你也可以在**FastAPI**的依赖项中使用带有`yield`的`with`或`async with`语句,通过在依赖函数内部使用它们。 |
|||
|
|||
```Python hl_lines="1-9 13" |
|||
{!../../../docs_src/dependencies/tutorial010.py!} |
|||
``` |
|||
|
|||
!!! tip |
|||
另一种创建上下文管理器的方法是: |
|||
|
|||
* <a href="https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a>或者 |
|||
* <a href="https://docs.python.org/zh-cn/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a> |
|||
|
|||
使用上下文管理器装饰一个只有单个`yield`的函数。这就是**FastAPI**在内部用于带有`yield`的依赖项的方式。 |
|||
|
|||
但是你不需要为FastAPI的依赖项使用这些装饰器(而且也不应该)。FastAPI会在内部为你处理这些。 |
Loading…
Reference in new issue