mirror of https://github.com/tiangolo/fastapi.git
Merge branch 'tiangolo:master' into patch-1
This commit is contained in:
commit
099e4c86db
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -130,6 +131,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -233,6 +234,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
## Latest Changes
|
||||
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/events.md`. PR [#9326](https://github.com/tiangolo/fastapi/pull/9326) by [@oandersonmagalhaes](https://github.com/oandersonmagalhaes).
|
||||
* 🌐 Add Russian translation for `docs/ru/docs/deployment/manually.md`. PR [#9417](https://github.com/tiangolo/fastapi/pull/9417) by [@Xewus](https://github.com/Xewus).
|
||||
* 🌐 Add setup for translations to Lao. PR [#9396](https://github.com/tiangolo/fastapi/pull/9396) by [@TheBrown](https://github.com/TheBrown).
|
||||
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/testing.md`. PR [#9403](https://github.com/tiangolo/fastapi/pull/9403) by [@Xewus](https://github.com/Xewus).
|
||||
* 🌐 Add Russian translation for `docs/ru/docs/deployment/https.md`. PR [#9428](https://github.com/tiangolo/fastapi/pull/9428) by [@Xewus](https://github.com/Xewus).
|
||||
* ✏ Fix command to install requirements in Windows. PR [#9445](https://github.com/tiangolo/fastapi/pull/9445) by [@MariiaRomanuik](https://github.com/MariiaRomanuik).
|
||||
* 🌐 Add French translation for `docs/fr/docs/advanced/response-directly.md`. PR [#9415](https://github.com/tiangolo/fastapi/pull/9415) by [@axel584](https://github.com/axel584).
|
||||
* 🌐 Initiate Czech translation setup. PR [#9288](https://github.com/tiangolo/fastapi/pull/9288) by [@3p1463k](https://github.com/3p1463k).
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -236,6 +237,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -139,6 +140,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -158,6 +159,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -173,6 +174,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -143,6 +144,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -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, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
|
||||
|
||||
---
|
||||
|
||||
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints.
|
||||
|
||||
The key features are:
|
||||
|
||||
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
|
||||
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
|
||||
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
|
||||
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
|
||||
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
|
||||
* **Robust**: Get production-ready code. With automatic interactive documentation.
|
||||
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
|
||||
|
||||
<small>* estimation based on tests on an internal development team, building production applications.</small>
|
||||
|
||||
## Sponsors
|
||||
|
||||
<!-- 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">Other sponsors</a>
|
||||
|
||||
## Opinions
|
||||
|
||||
"_[...] 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, and 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>
|
||||
|
||||
---
|
||||
|
||||
"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
## **Typer**, the FastAPI of CLIs
|
||||
|
||||
<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>
|
||||
|
||||
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
|
||||
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
|
||||
|
||||
## Requirements
|
||||
|
||||
Python 3.7+
|
||||
|
||||
FastAPI stands on the shoulders of giants:
|
||||
|
||||
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
|
||||
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
|
||||
|
||||
## Installation
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install fastapi
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
You will also need an ASGI server, for production such as <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Example
|
||||
|
||||
### Create it
|
||||
|
||||
* Create a file `main.py` with:
|
||||
|
||||
```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>Or use <code>async def</code>...</summary>
|
||||
|
||||
If your code uses `async` / `await`, use `async def`:
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
**Note**:
|
||||
|
||||
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
|
||||
|
||||
</details>
|
||||
|
||||
### Run it
|
||||
|
||||
Run the server with:
|
||||
|
||||
<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>About the command <code>uvicorn main:app --reload</code>...</summary>
|
||||
|
||||
The command `uvicorn main:app` refers to:
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--reload`: make the server restart after code changes. Only do this for development.
|
||||
|
||||
</details>
|
||||
|
||||
### Check it
|
||||
|
||||
Open your browser at <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>.
|
||||
|
||||
You will see the JSON response as:
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
You already created an API that:
|
||||
|
||||
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
|
||||
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
|
||||
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
|
||||
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
|
||||
|
||||
### Interactive API docs
|
||||
|
||||
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
|
||||
|
||||

|
||||
|
||||
### Alternative API docs
|
||||
|
||||
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
|
||||
|
||||

|
||||
|
||||
## Example upgrade
|
||||
|
||||
Now modify the file `main.py` to receive a body from a `PUT` request.
|
||||
|
||||
Declare the body using standard Python types, thanks to Pydantic.
|
||||
|
||||
```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}
|
||||
```
|
||||
|
||||
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
|
||||
|
||||
### Interactive API docs upgrade
|
||||
|
||||
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
* The interactive API documentation will be automatically updated, including the new body:
|
||||
|
||||

|
||||
|
||||
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
|
||||
|
||||

|
||||
|
||||
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
|
||||
|
||||

|
||||
|
||||
### Alternative API docs upgrade
|
||||
|
||||
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
* The alternative documentation will also reflect the new query parameter and body:
|
||||
|
||||

|
||||
|
||||
### Recap
|
||||
|
||||
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
|
||||
|
||||
You do that with standard modern Python types.
|
||||
|
||||
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
|
||||
|
||||
Just standard **Python 3.7+**.
|
||||
|
||||
For example, for an `int`:
|
||||
|
||||
```Python
|
||||
item_id: int
|
||||
```
|
||||
|
||||
or for a more complex `Item` model:
|
||||
|
||||
```Python
|
||||
item: Item
|
||||
```
|
||||
|
||||
...and with that single declaration you get:
|
||||
|
||||
* Editor support, including:
|
||||
* Completion.
|
||||
* Type checks.
|
||||
* Validation of data:
|
||||
* Automatic and clear errors when the data is invalid.
|
||||
* Validation even for deeply nested JSON objects.
|
||||
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
|
||||
* JSON.
|
||||
* Path parameters.
|
||||
* Query parameters.
|
||||
* Cookies.
|
||||
* Headers.
|
||||
* Forms.
|
||||
* Files.
|
||||
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
|
||||
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
|
||||
* `datetime` objects.
|
||||
* `UUID` objects.
|
||||
* Database models.
|
||||
* ...and many more.
|
||||
* Automatic interactive API documentation, including 2 alternative user interfaces:
|
||||
* Swagger UI.
|
||||
* ReDoc.
|
||||
|
||||
---
|
||||
|
||||
Coming back to the previous code example, **FastAPI** will:
|
||||
|
||||
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
|
||||
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
|
||||
* If it is not, the client will see a useful, clear error.
|
||||
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
|
||||
* As the `q` parameter is declared with `= None`, it is optional.
|
||||
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
||||
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
|
||||
* Check that it has a required attribute `name` that should be a `str`.
|
||||
* Check that it has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
||||
* All this would also work for deeply nested JSON objects.
|
||||
* Convert from and to JSON automatically.
|
||||
* Document everything with OpenAPI, that can be used by:
|
||||
* Interactive documentation systems.
|
||||
* Automatic client code generation systems, for many languages.
|
||||
* Provide 2 interactive documentation web interfaces directly.
|
||||
|
||||
---
|
||||
|
||||
We just scratched the surface, but you already get the idea of how it all works.
|
||||
|
||||
Try changing the line with:
|
||||
|
||||
```Python
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
...from:
|
||||
|
||||
```Python
|
||||
... "item_name": item.name ...
|
||||
```
|
||||
|
||||
...to:
|
||||
|
||||
```Python
|
||||
... "item_price": item.price ...
|
||||
```
|
||||
|
||||
...and see how your editor will auto-complete the attributes and know their types:
|
||||
|
||||

|
||||
|
||||
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
|
||||
|
||||
**Spoiler alert**: the tutorial - user guide includes:
|
||||
|
||||
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
|
||||
* How to set **validation constraints** as `maximum_length` or `regex`.
|
||||
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
|
||||
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
|
||||
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
|
||||
## Performance
|
||||
|
||||
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <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">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
|
||||
|
||||
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
|
||||
|
||||
## Optional Dependencies
|
||||
|
||||
Used by Pydantic:
|
||||
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
|
||||
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
|
||||
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
|
||||
|
||||
Used by FastAPI / Starlette:
|
||||
|
||||
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
|
||||
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
|
||||
|
||||
You can install all of these with `pip install "fastapi[all]"`.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the terms of the MIT license.
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
site_name: FastAPI
|
||||
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
||||
site_url: https://fastapi.tiangolo.com/lo/
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: overrides
|
||||
palette:
|
||||
- media: '(prefers-color-scheme: light)'
|
||||
scheme: default
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb
|
||||
name: Switch to light mode
|
||||
- media: '(prefers-color-scheme: dark)'
|
||||
scheme: slate
|
||||
primary: teal
|
||||
accent: amber
|
||||
toggle:
|
||||
icon: material/lightbulb-outline
|
||||
name: Switch to dark mode
|
||||
features:
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
- content.tabs.link
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: https://fastapi.tiangolo.com/img/icon-white.svg
|
||||
favicon: https://fastapi.tiangolo.com/img/favicon.png
|
||||
language: en
|
||||
repo_name: tiangolo/fastapi
|
||||
repo_url: https://github.com/tiangolo/fastapi
|
||||
edit_uri: ''
|
||||
plugins:
|
||||
- search
|
||||
- markdownextradata:
|
||||
data: data
|
||||
nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- az: /az/
|
||||
- de: /de/
|
||||
- em: /em/
|
||||
- es: /es/
|
||||
- fa: /fa/
|
||||
- fr: /fr/
|
||||
- he: /he/
|
||||
- hy: /hy/
|
||||
- id: /id/
|
||||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- sq: /sq/
|
||||
- sv: /sv/
|
||||
- ta: /ta/
|
||||
- tr: /tr/
|
||||
- uk: /uk/
|
||||
- zh: /zh/
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
- markdown.extensions.codehilite:
|
||||
guess_lang: false
|
||||
- mdx_include:
|
||||
base_path: docs
|
||||
- admonition
|
||||
- codehilite
|
||||
- extra
|
||||
- pymdownx.superfences:
|
||||
custom_fences:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format ''
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- attr_list
|
||||
- md_in_html
|
||||
extra:
|
||||
analytics:
|
||||
provider: google
|
||||
property: G-YNEVN69SC3
|
||||
social:
|
||||
- icon: fontawesome/brands/github-alt
|
||||
link: https://github.com/tiangolo/fastapi
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/VQjSZaeJmf
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://twitter.com/fastapi
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/in/tiangolo
|
||||
- icon: fontawesome/brands/dev
|
||||
link: https://dev.to/tiangolo
|
||||
- icon: fontawesome/brands/medium
|
||||
link: https://medium.com/@tiangolo
|
||||
- icon: fontawesome/solid/globe
|
||||
link: https://tiangolo.com
|
||||
alternate:
|
||||
- link: /
|
||||
name: en - English
|
||||
- link: /az/
|
||||
name: az
|
||||
- link: /de/
|
||||
name: de
|
||||
- link: /em/
|
||||
name: 😉
|
||||
- link: /es/
|
||||
name: es - español
|
||||
- link: /fa/
|
||||
name: fa
|
||||
- link: /fr/
|
||||
name: fr - français
|
||||
- link: /he/
|
||||
name: he
|
||||
- link: /hy/
|
||||
name: hy
|
||||
- link: /id/
|
||||
name: id
|
||||
- link: /it/
|
||||
name: it - italiano
|
||||
- link: /ja/
|
||||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
name: pl
|
||||
- link: /pt/
|
||||
name: pt - português
|
||||
- link: /ru/
|
||||
name: ru - русский язык
|
||||
- link: /sq/
|
||||
name: sq - shqip
|
||||
- link: /sv/
|
||||
name: sv - svenska
|
||||
- link: /ta/
|
||||
name: ta - தமிழ்
|
||||
- link: /tr/
|
||||
name: tr - Türkçe
|
||||
- link: /uk/
|
||||
name: uk - українська мова
|
||||
- link: /zh/
|
||||
name: zh - 汉语
|
||||
extra_css:
|
||||
- https://fastapi.tiangolo.com/css/termynal.css
|
||||
- https://fastapi.tiangolo.com/css/custom.css
|
||||
extra_javascript:
|
||||
- https://fastapi.tiangolo.com/js/termynal.js
|
||||
- https://fastapi.tiangolo.com/js/custom.js
|
||||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -132,6 +133,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
# Eventos de vida útil
|
||||
|
||||
Você pode definir a lógica (código) que poderia ser executada antes da aplicação **inicializar**. Isso significa que esse código será executado **uma vez**, **antes** da aplicação **começar a receber requisições**.
|
||||
|
||||
Do mesmo modo, você pode definir a lógica (código) que será executada quando a aplicação estiver sendo **encerrada**. Nesse caso, este código será executado **uma vez**, **depois** de ter possivelmente tratado **várias requisições**.
|
||||
|
||||
Por conta desse código ser executado antes da aplicação **começar** a receber requisições, e logo após **terminar** de lidar com as requisições, ele cobre toda a **vida útil** (_lifespan_) da aplicação (o termo "vida útil" será importante em um segundo 😉).
|
||||
|
||||
Pode ser muito útil para configurar **recursos** que você precisa usar por toda aplicação, e que são **compartilhados** entre as requisições, e/ou que você precisa **limpar** depois. Por exemplo, o pool de uma conexão com o banco de dados ou carregamento de um modelo compartilhado de aprendizado de máquina (_machine learning_).
|
||||
|
||||
## Caso de uso
|
||||
|
||||
Vamos iniciar com um exemplo de **caso de uso** e então ver como resolvê-lo com isso.
|
||||
|
||||
Vamos imaginar que você tem alguns **modelos de _machine learning_** que deseja usar para lidar com as requisições. 🤖
|
||||
|
||||
Os mesmos modelos são compartilhados entre as requisições, então não é um modelo por requisição, ou um por usuário ou algo parecido.
|
||||
|
||||
Vamos imaginar que o carregamento do modelo pode **demorar bastante tempo**, porque ele tem que ler muitos **dados do disco**. Então você não quer fazer isso a cada requisição.
|
||||
|
||||
Você poderia carregá-lo no nível mais alto do módulo/arquivo, mas isso também poderia significaria **carregar o modelo** mesmo se você estiver executando um simples teste automatizado, então esse teste poderia ser **lento** porque teria que esperar o carregamento do modelo antes de ser capaz de executar uma parte independente do código.
|
||||
|
||||
|
||||
Isso é que nós iremos resolver, vamos carregar o modelo antes das requisições serem manuseadas, mas apenas um pouco antes da aplicação começar a receber requisições, não enquanto o código estiver sendo carregado.
|
||||
|
||||
## Vida útil (_Lifespan_)
|
||||
|
||||
Você pode definir essa lógica de *inicialização* e *encerramento* usando os parâmetros de `lifespan` da aplicação `FastAPI`, e um "gerenciador de contexto" (te mostrarei o que é isso a seguir).
|
||||
|
||||
Vamos iniciar com um exemplo e ver isso detalhadamente.
|
||||
|
||||
Nós criamos uma função assíncrona chamada `lifespan()` com `yield` como este:
|
||||
|
||||
```Python hl_lines="16 19"
|
||||
{!../../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
|
||||
Aqui nós estamos simulando a *inicialização* custosa do carregamento do modelo colocando a (falsa) função de modelo no dicionário com modelos de _machine learning_ antes do `yield`. Este código será executado **antes** da aplicação **começar a receber requisições**, durante a *inicialização*.
|
||||
|
||||
E então, logo após o `yield`, descarregaremos o modelo. Esse código será executado **após** a aplicação **terminar de lidar com as requisições**, pouco antes do *encerramento*. Isso poderia, por exemplo, liberar recursos como memória ou GPU.
|
||||
|
||||
!!! tip "Dica"
|
||||
O `shutdown` aconteceria quando você estivesse **encerrando** a aplicação.
|
||||
|
||||
Talvez você precise inicializar uma nova versão, ou apenas cansou de executá-la. 🤷
|
||||
|
||||
### Função _lifespan_
|
||||
|
||||
A primeira coisa a notar, é que estamos definindo uma função assíncrona com `yield`. Isso é muito semelhante à Dependências com `yield`.
|
||||
|
||||
```Python hl_lines="14-19"
|
||||
{!../../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
|
||||
A primeira parte da função, antes do `yield`, será executada **antes** da aplicação inicializar.
|
||||
|
||||
E a parte posterior do `yield` irá executar **após** a aplicação ser encerrada.
|
||||
|
||||
### Gerenciador de Contexto Assíncrono
|
||||
|
||||
Se você verificar, a função está decorada com um `@asynccontextmanager`.
|
||||
|
||||
Que converte a função em algo chamado de "**Gerenciador de Contexto Assíncrono**".
|
||||
|
||||
```Python hl_lines="1 13"
|
||||
{!../../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
|
||||
Um **gerenciador de contexto** em Python é algo que você pode usar em uma declaração `with`, por exemplo, `open()` pode ser usado como um gerenciador de contexto:
|
||||
|
||||
```Python
|
||||
with open("file.txt") as file:
|
||||
file.read()
|
||||
```
|
||||
|
||||
Nas versões mais recentes de Python, há também um **gerenciador de contexto assíncrono**. Você o usaria com `async with`:
|
||||
|
||||
```Python
|
||||
async with lifespan(app):
|
||||
await do_stuff()
|
||||
```
|
||||
|
||||
Quando você cria um gerenciador de contexto ou um gerenciador de contexto assíncrono como mencionado acima, o que ele faz é que, antes de entrar no bloco `with`, ele irá executar o código anterior ao `yield`, e depois de sair do bloco `with`, ele irá executar o código depois do `yield`.
|
||||
|
||||
No nosso exemplo de código acima, nós não usamos ele diretamente, mas nós passamos para o FastAPI para ele usá-lo.
|
||||
|
||||
O parâmetro `lifespan` da aplicação `FastAPI` usa um **Gerenciador de Contexto Assíncrono**, então nós podemos passar nosso novo gerenciador de contexto assíncrono do `lifespan` para ele.
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!../../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Eventos alternativos (deprecados)
|
||||
|
||||
!!! warning "Aviso"
|
||||
A maneira recomendada para lidar com a *inicialização* e o *encerramento* é usando o parâmetro `lifespan` da aplicação `FastAPI` como descrito acima.
|
||||
|
||||
Você provavelmente pode pular essa parte.
|
||||
|
||||
Existe uma forma alternativa para definir a execução dessa lógica durante *inicialização* e durante *encerramento*.
|
||||
|
||||
Você pode definir manipuladores de eventos (funções) que precisam ser executadas antes da aplicação inicializar, ou quando a aplicação estiver encerrando.
|
||||
|
||||
Essas funções podem ser declaradas com `async def` ou `def` normal.
|
||||
|
||||
### Evento `startup`
|
||||
|
||||
Para adicionar uma função que deve rodar antes da aplicação iniciar, declare-a com o evento `"startup"`:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/events/tutorial001.py!}
|
||||
```
|
||||
|
||||
Nesse caso, a função de manipulação de evento `startup` irá inicializar os itens do "banco de dados" (só um `dict`) com alguns valores.
|
||||
|
||||
Você pode adicionar mais que uma função de manipulação de evento.
|
||||
|
||||
E sua aplicação não irá começar a receber requisições até que todos os manipuladores de eventos de `startup` sejam concluídos.
|
||||
|
||||
### Evento `shutdown`
|
||||
|
||||
Para adicionar uma função que deve ser executada quando a aplicação estiver encerrando, declare ela com o evento `"shutdown"`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/events/tutorial002.py!}
|
||||
```
|
||||
|
||||
Aqui, a função de manipulação de evento `shutdown` irá escrever uma linha de texto `"Application shutdown"` no arquivo `log.txt`.
|
||||
|
||||
!!! info "Informação"
|
||||
Na função `open()`, o `mode="a"` significa "acrescentar", então, a linha irá ser adicionada depois de qualquer coisa que esteja naquele arquivo, sem sobrescrever o conteúdo anterior.
|
||||
|
||||
!!! tip "Dica"
|
||||
Perceba que nesse caso nós estamos usando a função padrão do Python `open()` que interage com um arquivo.
|
||||
|
||||
Então, isso envolve I/O (input/output), que exige "esperar" que coisas sejam escritas em disco.
|
||||
|
||||
Mas `open()` não usa `async` e `await`.
|
||||
|
||||
Então, nós declaramos uma função de manipulação de evento com o padrão `def` ao invés de `async def`.
|
||||
|
||||
### `startup` e `shutdown` juntos
|
||||
|
||||
Há uma grande chance que a lógica para sua *inicialização* e *encerramento* esteja conectada, você pode querer iniciar alguma coisa e então finalizá-la, adquirir um recurso e então liberá-lo, etc.
|
||||
|
||||
Fazendo isso em funções separadas que não compartilham lógica ou variáveis entre elas é mais difícil já que você precisa armazenar os valores em variáveis globais ou truques parecidos.
|
||||
|
||||
Por causa disso, agora é recomendado em vez disso usar o `lifespan` como explicado acima.
|
||||
|
||||
## Detalhes técnicos
|
||||
|
||||
Só um detalhe técnico para nerds curiosos. 🤓
|
||||
|
||||
Por baixo, na especificação técnica ASGI, essa é a parte do <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Protocolo Lifespan</a>, e define eventos chamados `startup` e `shutdown`.
|
||||
|
||||
!!! info "Informação"
|
||||
Você pode ler mais sobre o manipulador `lifespan` do Starlette na <a href="https://www.starlette.io/lifespan/" class="external-link" target="_blank">Documentação do Lifespan Starlette</a>.
|
||||
|
||||
Incluindo como manipular estado do lifespan que pode ser usado em outras áreas do seu código.
|
||||
|
||||
## Sub Aplicações
|
||||
|
||||
🚨 Tenha em mente que esses eventos de lifespan (de inicialização e desligamento) irão somente ser executados para a aplicação principal, não para [Sub Aplicações - Montagem](./sub-applications.md){.internal-link target=_blank}.
|
||||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -91,6 +92,7 @@ nav:
|
|||
- tutorial/static-files.md
|
||||
- Guia de Usuário Avançado:
|
||||
- advanced/index.md
|
||||
- advanced/events.md
|
||||
- Implantação:
|
||||
- deployment/index.md
|
||||
- deployment/versions.md
|
||||
|
|
@ -169,6 +171,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,198 @@
|
|||
# Об HTTPS
|
||||
|
||||
Обычно представляется, что HTTPS это некая опция, которая либо "включена", либо нет.
|
||||
|
||||
Но всё несколько сложнее.
|
||||
|
||||
!!! tip "Заметка"
|
||||
Если Вы торопитесь или Вам не интересно, можете перейти на следующую страницу этого пошагового руководства по размещению приложений на серверах с использованием различных технологий.
|
||||
|
||||
Чтобы **изучить основы HTTPS** для клиента, перейдите по ссылке <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
|
||||
Здесь же представлены некоторые концепции, которые **разработчик** должен иметь в виду при размышлениях об HTTPS:
|
||||
|
||||
* Протокол HTTPS предполагает, что **серверу** нужно **располагать "сертификатами"** сгенерированными **третьей стороной**.
|
||||
* На самом деле эти сертификаты **приобретены** у третьей стороны, а не "сгенерированы".
|
||||
* У сертификатов есть **срок годности**.
|
||||
* Срок годности **истекает**.
|
||||
* По истечении срока годности их нужно **обновить**, то есть **снова получить** у третьей стороны.
|
||||
* Шифрование соединения происходит **на уровне протокола TCP**.
|
||||
* Протокол TCP находится на один уровень **ниже протокола HTTP**.
|
||||
* Поэтому **проверка сертификатов и шифрование** происходит **до HTTP**.
|
||||
* **TCP не знает о "доменах"**, но знает об IP-адресах.
|
||||
* Информация о **запрашиваемом домене** извлекается из запроса **на уровне HTTP**.
|
||||
* **Сертификаты HTTPS** "сертифицируют" **конкретный домен**, но проверка сертификатов и шифрование данных происходит на уровне протокола TCP, то есть **до того**, как станет известен домен-получатель данных.
|
||||
* **По умолчанию** это означает, что у Вас может быть **только один сертификат HTTPS на один IP-адрес**.
|
||||
* Не важно, насколько большой у Вас сервер и насколько маленькие приложения на нём могут быть.
|
||||
* Однако, у этой проблемы есть **решение**.
|
||||
* Существует **расширение** протокола **TLS** (который работает на уровне TCP, то есть до HTTP) называемое **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Указание имени сервера">SNI</abbr></a>**.
|
||||
* Расширение SNI позволяет одному серверу (с **одним IP-адресом**) иметь **несколько сертификатов HTTPS** и обслуживать **множество HTTPS-доменов/приложений**.
|
||||
* Чтобы эта конструкция работала, **один** её компонент (программа) запущенный на сервере и слушающий **публичный IP-адрес**, должен иметь **все сертификаты HTTPS** для этого сервера.
|
||||
* **После** установления защищённого соединения, протоколом передачи данных **остаётся HTTP**.
|
||||
* Но данные теперь **зашифрованы**, несмотря на то, что они передаются по **протоколу HTTP**.
|
||||
|
||||
Обычной практикой является иметь **одну программу/HTTP-сервер** запущенную на сервере (машине, хосте и т.д.) и **ответственную за всю работу с HTTPS**:
|
||||
|
||||
* получение **зашифрованных HTTPS-запросов**
|
||||
* отправка **расшифрованных HTTP запросов** в соответствующее HTTP-приложение, работающее на том же сервере (в нашем случае, это приложение **FastAPI**)
|
||||
* получние **HTTP-ответа** от приложения
|
||||
* **шифрование ответа** используя подходящий **сертификат HTTPS**
|
||||
* отправка зашифрованного **HTTPS-ответа клиенту**.
|
||||
Такой сервер часто называют **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Прокси-сервер завершения работы TLS</a>** или просто "прокси-сервер".
|
||||
|
||||
Вот некоторые варианты, которые Вы можете использовать в качестве такого прокси-сервера:
|
||||
|
||||
* Traefik (может обновлять сертификаты)
|
||||
* Caddy (может обновлять сертификаты)
|
||||
* Nginx
|
||||
* HAProxy
|
||||
|
||||
## Let's Encrypt (центр сертификации)
|
||||
|
||||
До появления Let's Encrypt **сертификаты HTTPS** приходилось покупать у третьих сторон.
|
||||
|
||||
Процесс получения такого сертификата был трудоёмким, требовал предоставления подтверждающих документов и сертификаты стоили дорого.
|
||||
|
||||
Но затем консорциумом Linux Foundation был создан проект **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**.
|
||||
|
||||
Он автоматически предоставляет **бесплатные сертификаты HTTPS**. Эти сертификаты используют все стандартные криптографические способы шифрования. Они имеют небольшой срок годности (около 3 месяцев), благодаря чему они даже **более безопасны**.
|
||||
|
||||
При запросе на получение сертификата, он автоматически генерируется и домен проверяется на безопасность. Это позволяет обновлять сертификаты автоматически.
|
||||
|
||||
Суть идеи в автоматическом получении и обновлении этих сертификатов, чтобы все могли пользоваться **безопасным HTTPS. Бесплатно. В любое время.**
|
||||
|
||||
## HTTPS для разработчиков
|
||||
|
||||
Ниже, шаг за шагом, с заострением внимания на идеях, важных для разработчика, описано, как может выглядеть HTTPS API.
|
||||
|
||||
### Имя домена
|
||||
|
||||
Чаще всего, всё начинается с **приобретения имени домена**. Затем нужно настроить DNS-сервер (вероятно у того же провайдера, который выдал Вам домен).
|
||||
|
||||
Далее, возможно, Вы получаете "облачный" сервер (виртуальную машину) или что-то типа этого, у которого есть <abbr title="Не изменяемый">постоянный</abbr> **публичный IP-адрес**.
|
||||
|
||||
На DNS-сервере (серверах) Вам следует настроить соответствующую ресурсную запись ("`запись A`"), указав, что **Ваш домен** связан с публичным **IP-адресом Вашего сервера**.
|
||||
|
||||
Обычно эту запись достаточно указать один раз, при первоначальной настройке всего сервера.
|
||||
|
||||
!!! tip "Заметка"
|
||||
Уровни протоколов, работающих с именами доменов, намного ниже HTTPS, но об этом следует упомянуть здесь, так как всё зависит от доменов и IP-адресов.
|
||||
|
||||
### DNS
|
||||
|
||||
Теперь давайте сфокусируемся на работе с HTTPS.
|
||||
|
||||
Всё начинается с того, что браузер спрашивает у **DNS-серверов**, какой **IP-адрес связан с доменом**, для примера возьмём домен `someapp.example.com`.
|
||||
|
||||
DNS-сервера присылают браузеру определённый **IP-адрес**, тот самый публичный IP-адрес Вашего сервера, который Вы указали в ресурсной "записи А" при настройке.
|
||||
|
||||
<img src="/img/deployment/https/https01.svg">
|
||||
|
||||
### Рукопожатие TLS
|
||||
|
||||
В дальнейшем браузер будет взаимодействовать с этим IP-адресом через **port 443** (общепринятый номер порта для HTTPS).
|
||||
|
||||
Первым шагом будет установление соединения между клиентом (браузером) и сервером и выбор криптографического ключа (для шифрования).
|
||||
|
||||
<img src="/img/deployment/https/https02.svg">
|
||||
|
||||
Эта часть клиент-серверного взаимодействия устанавливает TLS-соединение и называется **TLS-рукопожатием**.
|
||||
|
||||
### TLS с расширением SNI
|
||||
|
||||
На сервере **только один процесс** может прослушивать определённый **порт** определённого **IP-адреса**. На сервере могут быть и другие процессы, слушающие другие порты этого же IP-адреса, но никакой процесс не может быть привязан к уже занятой комбинации IP-адрес:порт. Эта комбинация называется "сокет".
|
||||
|
||||
По умолчанию TLS (HTTPS) использует порт `443`. Потому этот же порт будем использовать и мы.
|
||||
|
||||
И раз уж только один процесс может слушать этот порт, то это будет процесс **прокси-сервера завершения работы TLS**.
|
||||
|
||||
Прокси-сервер завершения работы TLS будет иметь доступ к одному или нескольким **TLS-сертификатам** (сертификаты HTTPS).
|
||||
|
||||
Используя **расширение SNI** упомянутое выше, прокси-сервер из имеющихся сертификатов TLS (HTTPS) выберет тот, который соответствует имени домена, указанному в запросе от клиента.
|
||||
|
||||
То есть будет выбран сертификат для домена `someapp.example.com`.
|
||||
|
||||
<img src="/img/deployment/https/https03.svg">
|
||||
|
||||
Клиент уже **доверяет** тому, кто выдал этот TLS-сертификат (в нашем случае - Let's Encrypt, но мы ещё обсудим это), потому может **проверить**, действителен ли полученный от сервера сертификат.
|
||||
|
||||
Затем, используя этот сертификат, клиент и прокси-сервер **выбирают способ шифрования** данных для устанавливаемого **TCP-соединения**. На этом операция **TLS-рукопожатия** завершена.
|
||||
|
||||
В дальнейшем клиент и сервер будут взаимодействовать по **зашифрованному TCP-соединению**, как предлагается в протоколе TLS. И на основе этого TCP-соедениния будет создано **HTTP-соединение**.
|
||||
|
||||
Таким образом, **HTTPS** это тот же **HTTP**, но внутри **безопасного TLS-соединения** вместо чистого (незашифрованного) TCP-соединения.
|
||||
|
||||
!!! tip "Заметка"
|
||||
Обратите внимание, что шифрование происходит на **уровне TCP**, а не на более высоком уровне HTTP.
|
||||
|
||||
### HTTPS-запрос
|
||||
|
||||
Теперь, когда между клиентом и сервером (в нашем случае, браузером и прокси-сервером) создано **зашифрованное TCP-соединение**, они могут начать **обмен данными по протоколу HTTP**.
|
||||
|
||||
Так клиент отправляет **HTTPS-запрос**. То есть обычный HTTP-запрос, но через зашифрованное TLS-содинение.
|
||||
|
||||
<img src="/img/deployment/https/https04.svg">
|
||||
|
||||
### Расшифровка запроса
|
||||
|
||||
Прокси-сервер, используя согласованный с клиентом ключ, расшифрует полученный **зашифрованный запрос** и передаст **обычный (незашифрованный) HTTP-запрос** процессу, запускающему приложение (например, процессу Uvicorn запускающему приложение FastAPI).
|
||||
|
||||
<img src="/img/deployment/https/https05.svg">
|
||||
|
||||
### HTTP-ответ
|
||||
|
||||
Приложение обработает запрос и вернёт **обычный (незашифрованный) HTTP-ответ** прокси-серверу.
|
||||
|
||||
<img src="/img/deployment/https/https06.svg">
|
||||
|
||||
### HTTPS-ответ
|
||||
|
||||
Пркоси-сервер **зашифрует ответ** используя ранее согласованный с клиентом способ шифрования (которые содержатся в сертификате для домена `someapp.example.com`) и отправит его браузеру.
|
||||
|
||||
Наконец, браузер проверит ответ, в том числе, что тот зашифрован с нужным ключом, **расшифрует его** и обработает.
|
||||
|
||||
<img src="/img/deployment/https/https07.svg">
|
||||
|
||||
Клиент (браузер) знает, что ответ пришёл от правильного сервера, так как использует методы шифрования, согласованные ими раннее через **HTTPS-сертификат**.
|
||||
|
||||
### Множество приложений
|
||||
|
||||
На одном и том же сервере (или серверах) можно разместить **множество приложений**, например, другие программы с API или базы данных.
|
||||
|
||||
Напомню, что только один процесс (например, прокси-сервер) может прослушивать определённый порт определённого IP-адреса.
|
||||
Но другие процессы и приложения тоже могут работать на этом же сервере (серверах), если они не пытаются использовать уже занятую **комбинацию IP-адреса и порта** (сокет).
|
||||
|
||||
<img src="/img/deployment/https/https08.svg">
|
||||
|
||||
Таким образом, сервер завершения TLS может обрабатывать HTTPS-запросы и использовать сертификаты для **множества доменов** или приложений и передавать запросы правильным адресатам (другим приложениям).
|
||||
|
||||
### Обновление сертификата
|
||||
|
||||
В недалёком будущем любой сертификат станет **просроченным** (примерно через три месяца после получения).
|
||||
|
||||
Когда это произойдёт, можно запустить другую программу, которая подключится к Let's Encrypt и обновит сертификат(ы). Существуют прокси-серверы, которые могут сделать это действие самостоятельно.
|
||||
|
||||
<img src="/img/deployment/https/https.svg">
|
||||
|
||||
**TLS-сертификаты** не привязаны к IP-адресу, но **связаны с именем домена**.
|
||||
|
||||
Так что при обновлении сертификатов программа должна **подтвердить** центру сертификации (Let's Encrypt), что обновление запросил **"владелец", который контролирует этот домен**.
|
||||
|
||||
Есть несколько путей осуществления этого. Самые популярные из них:
|
||||
|
||||
* **Изменение записей DNS**.
|
||||
* Для такого варианта Ваша программа обновления должна уметь работать с API DNS-провайдера, обслуживающего Ваши DNS-записи. Не у всех провайдеров есть такой API, так что этот способ не всегда применим.
|
||||
* **Запуск в качестве программы-сервера** (как минимум, на время обновления сертификатов) на публичном IP-адресе домена.
|
||||
* Как уже не раз упоминалось, только один процесс может прослушивать определённый порт определённого IP-адреса.
|
||||
* Это одна из причин использования прокси-сервера ещё и в качестве программы обновления сертификатов.
|
||||
* В случае, если обновлением сертификатов занимается другая программа, Вам понадобится остановить прокси-сервер, запустить программу обновления сертификатов на сокете, предназначенном для прокси-сервера, настроить прокси-сервер на работу с новыми сертификатами и перезапустить его. Эта схема далека от идеальной, так как Ваши приложения будут недоступны на время отключения прокси-сервера.
|
||||
|
||||
Весь этот процесс обновления, одновременный с обслуживанием запросов, является одной из основных причин, по которой желательно иметь **отдельную систему для работы с HTTPS** в виде прокси-сервера завершения TLS, а не просто использовать сертификаты TLS непосредственно с сервером приложений (например, Uvicorn).
|
||||
|
||||
## Резюме
|
||||
|
||||
Наличие **HTTPS** очень важно и довольно **критично** в большинстве случаев. Однако, Вам, как разработчику, не нужно тратить много сил на это, достаточно **понимать эти концепции** и принципы их работы.
|
||||
|
||||
Но узнав базовые основы **HTTPS** Вы можете легко совмещать разные инструменты, которые помогут Вам в дальнейшей разработке.
|
||||
|
||||
В следующих главах я покажу Вам несколько примеров, как настраивать **HTTPS** для приложений **FastAPI**. 🔒
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
# Запуск сервера вручную - Uvicorn
|
||||
|
||||
Для запуска приложения **FastAPI** на удалённой серверной машине Вам необходим программный сервер, поддерживающий протокол ASGI, такой как **Uvicorn**.
|
||||
|
||||
Существует три наиболее распространённые альтернативы:
|
||||
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>: высокопроизводительный ASGI сервер.
|
||||
* <a href="https://pgjones.gitlab.io/hypercorn/" class="external-link" target="_blank">Hypercorn</a>: ASGI сервер, помимо прочего поддерживающий HTTP/2 и Trio.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: ASGI сервер, созданный для Django Channels.
|
||||
|
||||
## Сервер как машина и сервер как программа
|
||||
|
||||
В этих терминах есть некоторые различия и Вам следует запомнить их. 💡
|
||||
|
||||
Слово "**сервер**" чаще всего используется в двух контекстах:
|
||||
|
||||
- удалённый или расположенный в "облаке" компьютер (физическая или виртуальная машина).
|
||||
- программа, запущенная на таком компьютере (например, Uvicorn).
|
||||
|
||||
Просто запомните, если Вам встретился термин "сервер", то обычно он подразумевает что-то из этих двух смыслов.
|
||||
|
||||
Когда имеют в виду именно удалённый компьютер, часто говорят просто **сервер**, но ещё его называют **машина**, **ВМ** (виртуальная машина), **нода**. Все эти термины обозначают одно и то же - удалённый компьютер, обычно под управлением Linux, на котором Вы запускаете программы.
|
||||
|
||||
## Установка программного сервера
|
||||
|
||||
Вы можете установить сервер, совместимый с протоколом ASGI, так:
|
||||
|
||||
=== "Uvicorn"
|
||||
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>, молниесный ASGI сервер, основанный на библиотеках uvloop и httptools.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "uvicorn[standard]"
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
!!! tip "Подсказка"
|
||||
С опцией `standard`, Uvicorn будет установливаться и использоваться с некоторыми дополнительными рекомендованными зависимостями.
|
||||
|
||||
В них входит `uvloop`, высокопроизводительная замена `asyncio`, которая значительно ускоряет работу асинхронных программ.
|
||||
|
||||
=== "Hypercorn"
|
||||
|
||||
* <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, ASGI сервер, поддерживающий протокол HTTP/2.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install hypercorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
...или какой-либо другой ASGI сервер.
|
||||
|
||||
## Запуск серверной программы
|
||||
|
||||
Затем запустите Ваше приложение так же, как было указано в руководстве ранее, но без опции `--reload`:
|
||||
|
||||
=== "Uvicorn"
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --host 0.0.0.0 --port 80
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
=== "Hypercorn"
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --bind 0.0.0.0:80
|
||||
|
||||
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
!!! warning "Предупреждение"
|
||||
|
||||
Не забудьте удалить опцию `--reload`, если ранее пользовались ею.
|
||||
|
||||
Включение опции `--reload` требует дополнительных ресурсов, влияет на стабильность работы приложения и может повлечь прочие неприятности.
|
||||
|
||||
Она сильно помогает во время **разработки**, но **не следует** использовать её при **реальной работе** приложения.
|
||||
|
||||
## Hypercorn с Trio
|
||||
|
||||
Starlette и **FastAPI** основаны на <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, которая делает их совместимыми как с <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> - стандартной библиотекой Python, так и с <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
|
||||
|
||||
Тем не менее Uvicorn совместим только с asyncio и обычно используется совместно с <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a>, высокопроизводительной заменой `asyncio`.
|
||||
|
||||
Но если Вы хотите использовать **Trio** напрямую, то можете воспользоваться **Hypercorn**, так как они совместимы. ✨
|
||||
|
||||
### Установка Hypercorn с Trio
|
||||
|
||||
Для начала, Вам нужно установить Hypercorn с поддержкой Trio:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "hypercorn[trio]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Запуск с Trio
|
||||
|
||||
Далее запустите Hypercorn с опцией `--worker-class` и аргументом `trio`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --worker-class trio
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Hypercorn, в свою очередь, запустит Ваше приложение использующее Trio.
|
||||
|
||||
Таким образом, Вы сможете использовать Trio в своём приложении. Но лучше использовать AnyIO, для сохранения совместимости и с Trio, и с asyncio. 🎉
|
||||
|
||||
## Концепции развёртывания
|
||||
|
||||
В вышеприведённых примерах серверные программы (например Uvicorn) запускали только **один процесс**, принимающий входящие запросы с любого IP (на это указывал аргумент `0.0.0.0`) на определённый порт (в примерах мы указывали порт `80`).
|
||||
|
||||
Это основная идея. Но возможно, Вы озаботитесь добавлением дополнительных возможностей, таких как:
|
||||
|
||||
* Использование более безопасного протокола HTTPS
|
||||
* Настройки запуска приложения
|
||||
* Перезагрузка приложения
|
||||
* Запуск нескольких экземпляров приложения
|
||||
* Управление памятью
|
||||
* Использование перечисленных функций перед запуском приложения.
|
||||
|
||||
Я поведаю Вам больше о каждой из этих концепций в следующих главах, с конкретными примерами стратегий работы с ними. 🚀
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
# Тестирование
|
||||
|
||||
Благодаря <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, тестировать приложения **FastAPI** легко и приятно.
|
||||
|
||||
Тестирование основано на библиотеке <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, которая в свою очередь основана на библиотеке Requests, так что все действия знакомы и интуитивно понятны.
|
||||
|
||||
Используя эти инструменты, Вы можете напрямую задействовать <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> с **FastAPI**.
|
||||
|
||||
## Использование класса `TestClient`
|
||||
|
||||
!!! info "Информация"
|
||||
Для использования класса `TestClient` необходимо установить библиотеку <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
|
||||
|
||||
Например, так: `pip install httpx`.
|
||||
|
||||
Импортируйте `TestClient`.
|
||||
|
||||
Создайте объект `TestClient`, передав ему в качестве параметра Ваше приложение **FastAPI**.
|
||||
|
||||
Создайте функцию, название которой должно начинаться с `test_` (это стандарт из соглашений `pytest`).
|
||||
|
||||
Используйте объект `TestClient` так же, как Вы используете `httpx`.
|
||||
|
||||
Напишите простое утверждение с `assert` дабы проверить истинность Python-выражения (это тоже стандарт `pytest`).
|
||||
|
||||
```Python hl_lines="2 12 15-18"
|
||||
{!../../../docs_src/app_testing/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Обратите внимание, что тестирующая функция является обычной `def`, а не асинхронной `async def`.
|
||||
|
||||
И вызов клиента также осуществляется без `await`.
|
||||
|
||||
Это позволяет вам использовать `pytest` без лишних усложнений.
|
||||
|
||||
!!! note "Технические детали"
|
||||
Также можно написать `from starlette.testclient import TestClient`.
|
||||
|
||||
**FastAPI** предоставляет тот же самый `starlette.testclient` как `fastapi.testclient`. Это всего лишь небольшое удобство для Вас, как разработчика.
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Если для тестирования Вам, помимо запросов к приложению FastAPI, необходимо вызывать асинхронные функции (например, для подключения к базе данных с помощью асинхронного драйвера), то ознакомьтесь со страницей [Асинхронное тестирование](../advanced/async-tests.md){.internal-link target=_blank} в расширенном руководстве.
|
||||
|
||||
## Разделение тестов и приложения
|
||||
|
||||
В реальном приложении Вы, вероятно, разместите тесты в отдельном файле.
|
||||
|
||||
Кроме того, Ваше приложение **FastAPI** может состоять из нескольких файлов, модулей и т.п.
|
||||
|
||||
### Файл приложения **FastAPI**
|
||||
|
||||
Допустим, структура файлов Вашего приложения похожа на ту, что описана на странице [Более крупные приложения](./bigger-applications.md){.internal-link target=_blank}:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ └── main.py
|
||||
```
|
||||
|
||||
Здесь файл `main.py` является "точкой входа" в Ваше приложение и содержит инициализацию Вашего приложения **FastAPI**:
|
||||
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/app_testing/main.py!}
|
||||
```
|
||||
|
||||
### Файл тестов
|
||||
|
||||
Также у Вас может быть файл `test_main.py` содержащий тесты. Можно разместить тестовый файл и файл приложения в одной директории (в директориях для Python-кода желательно размещать и файл `__init__.py`):
|
||||
|
||||
``` hl_lines="5"
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/app_testing/test_main.py!}
|
||||
```
|
||||
|
||||
...и писать дальше тесты, как и раньше.
|
||||
|
||||
## Тестирование: расширенный пример
|
||||
|
||||
Теперь давайте расширим наш пример и добавим деталей, чтоб посмотреть, как тестировать различные части приложения.
|
||||
|
||||
### Расширенный файл приложения **FastAPI**
|
||||
|
||||
Мы продолжим работу с той же файловой структурой, что и ранее:
|
||||
|
||||
```
|
||||
.
|
||||
├── app
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py
|
||||
│ └── test_main.py
|
||||
```
|
||||
|
||||
Предположим, что в файле `main.py` с приложением **FastAPI** есть несколько **операций пути**.
|
||||
|
||||
В нём описана операция `GET`, которая может вернуть ошибку.
|
||||
|
||||
Ещё есть операция `POST` и она тоже может вернуть ошибку.
|
||||
|
||||
Обе *операции пути* требуют наличия в запросе заголовка `X-Token`.
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_an_py310/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_an_py39/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_an/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10+ без Annotated"
|
||||
|
||||
!!! tip "Подсказка"
|
||||
По возможности используйте версию с `Annotated`.
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+ без Annotated"
|
||||
|
||||
!!! tip "Подсказка"
|
||||
По возможности используйте версию с `Annotated`.
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b/main.py!}
|
||||
```
|
||||
|
||||
### Расширенный файл тестов
|
||||
|
||||
Теперь обновим файл `test_main.py`, добавив в него тестов:
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/app_testing/app_b/test_main.py!}
|
||||
```
|
||||
|
||||
Если Вы не знаете, как передать информацию в запросе, можете воспользоваться поисковиком (погуглить) и задать вопрос: "Как передать информацию в запросе с помощью `httpx`", можно даже спросить: "Как передать информацию в запросе с помощью `requests`", поскольку дизайн HTTPX основан на дизайне Requests.
|
||||
|
||||
Затем Вы просто применяете найденные ответы в тестах.
|
||||
|
||||
Например:
|
||||
|
||||
* Передаёте *path*-параметры или *query*-параметры, вписав их непосредственно в строку URL.
|
||||
* Передаёте JSON в теле запроса, передав Python-объект (например: `dict`) через именованный параметр `json`.
|
||||
* Если же Вам необходимо отправить *форму с данными* вместо JSON, то используйте параметр `data` вместо `json`.
|
||||
* Для передачи *заголовков*, передайте объект `dict` через параметр `headers`.
|
||||
* Для передачи *cookies* также передайте `dict`, но через параметр `cookies`.
|
||||
|
||||
Для получения дополнительной информации о передаче данных на бэкенд с помощью `httpx` или `TestClient` ознакомьтесь с <a href="https://www.python-httpx.org" class="external-link" target="_blank">документацией HTTPX</a>.
|
||||
|
||||
!!! info "Информация"
|
||||
Обратите внимание, что `TestClient` принимает данные, которые можно конвертировать в JSON, но не модели Pydantic.
|
||||
|
||||
Если в Ваших тестах есть модели Pydantic и Вы хотите отправить их в тестируемое приложение, то можете использовать функцию `jsonable_encoder`, описанную на странице [Кодировщик совместимый с JSON](encoder.md){.internal-link target=_blank}.
|
||||
|
||||
## Запуск тестов
|
||||
|
||||
Далее Вам нужно установить `pytest`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install pytest
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Он автоматически найдёт все файлы и тесты, выполнит их и предоставит Вам отчёт о результатах тестирования.
|
||||
|
||||
Запустите тесты командой `pytest` и увидите результат:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pytest
|
||||
|
||||
================ test session starts ================
|
||||
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
|
||||
rootdir: /home/user/code/superawesome-cli/app
|
||||
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
|
||||
collected 6 items
|
||||
|
||||
---> 100%
|
||||
|
||||
test_main.py <span style="color: green; white-space: pre;">...... [100%]</span>
|
||||
|
||||
<span style="color: green;">================= 1 passed in 0.03s =================</span>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -71,11 +72,14 @@ nav:
|
|||
- tutorial/background-tasks.md
|
||||
- tutorial/extra-data-types.md
|
||||
- tutorial/cookie-params.md
|
||||
- tutorial/testing.md
|
||||
- tutorial/response-status-code.md
|
||||
- async.md
|
||||
- Развёртывание:
|
||||
- deployment/index.md
|
||||
- deployment/versions.md
|
||||
- deployment/https.md
|
||||
- deployment/manually.md
|
||||
- project-generation.md
|
||||
- alternatives.md
|
||||
- history-design-future.md
|
||||
|
|
@ -150,6 +154,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -134,6 +135,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -129,6 +130,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ nav:
|
|||
- it: /it/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- lo: /lo/
|
||||
- nl: /nl/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
|
|
@ -186,6 +187,8 @@ extra:
|
|||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
name: ko - 한국어
|
||||
- link: /lo/
|
||||
name: lo - ພາສາລາວ
|
||||
- link: /nl/
|
||||
name: nl
|
||||
- link: /pl/
|
||||
|
|
|
|||
Loading…
Reference in New Issue