mirror of
https://github.com/fastapi/fastapi.git
synced 2026-02-12 23:31:04 -05:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fa573ce0b | ||
|
|
55a9eee13a | ||
|
|
c206f19b5d | ||
|
|
d11f820ac3 |
@@ -7,6 +7,14 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Update docs for JWT to prevent timing attacks. PR [#14908](https://github.com/fastapi/fastapi/pull/14908) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Update translations for ru (update-outdated). PR [#14909](https://github.com/fastapi/fastapi/pull/14909) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
||||
## 0.129.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -116,7 +116,11 @@ And another utility to verify if a received password matches the hash stored.
|
||||
|
||||
And another one to authenticate and return a user.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
|
||||
|
||||
When `authenticate_user` is called with a username that doesn't exist in the database, we still run `verify_password` against a dummy hash.
|
||||
|
||||
This ensures the endpoint takes roughly the same amount of time to respond whether the username is valid or not, preventing **timing attacks** that could be used to enumerate existing usernames.
|
||||
|
||||
/// note
|
||||
|
||||
@@ -152,7 +156,7 @@ Define a Pydantic Model that will be used in the token endpoint for the response
|
||||
|
||||
Create a utility function to generate a new access token.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
|
||||
|
||||
## Update the dependencies { #update-the-dependencies }
|
||||
|
||||
@@ -162,7 +166,7 @@ Decode the received token, verify it, and return the current user.
|
||||
|
||||
If the token is invalid, return an HTTP error right away.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
|
||||
|
||||
## Update the `/token` *path operation* { #update-the-token-path-operation }
|
||||
|
||||
@@ -170,7 +174,7 @@ Create a `timedelta` with the expiration time of the token.
|
||||
|
||||
Create a real JWT access token and return it.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
|
||||
|
||||
### Technical details about the JWT "subject" `sub` { #technical-details-about-the-jwt-subject-sub }
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
Например, чтобы объявить ещё один ответ со статус-кодом `404` и Pydantic-моделью `Message`, можно написать:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
|
||||
А также ответ со статус-кодом `200`, который использует ваш `response_model`, но включает пользовательский `example`:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
|
||||
|
||||
Всё это будет объединено и включено в ваш OpenAPI и отображено в документации API:
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
Для этого объявляем метод `__call__`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
В этом случае именно `__call__` **FastAPI** использует для проверки дополнительных параметров и подзависимостей, и именно он будет вызван, чтобы позже передать значение параметру в вашей *функции-обработчике пути*.
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
Теперь мы можем использовать `__init__`, чтобы объявить параметры экземпляра, с помощью которых будем «параметризовать» зависимость:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
В этом случае **FastAPI** вовсе не трогает `__init__` и не зависит от него — мы используем его напрямую в нашем коде.
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
Мы можем создать экземпляр этого класса так:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
Так мы «параметризуем» нашу зависимость: теперь внутри неё хранится "bar" в атрибуте `checker.fixed_content`.
|
||||
|
||||
@@ -50,7 +50,7 @@ checker(q="somequery")
|
||||
|
||||
…и передаст возвращённое значение как значение зависимости в параметр `fixed_content_included` нашей *функции-обработчика пути*:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
|
||||
61
docs/ru/docs/advanced/advanced-python-types.md
Normal file
61
docs/ru/docs/advanced/advanced-python-types.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Продвинутые типы Python { #advanced-python-types }
|
||||
|
||||
Ниже несколько дополнительных идей, которые могут быть полезны при работе с типами Python.
|
||||
|
||||
## Использование `Union` или `Optional` { #using-union-or-optional }
|
||||
|
||||
Если по какой-то причине ваш код не может использовать `|`, например, если это не аннотация типов, а что-то вроде `response_model=`, вместо вертикальной черты (`|`) можно использовать `Union` из `typing`.
|
||||
|
||||
Например, вы можете объявить, что значение может быть `str` или `None`:
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
В `typing` также есть сокращение, чтобы объявить, что значение может быть `None`, — `Optional`.
|
||||
|
||||
Вот совет с моей очень субъективной точки зрения:
|
||||
|
||||
- 🚨 Избегайте использования `Optional[SomeType]`
|
||||
- Вместо этого ✨ используйте **`Union[SomeType, None]`** ✨.
|
||||
|
||||
Оба варианта эквивалентны и под капотом это одно и то же, но я бы рекомендовал `Union` вместо `Optional`, потому что слово «optional» может наводить на мысль, что значение необязательное, тогда как на самом деле это означает «значение может быть `None`», даже если оно не является необязательным и по-прежнему требуется.
|
||||
|
||||
По-моему, `Union[SomeType, None]` более явно передаёт смысл.
|
||||
|
||||
Речь только о словах и названиях. Но эти слова могут влиять на то, как вы и ваша команда думаете о коде.
|
||||
|
||||
В качестве примера возьмём такую функцию:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Параметр `name` объявлен как `Optional[str]`, но он не является необязательным: вы не можете вызвать функцию без этого параметра:
|
||||
|
||||
```Python
|
||||
say_hi() # О нет, это вызывает ошибку! 😱
|
||||
```
|
||||
|
||||
Параметр `name` по-прежнему обязателен (не «optional»), так как у него нет значения по умолчанию. При этом `name` принимает `None` в качестве значения:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Это работает, None допустим 🎉
|
||||
```
|
||||
|
||||
Хорошая новость: в большинстве случаев вы сможете просто использовать `|` для объявления объединений типов:
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
Так что обычно вам не о чем переживать из‑за названий вроде `Optional` и `Union`. 😎
|
||||
@@ -32,11 +32,11 @@
|
||||
|
||||
Файл `main.py`:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
Файл `test_main.py` содержит тесты для `main.py`, теперь он может выглядеть так:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## Запуск тестов { #run-it }
|
||||
|
||||
@@ -56,7 +56,7 @@ $ pytest
|
||||
|
||||
Маркер `@pytest.mark.anyio` говорит pytest, что тестовая функция должна быть вызвана асинхронно:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -66,7 +66,7 @@ $ pytest
|
||||
|
||||
Затем мы можем создать `AsyncClient` со ссылкой на приложение и посылать асинхронные запросы, используя `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
Это эквивалентно следующему:
|
||||
|
||||
@@ -94,6 +94,6 @@ response = client.get('/')
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Если вы столкнулись с `RuntimeError: Task attached to a different loop` при вызове асинхронных функций в ваших тестах (например, при использовании <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB's MotorClient</a>), то не забывайте инициализировать объекты, которым нужен цикл событий (event loop), только внутри асинхронных функций, например, в `'@app.on_event("startup")` callback.
|
||||
Если вы столкнулись с `RuntimeError: Task attached to a different loop` при вызове асинхронных функций в ваших тестах (например, при использовании <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB's MotorClient</a>), то не забывайте инициализировать объекты, которым нужен цикл событий (event loop), только внутри асинхронных функций, например, в `@app.on_event("startup")` callback.
|
||||
|
||||
///
|
||||
|
||||
@@ -44,7 +44,7 @@ $ fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
Например, вы объявили операцию пути `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
Если клиент обратится к `/items`, по умолчанию произойдёт редирект на `/items/`.
|
||||
|
||||
@@ -115,7 +115,7 @@ sequenceDiagram
|
||||
|
||||
Хотя весь ваш код написан с расчётом, что путь один — `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
Прокси будет «обрезать» префикс пути на лету перед передачей запроса на сервер приложения (скорее всего Uvicorn, запущенный через FastAPI CLI), поддерживая у вашего приложения иллюзию, что его обслуживают по `/app`, чтобы вам не пришлось менять весь код и добавлять префикс `/api/v1`.
|
||||
|
||||
@@ -193,7 +193,7 @@ $ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
Здесь мы добавляем его в сообщение лишь для демонстрации.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Затем, если вы запустите Uvicorn так:
|
||||
|
||||
@@ -220,7 +220,7 @@ $ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
Если нет возможности передать опцию командной строки `--root-path` (или аналог), вы можете указать параметр `root_path` при создании приложения FastAPI:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Передача `root_path` в `FastAPI` эквивалентна опции командной строки `--root-path` для Uvicorn или Hypercorn.
|
||||
|
||||
@@ -241,17 +241,17 @@ $ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
Uvicorn ожидает, что прокси обратится к нему по `http://127.0.0.1:8000/app`, а уже задача прокси — добавить сверху префикс `/api/v1`.
|
||||
|
||||
## О прокси с урезанным префиксом пути { #about-proxies-with-a-stripped-path-prefix }
|
||||
## О прокси с функцией удаления префикса пути { #about-proxies-with-a-stripped-path-prefix }
|
||||
|
||||
Помните, что прокси с урезанным префиксом пути — лишь один из вариантов настройки.
|
||||
Помните, что прокси с функцией удаления префикса пути — лишь один из вариантов настройки.
|
||||
|
||||
Во многих случаях по умолчанию прокси будет без урезанного префикса пути.
|
||||
Во многих случаях по умолчанию прокси будет без функции удаления префикса пути.
|
||||
|
||||
В таком случае (без урезанного префикса) прокси слушает, например, по адресу `https://myawesomeapp.com`, и если браузер идёт на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (например, Uvicorn) слушает на `http://127.0.0.1:8000`, то прокси (без урезанного префикса) обратится к Uvicorn по тому же пути: `http://127.0.0.1:8000/api/v1/app`.
|
||||
В таком случае (без функции удаления префикса пути) прокси слушает, например, по адресу `https://myawesomeapp.com`, и если браузер идёт на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (например, Uvicorn) слушает на `http://127.0.0.1:8000`, то прокси (без урезанного префикса) обратится к Uvicorn по тому же пути: `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
## Локальное тестирование с Traefik { #testing-locally-with-traefik }
|
||||
|
||||
Вы можете легко поэкспериментировать локально с урезанным префиксом пути, используя <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
|
||||
Вы можете легко поэкспериментировать локально с функцией удаления префикса пути, используя <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> — это один бинарный файл; распакуйте архив и запустите его прямо из терминала.
|
||||
|
||||
@@ -400,7 +400,7 @@ $ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
Например:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
Будет сгенерирована схема OpenAPI примерно такая:
|
||||
|
||||
@@ -455,7 +455,7 @@ $ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
Если вы не хотите, чтобы FastAPI добавлял автоматический сервер, используя `root_path`, укажите параметр `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
и тогда этот сервер не будет добавлен в схему OpenAPI.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
Но если вы уверены, что содержимое, которое вы возвращаете, **сериализуемо в JSON**, вы можете передать его напрямую в класс ответа и избежать дополнительных накладных расходов, которые FastAPI понёс бы, пропуская возвращаемое содержимое через `jsonable_encoder` перед передачей в класс ответа.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Информация
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
- Импортируйте `HTMLResponse`.
|
||||
- Передайте `HTMLResponse` в параметр `response_class` вашего декоратора операции пути.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info | Информация
|
||||
|
||||
@@ -73,17 +73,17 @@
|
||||
|
||||
Тот же пример сверху, возвращающий `HTMLResponse`, может выглядеть так:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Предупреждение
|
||||
|
||||
`Response`, возвращённый напрямую вашей функцией-обработчиком пути, не будет задокументирован в OpenAPI (например, `Content-Type` нне будет задокументирова) и не будет виден в автоматически сгенерированной интерактивной документации.
|
||||
`Response`, возвращённый напрямую вашей функцией-обработчиком пути, не будет задокументирован в OpenAPI (например, `Content-Type` не будет задокументирован) и не будет виден в автоматически сгенерированной интерактивной документации.
|
||||
|
||||
///
|
||||
|
||||
/// info | Информация
|
||||
|
||||
Разумеется, фактические заголовок `Content-Type`, статус-код и т.д. возьмутся из объекта `Response`, который вы вернули.
|
||||
Разумеется, фактический заголовок `Content-Type`, статус-код и т.д. возьмутся из объекта `Response`, который вы вернули.
|
||||
|
||||
///
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
Например, это может быть что-то вроде:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
В этом примере функция `generate_html_response()` уже генерирует и возвращает `Response` вместо возврата HTML в `str`.
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
|
||||
FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Также будет добавлен заголовок Content-Type, основанный на `media_type` и с добавлением charset для текстовых типов.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
@@ -146,7 +146,7 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
Принимает текст или байты и возвращает ответ в виде простого текста.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
@@ -180,7 +180,7 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -194,13 +194,13 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
Вы можете вернуть `RedirectResponse` напрямую:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
Или можно использовать его в параметре `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
Если вы сделаете так, то сможете возвращать URL напрямую из своей функции-обработчика пути.
|
||||
|
||||
@@ -210,13 +210,13 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
Также вы можете использовать параметр `status_code` в сочетании с параметром `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа потоково.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### Использование `StreamingResponse` с файлоподобными объектами { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
@@ -226,7 +226,7 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
Это включает многие библиотеки для работы с облачным хранилищем, обработки видео и т.д.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. Это функция-генератор. Она является «функцией-генератором», потому что содержит оператор(ы) `yield` внутри.
|
||||
2. Используя блок `with`, мы гарантируем, что файлоподобный объект будет закрыт после завершения работы функции-генератора. То есть после того, как она закончит отправку ответа.
|
||||
@@ -255,11 +255,11 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
Файловые ответы будут содержать соответствующие заголовки `Content-Length`, `Last-Modified` и `ETag`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
Вы также можете использовать параметр `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.
|
||||
|
||||
@@ -273,7 +273,7 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
Вы могли бы создать `CustomORJSONResponse`. Главное, что вам нужно сделать — реализовать метод `Response.render(content)`, который возвращает содержимое как `bytes`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
Теперь вместо того, чтобы возвращать:
|
||||
|
||||
@@ -299,7 +299,7 @@ FastAPI (фактически Starlette) автоматически добави
|
||||
|
||||
В примере ниже **FastAPI** будет использовать `ORJSONResponse` по умолчанию во всех операциях пути вместо `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ FastAPI построен поверх **Pydantic**, и я показывал в
|
||||
|
||||
6. Здесь мы возвращаем словарь, содержащий `items`, который является списком dataclass.
|
||||
|
||||
FastAPI по-прежнему способен <abbr title="преобразование данных в формат, который можно передавать">сериализовать</abbr> данные в JSON.
|
||||
FastAPI по-прежнему способен <dfn title="преобразование данных в формат, который можно передавать">сериализовать</dfn> данные в JSON.
|
||||
|
||||
7. Здесь `response_model` использует аннотацию типа — список dataclass `Author`.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
Мы создаём асинхронную функцию `lifespan()` с `yield` примерно так:
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
Здесь мы симулируем дорогую операцию startup по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен до того, как приложение начнет принимать запросы, во время startup.
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
Первое, на что стоит обратить внимание, — мы определяем асинхронную функцию с `yield`. Это очень похоже на Зависимости с `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
Первая часть функции, до `yield`, будет выполнена до запуска приложения.
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
Это превращает функцию в «асинхронный менеджер контекста».
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
Менеджер контекста в Python — это то, что можно использовать в операторе `with`. Например, `open()` можно использовать как менеджер контекста:
|
||||
|
||||
@@ -82,7 +82,7 @@ async with lifespan(app):
|
||||
|
||||
Параметр `lifespan` приложения `FastAPI` принимает асинхронный менеджер контекста, поэтому мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## Альтернативные события (устаревшие) { #alternative-events-deprecated }
|
||||
|
||||
@@ -104,7 +104,7 @@ async with lifespan(app):
|
||||
|
||||
Чтобы добавить функцию, которую нужно запустить до старта приложения, объявите её как обработчик события `"startup"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
В этом случае функция-обработчик события `startup` инициализирует «базу данных» items (это просто `dict`) некоторыми значениями.
|
||||
|
||||
@@ -116,7 +116,7 @@ async with lifespan(app):
|
||||
|
||||
Чтобы добавить функцию, которую нужно запустить при завершении работы приложения, объявите её как обработчик события `"shutdown"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
Здесь функция-обработчик события `shutdown` запишет строку текста `"Application shutdown"` в файл `log.txt`.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Поскольку **FastAPI** основан на спецификации **OpenAPI**, его API можно описать в стандартном формате, понятном множеству инструментов.
|
||||
|
||||
Это упрощает генерацию актуальной **документации**, клиентских библиотек (<abbr title="Software Development Kits – Наборы средств разработки">**SDKs**</abbr>) на разных языках, а также **тестирования** или **воркфлоу автоматизации**, которые остаются синхронизированными с вашим кодом.
|
||||
Это упрощает генерацию актуальной **документации**, клиентских библиотек (<abbr title="Software Development Kits - Наборы средств разработки">**SDKs**</abbr>) на разных языках, а также **тестирования** или **воркфлоу автоматизации**, которые остаются синхронизированными с вашим кодом.
|
||||
|
||||
В этом руководстве вы узнаете, как сгенерировать **TypeScript SDK** для вашего бэкенда на FastAPI.
|
||||
|
||||
@@ -40,7 +40,7 @@ FastAPI автоматически генерирует спецификации
|
||||
|
||||
Начнём с простого приложения FastAPI:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Обратите внимание, что *операции пути (обработчики пути)* определяют модели, которые они используют для полезной нагрузки запроса и полезной нагрузки ответа, с помощью моделей `Item` и `ResponseMessage`.
|
||||
|
||||
@@ -98,7 +98,7 @@ npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
||||
|
||||
Например, у вас может быть раздел для **items** и другой раздел для **users**, и они могут быть разделены тегами:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### Генерация TypeScript‑клиента с тегами { #generate-a-typescript-client-with-tags }
|
||||
|
||||
@@ -145,7 +145,7 @@ FastAPI использует **уникальный ID** для каждой *о
|
||||
|
||||
Затем вы можете передать эту пользовательскую функцию в **FastAPI** через параметр `generate_unique_id_function`:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### Генерация TypeScript‑клиента с пользовательскими ID операций { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
@@ -157,7 +157,7 @@ FastAPI использует **уникальный ID** для каждой *о
|
||||
|
||||
### Предобработка спецификации OpenAPI для генератора клиента { #preprocess-the-openapi-specification-for-the-client-generator }
|
||||
|
||||
Сгенерированном коде всё ещё есть **дублирующаяся информация**.
|
||||
В сгенерированном коде всё ещё есть **дублирующаяся информация**.
|
||||
|
||||
Мы уже знаем, что этот метод относится к **items**, потому что это слово есть в `ItemsService` (взято из тега), но при этом имя тега всё ещё добавлено префиксом к имени метода. 😕
|
||||
|
||||
@@ -167,7 +167,7 @@ FastAPI использует **уникальный ID** для каждой *о
|
||||
|
||||
Мы можем скачать OpenAPI JSON в файл `openapi.json`, а затем **убрать этот префикс‑тег** таким скриптом:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
|
||||
@@ -57,13 +57,13 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
|
||||
Любой входящий запрос по `http` или `ws` будет перенаправлен на безопасную схему.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
Гарантирует, что во всех входящих запросах корректно установлен `Host`‑заголовок, чтобы защититься от атак на HTTP‑заголовок Host.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
|
||||
|
||||
Поддерживаются следующие аргументы:
|
||||
|
||||
@@ -78,7 +78,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
|
||||
Это middleware обрабатывает как обычные, так и потоковые ответы.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
|
||||
|
||||
Поддерживаются следующие аргументы:
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
Вся логика регистрации URL-адресов для вебхуков и код, который реально отправляет эти запросы, целиком на вашей стороне. Вы пишете это так, как вам нужно, в своем собственном коде.
|
||||
|
||||
## Документирование вебхуков с помощью FastAPI и OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
|
||||
## Документирование вебхуков с помощью **FastAPI** и OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
|
||||
|
||||
С FastAPI, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP-операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), а также тела запросов, которые ваше приложение будет отправлять.
|
||||
С **FastAPI**, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP-операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), а также тела запросов, которые ваше приложение будет отправлять.
|
||||
|
||||
Это значительно упростит вашим пользователям реализацию их API для приема ваших вебхук-запросов; возможно, они даже смогут автоматически сгенерировать часть кода своего API.
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
При создании приложения на **FastAPI** есть атрибут `webhooks`, с помощью которого можно объявлять вебхуки так же, как вы объявляете операции пути (обработчики пути), например с `@app.webhooks.post()`.
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
|
||||
|
||||
Определенные вами вебхуки попадут в схему **OpenAPI** и в автоматический **интерфейс документации**.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
Нужно убедиться, что он уникален для каждой операции.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
|
||||
|
||||
### Использование имени *функции-обработчика пути* как operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
Делать это следует после добавления всех *операций пути*.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматических систем документации), используйте параметр `include_in_schema` и установите его в `False`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
|
||||
|
||||
## Расширенное описание из docstring { #advanced-description-from-docstring }
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
`openapi_extra` может пригодиться, например, чтобы объявить [Расширения OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
|
||||
|
||||
Если вы откроете автоматическую документацию API, ваше расширение появится внизу страницы конкретной *операции пути*.
|
||||
|
||||
@@ -139,9 +139,9 @@
|
||||
|
||||
Это можно сделать с помощью `openapi_extra`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
|
||||
|
||||
В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не <abbr title="преобразовано из простого формата, например байтов, в объекты Python">распарсено</abbr> как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
|
||||
В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не <dfn title="преобразован из простого формата, например байтов, в объекты Python">распарсено</dfn> как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
|
||||
|
||||
Тем не менее, мы можем объявить ожидаемую схему для тела запроса.
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
|
||||
Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого HTTP-запроса как YAML, а не JSON:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
|
||||
|
||||
Тем не менее, хотя мы не используем встроенную функциональность по умолчанию, мы всё равно используем Pydantic-модель, чтобы вручную сгенерировать JSON Schema для данных, которые мы хотим получить в YAML.
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
А затем в нашем коде мы напрямую парсим это содержимое YAML и снова используем ту же Pydantic-модель, чтобы валидировать YAML-содержимое:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
И затем вы можете установить `status_code` в этом *временном* объекте ответа.
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
|
||||
|
||||
После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.).
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
Затем установить cookies в этом временном объекте ответа.
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
|
||||
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
|
||||
|
||||
После этого можно вернуть любой объект, как и раньше (например, `dict`, объект модели базы данных и так далее).
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
Затем установите cookies и верните этот объект:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
Вы можете поместить ваш XML-контент в строку, поместить её в `Response` и вернуть:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
## Примечания { #notes }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
А затем вы можете устанавливать HTTP-заголовки в этом *временном* объекте ответа.
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
|
||||
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
|
||||
|
||||
После этого вы можете вернуть любой нужный объект, как обычно (например, `dict`, модель из базы данных и т.д.).
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}, и передайте заголовки как дополнительный параметр:
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Она возвращает объект типа `HTTPBasicCredentials`:
|
||||
* Он содержит отправленные `username` и `password`.
|
||||
|
||||
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
|
||||
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
|
||||
|
||||
Когда вы впервые откроете URL (или нажмёте кнопку «Execute» в документации), браузер попросит ввести имя пользователя и пароль:
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
Затем можно использовать `secrets.compare_digest()`, чтобы убедиться, что `credentials.username` равен `"stanleyjobson"`, а `credentials.password` — `"swordfish"`.
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
|
||||
|
||||
Это было бы похоже на:
|
||||
|
||||
@@ -104,4 +104,4 @@ Pythonу придётся сравнить весь общий префикс `s
|
||||
|
||||
После того как обнаружено, что учётные данные некорректны, верните `HTTPException` со статус-кодом ответа 401 (тем же, что и при отсутствии учётных данных) и добавьте HTTP-заголовок `WWW-Authenticate`, чтобы браузер снова показал окно входа:
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
|
||||
|
||||
@@ -54,7 +54,7 @@ $ pip install "fastapi[all]"
|
||||
|
||||
Вы можете использовать все те же возможности валидации и инструменты, что и для Pydantic‑моделей, например разные типы данных и дополнительную валидацию через `Field()`.
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -70,7 +70,7 @@ $ pip install "fastapi[all]"
|
||||
|
||||
Затем вы можете использовать новый объект `settings` в вашем приложении:
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
|
||||
|
||||
### Запуск сервера { #run-the-server }
|
||||
|
||||
@@ -104,11 +104,11 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
||||
|
||||
Например, у вас может быть файл `config.py` со следующим содержимым:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/config.py *}
|
||||
{* ../../docs_src/settings/app01_py310/config.py *}
|
||||
|
||||
А затем использовать его в файле `main.py`:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
|
||||
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -126,7 +126,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
||||
|
||||
Продолжая предыдущий пример, ваш файл `config.py` может выглядеть так:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
|
||||
|
||||
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`.
|
||||
|
||||
@@ -134,7 +134,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
||||
|
||||
Теперь мы создаем зависимость, которая возвращает новый `config.Settings()`.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -146,13 +146,13 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
||||
|
||||
Затем мы можем запросить ее в *функции-обработчике пути* как зависимость и использовать там, где нужно.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
|
||||
|
||||
### Настройки и тестирование { #settings-and-testing }
|
||||
|
||||
Далее будет очень просто предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `get_settings`:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
|
||||
|
||||
В переопределении зависимости мы задаем новое значение `admin_email` при создании нового объекта `Settings`, а затем возвращаем этот новый объект.
|
||||
|
||||
@@ -193,7 +193,7 @@ APP_NAME="ChimichangApp"
|
||||
|
||||
Затем обновите ваш `config.py` так:
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -226,7 +226,7 @@ def get_settings():
|
||||
|
||||
Но так как мы используем декоратор `@lru_cache` сверху, объект `Settings` будет создан только один раз — при первом вызове. ✔️
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
|
||||
|
||||
Затем при любых последующих вызовах `get_settings()` в зависимостях для следующих запросов, вместо выполнения внутреннего кода `get_settings()` и создания нового объекта `Settings`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
Сначала создайте основное, верхнего уровня, приложение **FastAPI** и его *операции пути*:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
|
||||
|
||||
### Подприложение { #sub-application }
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
Это подприложение — обычное стандартное приложение FastAPI, но именно оно будет «смонтировано»:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
|
||||
|
||||
### Смонтируйте подприложение { #mount-the-sub-application }
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
В этом случае оно будет смонтировано по пути `/subapi`:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
|
||||
|
||||
### Проверьте автоматическую документацию API { #check-the-automatic-api-docs }
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ $ pip install jinja2
|
||||
- Объявите параметр `Request` в *операции пути*, которая будет возвращать шаблон.
|
||||
- Используйте созданный `templates`, чтобы отрендерить и вернуть `TemplateResponse`; передайте имя шаблона, объект `request` и словарь «context» с парами ключ-значение для использования внутри шаблона Jinja2.
|
||||
|
||||
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
|
||||
{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
Если вам нужно, чтобы `lifespan` выполнялся в ваших тестах, вы можете использовать `TestClient` вместе с оператором `with`:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
|
||||
|
||||
Вы можете узнать больше подробностей в статье [Запуск lifespan в тестах на официальном сайте документации Starlette.](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
|
||||
|
||||
Для устаревших событий `startup` и `shutdown` вы можете использовать `TestClient` следующим образом:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
|
||||
{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Для этого используйте `TestClient` с менеджером контекста `with`, подключаясь к WebSocket:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
|
||||
{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
Для этого нужно обратиться к запросу напрямую.
|
||||
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
|
||||
|
||||
Если объявить параметр *функции-обработчика пути* с типом `Request`, **FastAPI** поймёт, что нужно передать объект `Request` в этот параметр.
|
||||
|
||||
|
||||
@@ -38,13 +38,13 @@ $ pip install websockets
|
||||
|
||||
Для примера нам нужен наиболее простой способ, который позволит сосредоточиться на серверной части веб‑сокетов и получить рабочий код:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
|
||||
|
||||
## Создание `websocket` { #create-a-websocket }
|
||||
|
||||
Создайте `websocket` в своем **FastAPI** приложении:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
@@ -58,7 +58,7 @@ $ pip install websockets
|
||||
|
||||
Через эндпоинт веб-сокета вы можете получать и отправлять сообщения.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
|
||||
|
||||
Вы можете получать и отправлять двоичные, текстовые и JSON данные.
|
||||
|
||||
@@ -154,7 +154,7 @@ $ fastapi dev main.py
|
||||
|
||||
Если веб-сокет соединение закрыто, то `await websocket.receive_text()` вызовет исключение `WebSocketDisconnect`, которое можно поймать и обработать как в этом примере:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
|
||||
{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
|
||||
|
||||
Чтобы воспроизвести пример:
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
После этого смонтируйте его на путь.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ def read_url():
|
||||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
|
||||
|
||||
Одна из основных возможностей, нужных системам API, — «<abbr title="также называемая маршаллингом или преобразованием">сериализация</abbr>» данных, то есть преобразование данных из кода (Python) во что-то, что можно отправить по сети. Например, преобразование объекта с данными из базы в JSON-объект. Преобразование объектов `datetime` в строки и т. п.
|
||||
Одна из основных возможностей, нужных системам API, — «<dfn title="также называемая маршаллингом, преобразованием">сериализация</dfn>» данных, то есть преобразование данных из кода (Python) во что-то, что можно отправить по сети. Например, преобразование объекта с данными из базы в JSON-объект. Преобразование объектов `datetime` в строки и т. п.
|
||||
|
||||
Ещё одна важная возможность, востребованная API, — валидация данных: убеждаться, что данные валидны с учётом заданных параметров. Например, что какое-то поле — `int`, а не произвольная строка. Это особенно полезно для входящих данных.
|
||||
|
||||
@@ -145,7 +145,7 @@ def read_url():
|
||||
|
||||
Именно для этих возможностей и был создан Marshmallow. Это отличная библиотека, я много ей пользовался раньше.
|
||||
|
||||
Но она появилась до того, как в Python появились аннотации типов. Поэтому для определения каждой <abbr title="описание того, как данные должны быть сформированы">схемы</abbr> нужно использовать специальные утилиты и классы, предоставляемые Marshmallow.
|
||||
Но она появилась до того, как в Python появились аннотации типов. Поэтому для определения каждой <dfn title="определение того, как должны быть сформированы данные">схемы</dfn> нужно использовать специальные утилиты и классы, предоставляемые Marshmallow.
|
||||
|
||||
/// check | Вдохновило **FastAPI** на
|
||||
|
||||
@@ -155,7 +155,7 @@ def read_url():
|
||||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
|
||||
|
||||
Ещё одна важная возможность для API — <abbr title="чтение и преобразование данных в объекты Python">парсинг</abbr> данных из входящих HTTP-запросов.
|
||||
Ещё одна важная возможность для API — <dfn title="чтение и преобразование в данные Python">парсинг</dfn> данных из входящих HTTP-запросов.
|
||||
|
||||
Webargs — это инструмент, созданный для этого поверх нескольких фреймворков, включая Flask.
|
||||
|
||||
@@ -245,7 +245,7 @@ Flask-apispec был создан теми же разработчиками, ч
|
||||
|
||||
В нём встроена система внедрения зависимостей, вдохновлённая Angular 2. Требуется предварительная регистрация «инжектируемых» компонентов (как и во всех известных мне системах внедрения зависимостей), что добавляет многословности и повторяемости кода.
|
||||
|
||||
Поскольку параметры описываются с помощью типов TypeScript (аналог аннотаций типов в Python), поддержка редактора весьма хороша.
|
||||
Поскольку параметры описываются с помощью типов TypeScript (аналог аннотаций типов в Python), поддержка редактора кода весьма хороша.
|
||||
|
||||
Но так как данные о типах TypeScript не сохраняются после компиляции в JavaScript, он не может полагаться на типы для одновременного определения валидации, сериализации и документации. Из‑за этого и некоторых проектных решений для получения валидации, сериализации и автоматической генерации схем приходится добавлять декораторы во многих местах. В итоге это становится довольно многословным.
|
||||
|
||||
@@ -359,7 +359,7 @@ Hug вдохновил **FastAPI** объявлять параметр `response
|
||||
|
||||
В нём были автоматические валидация данных, сериализация данных и генерация схемы OpenAPI на основе тех же аннотаций типов в нескольких местах.
|
||||
|
||||
Определение схемы тела запроса не использовало те же аннотации типов Python, как в Pydantic, — это было ближе к Marshmallow, поэтому поддержка редактора была бы хуже, но всё равно APIStar оставался лучшим доступным вариантом.
|
||||
Определение схемы тела запроса не использовало те же аннотации типов Python, как в Pydantic, — это было ближе к Marshmallow, поэтому поддержка редактора кода была бы хуже, но всё равно APIStar оставался лучшим доступным вариантом.
|
||||
|
||||
На тот момент у него были лучшие показатели в бенчмарках (его превосходил только Starlette).
|
||||
|
||||
@@ -419,7 +419,7 @@ Pydantic — это библиотека для определения вали
|
||||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
|
||||
|
||||
Starlette — это лёгкий <abbr title="Новый стандарт построения асинхронных веб-сервисов Python">ASGI</abbr> фреймворк/набор инструментов, идеально подходящий для создания высокопроизводительных asyncio‑сервисов.
|
||||
Starlette — это лёгкий <dfn title="Новый стандарт построения асинхронных веб-приложений на Python">ASGI</dfn> фреймворк/набор инструментов, идеально подходящий для создания высокопроизводительных asyncio‑сервисов.
|
||||
|
||||
Он очень простой и интуитивный. Спроектирован так, чтобы его было легко расширять, и чтобы компоненты были модульными.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Нет времени? { #in-a-hurry }
|
||||
|
||||
<abbr title="too long; didn't read – слишком длинно; не читал"><strong>TL;DR:</strong></abbr>
|
||||
<abbr title="too long; didn't read - слишком длинно; не читал"><strong>TL;DR:</strong></abbr>
|
||||
|
||||
Если вы используете сторонние библиотеки, которые нужно вызывать с `await`, например:
|
||||
|
||||
@@ -68,13 +68,13 @@ def results():
|
||||
|
||||
Асинхронный код значит, что в языке 💬 есть способ сказать компьютеру/программе 🤖, что в некоторый момент кода ему 🤖 придётся подождать, пока *что-то ещё* где-то в другом месте завершится. Назовём это *что-то ещё* «медленный файл» 📝.
|
||||
|
||||
И пока мы ждём завершения работы с «медленныи файлом» 📝, компьютер может заняться другой работой.
|
||||
И пока мы ждём завершения работы с «медленным файлом» 📝, компьютер может заняться другой работой.
|
||||
|
||||
Затем компьютер/программа 🤖 будет возвращаться каждый раз, когда появится возможность (пока снова где-то идёт ожидание), или когда 🤖 завершит всю текущую работу. И он 🤖 проверит, не завершилась ли какая-либо из задач, которых он ждал, и сделает то, что нужно.
|
||||
|
||||
Далее он 🤖 возьмёт первую завершившуюся задачу (скажем, наш «медленный файл» 📝) и продолжит делать с ней то, что требуется.
|
||||
|
||||
Это «ожидание чего-то ещё» обычно относится к операциям <abbr title="Input and Output – Ввод/вывод">I/O</abbr>, которые относительно «медленные» (по сравнению со скоростью процессора и оперативной памяти), например ожидание:
|
||||
Это «ожидание чего-то ещё» обычно относится к операциям <abbr title="Input and Output - Ввод/вывод">I/O</abbr>, которые относительно «медленные» (по сравнению со скоростью процессора и оперативной памяти), например ожидание:
|
||||
|
||||
* отправки данных клиентом по сети
|
||||
* получения клиентом данных, отправленных вашей программой по сети
|
||||
@@ -85,7 +85,7 @@ def results():
|
||||
* возврата результатов запроса к базе данных
|
||||
* и т.д.
|
||||
|
||||
Поскольку основное время выполнения уходит на ожидание операций <abbr title="Input and Output – Ввод/вывод">I/O</abbr>, их называют операциями, «ограниченными вводом-выводом» (I/O bound).
|
||||
Поскольку основное время выполнения уходит на ожидание операций <abbr title="Input and Output - Ввод/вывод">I/O</abbr>, их называют операциями, «ограниченными вводом-выводом» (I/O bound).
|
||||
|
||||
Это называется «асинхронным», потому что компьютеру/программе не нужно «синхронизироваться» с медленной задачей, простаивая и выжидая точный момент её завершения, чтобы забрать результат и продолжить работу.
|
||||
|
||||
@@ -277,7 +277,7 @@ def results():
|
||||
|
||||
В этом сценарии каждый уборщик (включая вас) был бы процессором, выполняющим свою часть работы.
|
||||
|
||||
И так как основное время выполнения уходит на реальную работу (а не ожидание), а работу в компьютере выполняет <abbr title="Central Processing Unit – Центральный процессор">CPU</abbr>, такие задачи называют «ограниченными процессором» (CPU bound).
|
||||
И так как основное время выполнения уходит на реальную работу (а не ожидание), а работу в компьютере выполняет <abbr title="Central Processing Unit - Центральный процессор">CPU</abbr>, такие задачи называют «ограниченными процессором» (CPU bound).
|
||||
|
||||
---
|
||||
|
||||
@@ -417,7 +417,7 @@ Starlette (и **FastAPI**) основаны на <a href="https://anyio.readthed
|
||||
|
||||
Когда вы объявляете *функцию-обработчик пути* обычным `def` вместо `async def`, она запускается во внешнем пуле потоков, который затем «ожидается», вместо прямого вызова (прямой вызов заблокировал бы сервер).
|
||||
|
||||
Если вы пришли из другого async-фреймворка, который работает иначе, и привыкли объявлять тривиальные *функции-обработчики пути*, выполняющие только вычисления, через простой `def` ради крошечной выгоды в производительности (около 100 наносекунд), обратите внимание: в **FastAPI** эффект будет противоположным. В таких случаях лучше использовать `async def`, если только ваши *функции-обработчики пути* не используют код, выполняющий блокирующий <abbr title="Input/Output – Ввод/вывод: чтение или запись на диск, сетевые соединения.">I/O</abbr>.
|
||||
Если вы пришли из другого async-фреймворка, который работает иначе, и привыкли объявлять тривиальные *функции-обработчики пути*, выполняющие только вычисления, через простой `def` ради крошечной выгоды в производительности (около 100 наносекунд), обратите внимание: в **FastAPI** эффект будет противоположным. В таких случаях лучше использовать `async def`, если только ваши *функции-обработчики пути* не используют код, выполняющий блокирующий <abbr title="Input/Output - Ввод/вывод: чтение или запись на диск, сетевые соединения.">I/O</abbr>.
|
||||
|
||||
Тем не менее, в обоих случаях велика вероятность, что **FastAPI** [всё равно будет быстрее](index.md#performance){.internal-link target=_blank} (или как минимум сопоставим) с вашим предыдущим фреймворком.
|
||||
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
|
||||
* **Uvicorn**: ASGI-сервер
|
||||
* **Starlette**: (использует Uvicorn) веб-микрофреймворк
|
||||
* **FastAPI**: (использует Starlette) API-микрофреймворк с рядом дополнительных возможностей для создания API, включая валидацию данных и т. п.
|
||||
* **FastAPI**: (использует Starlette) API-микрофреймворк с рядом дополнительных возможностей для создания API, включая валидацию данных и т.п.
|
||||
|
||||
* **Uvicorn**:
|
||||
* Будет иметь наилучшую производительность, так как помимо самого сервера у него немного дополнительного кода.
|
||||
* Вы не будете писать приложение непосредственно на Uvicorn. Это означало бы, что Ваш код должен включать как минимум весь код, предоставляемый Starlette (или **FastAPI**). И если Вы так сделаете, то в конечном итоге Ваше приложение будет иметь те же накладные расходы, что и при использовании фреймворка, минимизирующего код Вашего приложения и Ваши ошибки.
|
||||
* Если Вы сравниваете Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и т. д. — серверами приложений.
|
||||
* Если Вы сравниваете Uvicorn, сравнивайте его с Daphne, Hypercorn, uWSGI и т.д. — серверами приложений.
|
||||
* **Starlette**:
|
||||
* Будет на следующем месте по производительности после Uvicorn. Фактически Starlette запускается под управлением Uvicorn, поэтому он может быть только «медленнее» Uvicorn из‑за выполнения большего объёма кода.
|
||||
* Зато он предоставляет Вам инструменты для создания простых веб‑приложений с маршрутизацией по путям и т. п.
|
||||
* Если Вы сравниваете Starlette, сравнивайте его с Sanic, Flask, Django и т. д. — веб‑фреймворками (или микрофреймворками).
|
||||
* Зато он предоставляет Вам инструменты для создания простых веб‑приложений с маршрутизацией по путям и т.п.
|
||||
* Если Вы сравниваете Starlette, сравнивайте его с Sanic, Flask, Django и т.д. — веб‑фреймворками (или микрофреймворками).
|
||||
* **FastAPI**:
|
||||
* Точно так же, как Starlette использует Uvicorn и не может быть быстрее него, **FastAPI** использует Starlette, поэтому не может быть быстрее его.
|
||||
* FastAPI предоставляет больше возможностей поверх Starlette — те, которые почти всегда нужны при создании API, такие как валидация и сериализация данных. В довесок Вы ещё и получаете автоматическую документацию (автоматическая документация даже не увеличивает накладные расходы при работе приложения, так как она создаётся при запуске).
|
||||
* Если бы Вы не использовали FastAPI, а использовали Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т. д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
|
||||
* Если бы Вы не использовали FastAPI, а использовали Starlette напрямую (или другой инструмент вроде Sanic, Flask, Responder и т.д.), Вам пришлось бы самостоятельно реализовать валидацию и сериализацию данных. То есть, в итоге, Ваше приложение имело бы такие же накладные расходы, как если бы оно было создано с использованием FastAPI. И во многих случаях валидация и сериализация данных представляют собой самый большой объём кода, написанного в приложениях.
|
||||
* Таким образом, используя FastAPI, Вы экономите время разработки, уменьшаете количество ошибок, строк кода и, вероятно, получите ту же производительность (или лучше), как и если бы не использовали его (поскольку Вам пришлось бы реализовать все его возможности в своём коде).
|
||||
* Если Вы сравниваете FastAPI, сравнивайте его с фреймворком веб‑приложений (или набором инструментов), который обеспечивает валидацию данных, сериализацию и документацию, такими как Flask-apispec, NestJS, Molten и им подобные. Фреймворки с интегрированной автоматической валидацией данных, сериализацией и документацией.
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
В [предыдущей главе про HTTPS](https.md){.internal-link target=_blank} мы разобрались, как HTTPS обеспечивает шифрование для вашего API.
|
||||
|
||||
Также мы увидели, что HTTPS обычно обеспечивает компонент, **внешний** по отношению к серверу вашего приложения — **TLS Termination Proxy**.
|
||||
Также мы увидели, что HTTPS обычно обеспечивает компонент, **внешний** по отношению к серверу вашего приложения — **прокси-сервер TSL-терминации**.
|
||||
|
||||
И должен быть компонент, отвечающий за **обновление HTTPS‑сертификатов** — это может быть тот же самый компонент или отдельный.
|
||||
|
||||
### Примеры инструментов для HTTPS { #example-tools-for-https }
|
||||
|
||||
Некоторые инструменты, которые можно использовать как TLS Termination Proxy:
|
||||
Некоторые инструменты, которые можно использовать как прокси-сервер TSL-терминации:
|
||||
|
||||
* Traefik
|
||||
* Автоматически обновляет сертификаты ✨
|
||||
@@ -47,7 +47,7 @@
|
||||
* С внешним компонентом (например, cert-manager) для обновления сертификатов
|
||||
* Обрабатывается внутри облачного провайдера как часть его услуг (см. ниже 👇)
|
||||
|
||||
Другой вариант — использовать **облачный сервис**, который возьмёт на себя больше задач, включая настройку HTTPS. Там могут быть ограничения или дополнительная стоимость и т.п., но в таком случае вам не придётся самим настраивать TLS Termination Proxy.
|
||||
Другой вариант — использовать **облачный сервис**, который возьмёт на себя больше задач, включая настройку HTTPS. Там могут быть ограничения или дополнительная стоимость и т.п., но в таком случае вам не придётся самим настраивать прокси-сервер TSL-терминации.
|
||||
|
||||
В следующих главах я покажу конкретные примеры.
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
|
||||
Процесс‑менеджер, вероятно, будет тем, кто слушает **порт** на IP. И он будет передавать всю коммуникацию воркер‑процессам.
|
||||
|
||||
Эти воркеры будут запускать ваше приложение, выполнять основные вычисления для получения **запроса** и возврата **ответа**, и загружать всё, что вы кладёте в переменные, в RAM.
|
||||
Эти воркеры будут запускать ваше приложение, выполнять основные вычисления для получения **HTTP‑запроса** и возврата **HTTP‑ответа**, и загружать всё, что вы кладёте в переменные, в RAM.
|
||||
|
||||
<img src="/img/deployment/concepts/process-ram.drawio.svg">
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
|
||||
Ваш сервер(а) — это **ресурс**, который ваши программы могут потреблять или **использовать**: время вычислений на CPU и доступную оперативную память (RAM).
|
||||
|
||||
Какую долю системных ресурсов вы хотите потреблять/использовать? Можно подумать «немного», но на практике вы, скорее всего, захотите потреблять **максимум без падений**.
|
||||
Какую долю системных ресурсов вы хотите потреблять/использовать? Можно подумать «немного», но на практике вы, вероятно, захотите потреблять **максимум без падений**.
|
||||
|
||||
Если вы платите за 3 сервера, но используете лишь малую часть их RAM и CPU, вы, вероятно, **тратите деньги впустую** 💸 и **электроэнергию серверов** 🌎 и т.п.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<summary>Предпросмотр Dockerfile 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
@@ -166,7 +166,7 @@ def read_item(item_id: int, q: str | None = None):
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)!
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
@@ -273,9 +273,9 @@ CMD fastapi run app/main.py --port 80
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
#### За прокси-сервером TLS терминации { #behind-a-tls-termination-proxy }
|
||||
#### За прокси-сервером TSL-терминации { #behind-a-tls-termination-proxy }
|
||||
|
||||
Если вы запускаете контейнер за прокси-сервером завершения TLS (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.
|
||||
Если вы запускаете контейнер за прокси-сервером TSL-терминации (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.
|
||||
|
||||
```Dockerfile
|
||||
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
|
||||
@@ -390,7 +390,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
|
||||
Тогда в `Dockerfile` нужно изменить пути копирования:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
@@ -454,7 +454,7 @@ CMD ["fastapi", "run", "main.py", "--port", "80"]
|
||||
|
||||
## Репликация — количество процессов { #replication-number-of-processes }
|
||||
|
||||
Если у вас есть <abbr title="Группа машин, настроенных так, чтобы быть соединенными и работать вместе определенным образом.">кластер</abbr> машин с **Kubernetes**, Docker Swarm Mode, Nomad или другой похожей системой для управления распределёнными контейнерами на нескольких машинах, скорее всего вы будете **управлять репликацией** на **уровне кластера**, а не использовать **менеджер процессов** (например, Uvicorn с воркерами) в каждом контейнере.
|
||||
Если у вас есть <dfn title="Группа машин, настроенных так, чтобы быть соединенными и работать вместе определенным образом.">кластер</dfn> машин с **Kubernetes**, Docker Swarm Mode, Nomad или другой похожей системой для управления распределёнными контейнерами на нескольких машинах, скорее всего вы будете **управлять репликацией** на **уровне кластера**, а не использовать **менеджер процессов** (например, Uvicorn с воркерами) в каждом контейнере.
|
||||
|
||||
Одна из таких систем управления распределёнными контейнерами, как Kubernetes, обычно имеет встроенный способ управлять **репликацией контейнеров**, поддерживая **балансировку нагрузки** для входящих запросов — всё это на **уровне кластера**.
|
||||
|
||||
@@ -462,17 +462,17 @@ CMD ["fastapi", "run", "main.py", "--port", "80"]
|
||||
|
||||
### Балансировщик нагрузки { #load-balancer }
|
||||
|
||||
При использовании контейнеров обычно есть компонент, **слушающий главный порт**. Это может быть другой контейнер — **прокси завершения TLS** для обработки **HTTPS** или похожий инструмент.
|
||||
При использовании контейнеров обычно есть компонент, **слушающий главный порт**. Это может быть другой контейнер — **прокси-сервер TSL-терминации** для обработки **HTTPS** или похожий инструмент.
|
||||
|
||||
Поскольку этот компонент принимает **нагрузку** запросов и распределяет её между воркерами **сбалансированно**, его часто называют **балансировщиком нагрузки**.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Тот же компонент **прокси завершения TLS**, который обрабатывает HTTPS, скорее всего также будет **балансировщиком нагрузки**.
|
||||
Тот же компонент **прокси-сервер TSL-терминации**, который обрабатывает HTTPS, скорее всего также будет **балансировщиком нагрузки**.
|
||||
|
||||
///
|
||||
|
||||
При работе с контейнерами система, которую вы используете для запуска и управления ими, уже имеет внутренние средства для передачи **сетевого взаимодействия** (например, HTTP-запросов) от **балансировщика нагрузки** (который также может быть **прокси завершения TLS**) к контейнеру(-ам) с вашим приложением.
|
||||
При работе с контейнерами система, которую вы используете для запуска и управления ими, уже имеет внутренние средства для передачи **сетевого взаимодействия** (например, HTTP-запросов) от **балансировщика нагрузки** (который также может быть **прокси-сервером TSL-терминации**) к контейнеру(-ам) с вашим приложением.
|
||||
|
||||
### Один балансировщик — несколько контейнеров-воркеров { #one-load-balancer-multiple-worker-containers }
|
||||
|
||||
@@ -499,7 +499,7 @@ CMD ["fastapi", "run", "main.py", "--port", "80"]
|
||||
В таких случаях вы можете использовать опцию командной строки `--workers`, чтобы указать нужное количество воркеров:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
Чаще всего всё начинается с **приобретения** **имени домена**. Затем вы настраиваете его на DNS‑сервере (возможно, у того же облачного провайдера).
|
||||
|
||||
Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет <abbr title="Не изменяется">постоянный</abbr> **публичный IP-адрес**.
|
||||
Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет <dfn title="Со временем не меняется. Не динамический.">постоянный</dfn> **публичный IP-адрес**.
|
||||
|
||||
На DNS‑сервере(ах) вы настроите запись («`A record`» - запись типа A), указывающую, что **ваш домен** должен указывать на публичный **IP‑адрес вашего сервера**.
|
||||
|
||||
|
||||
@@ -18,6 +18,6 @@
|
||||
|
||||
Например, мы, команда, стоящая за FastAPI, создали <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>, чтобы сделать развёртывание приложений FastAPI в облаке как можно более простым и прямолинейным, с тем же удобством для разработчика, что и при работе с FastAPI.
|
||||
|
||||
В этом блоке я покажу вам некоторые из основных концепций, которые вы, вероятно, должны иметь в виду при развертывании приложения **FastAPI** (хотя большинство из них применимо к любому другому типу веб-приложений).
|
||||
Я покажу вам некоторые из основных концепций, которые вы, вероятно, должны иметь в виду при развертывании приложения **FastAPI** (хотя большинство из них применимо к любому другому типу веб-приложений).
|
||||
|
||||
В последующих разделах вы узнаете больше деталей и методов, необходимых для этого. ✨
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# О версиях FastAPI { #about-fastapi-versions }
|
||||
|
||||
**FastAPI** уже используется в продакшене во многих приложениях и системах. Покрытие тестами поддерживается на уровне 100%. Но его разработка всё ещё движется быстрыми темпами.
|
||||
**FastAPI** уже используется в продакшн во многих приложениях и системах. Покрытие тестами поддерживается на уровне 100%. Но его разработка всё ещё движется быстрыми темпами.
|
||||
|
||||
Часто добавляются новые функции, регулярно исправляются баги, код продолжает постоянно совершенствоваться.
|
||||
|
||||
По указанным причинам текущие версии до сих пор `0.x.x`. Это говорит о том, что каждая версия может содержать обратно несовместимые изменения, следуя <a href="https://semver.org/" class="external-link" target="_blank">Семантическому версионированию</a>.
|
||||
|
||||
Уже сейчас вы можете создавать приложения в продакшене, используя **FastAPI** (и скорее всего так и делаете), главное убедиться в том, что вы используете версию, которая корректно работает с вашим кодом.
|
||||
Уже сейчас вы можете создавать приложения в продакшн, используя **FastAPI** (и скорее всего так и делаете), главное убедиться в том, что вы используете версию, которая корректно работает с вашим кодом.
|
||||
|
||||
## Закрепите вашу версию `fastapi` { #pin-your-fastapi-version }
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ Hello World from Python
|
||||
|
||||
Эти переменные окружения могут работать только с **текстовыми строками**, поскольку они являются внешними по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с различными операционными системами, такими как Linux, Windows, macOS).
|
||||
|
||||
Это означает, что **любое значение**, считанное в Python из переменной окружения, **будет `str`**, и любое преобразование к другому типу или любая проверка должны быть выполнены в коде.
|
||||
Это означает, что **любое значение**, считанное в Python из переменной окружения, **будет `str`**, и любое преобразование к другому типу или любая валидация должны быть выполнены в коде.
|
||||
|
||||
Подробнее об использовании переменных окружения для работы с **настройками приложения** вы узнаете в [Расширенное руководство пользователя - Настройки и переменные среды](./advanced/settings.md){.internal-link target=_blank}.
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ FastAPI CLI берет путь к вашей Python-программе (нап
|
||||
|
||||
Для работы в режиме продакшн вместо `fastapi dev` нужно использовать `fastapi run`. 🚀
|
||||
|
||||
Внутри **FastAPI CLI** используется <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, высокопроизводительный, готовый к работе в продакшне ASGI-сервер. 😎
|
||||
Внутри **FastAPI CLI** используется <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>, высокопроизводительный, готовый к работе в продакшн ASGI-сервер. 😎
|
||||
|
||||
## `fastapi dev` { #fastapi-dev }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
### Основано на открытых стандартах { #based-on-open-standards }
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> для создания API, включая объявления <abbr title="также известные как HTTP-методы, например: POST, GET, PUT, DELETE">операций</abbr> <abbr title="также известен как: эндпоинты, маршруты)">пути</abbr>, параметров, тел запросов, безопасности и т. д.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> для создания API, включая объявления <dfn title="также известны как HTTP-методы, например: POST, GET, PUT, DELETE">операций</dfn> <dfn title="также известен как: эндпоинты, маршруты">пути</dfn>, параметров, тел запросов, безопасности и т.д.
|
||||
* Автоматическая документация моделей данных с помощью <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (так как сама спецификация OpenAPI основана на JSON Schema).
|
||||
* Разработан вокруг этих стандартов, после тщательного их изучения. Это не дополнительная надстройка поверх.
|
||||
* Это также позволяет использовать автоматическую **генерацию клиентского кода** на многих языках.
|
||||
@@ -107,7 +107,7 @@ FastAPI имеет продуманные значения **по умолчан
|
||||
* Объекты JSON (`dict`).
|
||||
* Массив JSON (`list`) с определёнными типами элементов.
|
||||
* Строковые (`str`) поля с ограничением минимальной и максимальной длины.
|
||||
* Числа (`int`, `float`) с минимальными и максимальными значениями и т. п.
|
||||
* Числа (`int`, `float`) с минимальными и максимальными значениями и т.п.
|
||||
|
||||
* Проверка для более экзотических типов, таких как:
|
||||
* URL.
|
||||
@@ -126,24 +126,24 @@ FastAPI имеет продуманные значения **по умолчан
|
||||
* HTTP Basic.
|
||||
* **OAuth2** (также с **токенами JWT**). Ознакомьтесь с руководством [OAuth2 с JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
|
||||
* Ключи API в:
|
||||
* Заголовках.
|
||||
* HTTP-заголовках.
|
||||
* Параметрах запросов.
|
||||
* Cookies и т.п.
|
||||
|
||||
Вдобавок все функции безопасности от Starlette (включая **сессионные cookies**).
|
||||
|
||||
Все инструменты и компоненты спроектированы для многократного использования и легко интегрируются с вашими системами, хранилищами данных, реляционными и NoSQL базами данных и т. д.
|
||||
Все инструменты и компоненты спроектированы для многократного использования и легко интегрируются с вашими системами, хранилищами данных, реляционными и NoSQL базами данных и т.д.
|
||||
|
||||
### Внедрение зависимостей { #dependency-injection }
|
||||
|
||||
FastAPI включает в себя чрезвычайно простую в использовании, но чрезвычайно мощную систему <abbr title='известную как: "components", "resources", "services", "providers"'><strong>Внедрения зависимостей</strong></abbr>.
|
||||
FastAPI включает в себя чрезвычайно простую в использовании, но чрезвычайно мощную систему <dfn title='также известна как: "компоненты", "ресурсы", "сервисы", "провайдеры"'><strong>Внедрения зависимостей</strong></dfn>.
|
||||
|
||||
* Даже зависимости могут иметь зависимости, создавая иерархию или **«граф» зависимостей**.
|
||||
* Всё **автоматически обрабатывается** фреймворком.
|
||||
* Все зависимости могут запрашивать данные из запросов и **дополнять операции пути** ограничениями и автоматической документацией.
|
||||
* **Автоматическая проверка** даже для параметров *операций пути*, определённых в зависимостях.
|
||||
* Поддержка сложных систем аутентификации пользователей, **соединений с базами данных** и т. д.
|
||||
* **Никаких компромиссов** с базами данных, интерфейсами и т. д. Но при этом — лёгкая интеграция со всеми ними.
|
||||
* Поддержка сложных систем аутентификации пользователей, **соединений с базами данных** и т.д.
|
||||
* **Никаких компромиссов** с базами данных, интерфейсами и т.д. Но при этом — лёгкая интеграция со всеми ними.
|
||||
|
||||
### Нет ограничений на "Плагины" { #unlimited-plug-ins }
|
||||
|
||||
@@ -153,8 +153,8 @@ FastAPI включает в себя чрезвычайно простую в и
|
||||
|
||||
### Проверен { #tested }
|
||||
|
||||
* 100% <abbr title="Количество автоматически проверяемого кода">покрытие тестами</abbr>.
|
||||
* 100% <abbr title="Аннотации типов Python, благодаря которым ваш редактор и внешние инструменты могут обеспечить вам лучшую поддержку">аннотирование типов</abbr> в кодовой базе.
|
||||
* 100% <dfn title="Количество автоматически проверяемого кода">покрытие тестами</dfn>.
|
||||
* 100% <dfn title="Аннотации типов Python, благодаря которым ваш редактор кода и внешние инструменты могут обеспечить вам лучшую поддержку">аннотирование типов</dfn> в кодовой базе.
|
||||
* Используется в продакшн‑приложениях.
|
||||
|
||||
## Возможности Starlette { #starlette-features }
|
||||
@@ -179,7 +179,7 @@ FastAPI включает в себя чрезвычайно простую в и
|
||||
|
||||
**FastAPI** полностью совместим с (и основан на) <a href="https://docs.pydantic.dev/" class="external-link" target="_blank"><strong>Pydantic</strong></a>. Поэтому любой дополнительный код Pydantic, который у вас есть, также будет работать.
|
||||
|
||||
Включая внешние библиотеки, также основанные на Pydantic, такие как <abbr title="Object-Relational Mapper">ORM</abbr>’ы, <abbr title="Object-Document Mapper">ODM</abbr>’ы для баз данных.
|
||||
Включая внешние библиотеки, также основанные на Pydantic, такие как <abbr title="Object-Relational Mapper - объектно-реляционный маппер">ORM</abbr>’ы, <abbr title="Object-Document Mapper - объектно-документный маппер">ODM</abbr>’ы для баз данных.
|
||||
|
||||
Это также означает, что во многих случаях вы можете передавать тот же объект, который получили из запроса, **непосредственно в базу данных**, так как всё проверяется автоматически.
|
||||
|
||||
@@ -190,10 +190,10 @@ FastAPI включает в себя чрезвычайно простую в и
|
||||
* **Никакой нервотрёпки**:
|
||||
* Не нужно изучать новые схемы в микроязыках.
|
||||
* Если вы знаете типы в Python, вы знаете, как использовать Pydantic.
|
||||
* Прекрасно сочетается с вашим **<abbr title="Integrated Development Environment - Интегрированная среда разработки: попросту «редактора кода»">IDE</abbr>/<abbr title="Программа, проверяющая ошибки в коде">linter</abbr>/мозгом**:
|
||||
* Прекрасно сочетается с вашим **<abbr title="Integrated Development Environment - Интегрированная среда разработки: похоже на редактор кода">IDE</abbr>/<dfn title="Программа, которая проверяет код на ошибки">линтер</dfn>/мозгом**:
|
||||
* Потому что структуры данных pydantic — это всего лишь экземпляры классов, определённых вами; автозавершение, проверка кода, mypy и ваша интуиция — всё будет работать с вашими валидированными данными.
|
||||
* Валидация **сложных структур**:
|
||||
* Использование иерархических моделей Pydantic; `List`, `Dict` и т. п. из модуля `typing` (входит в стандартную библиотеку Python).
|
||||
* Использование иерархических моделей Pydantic; `List`, `Dict` и т.п. из модуля `typing` (входит в стандартную библиотеку Python).
|
||||
* Валидаторы позволяют чётко и легко определять, проверять и документировать сложные схемы данных в виде JSON Schema.
|
||||
* У вас могут быть глубоко **вложенные объекты JSON**, и все они будут проверены и аннотированы.
|
||||
* **Расширяемость**:
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
## Исследования { #investigation }
|
||||
|
||||
Благодаря опыту использования существующих альтернатив, мы с коллегами изучили их основные идеи и скомбинировали собранные знания наилучшим образом.
|
||||
Используя все существовавшие ранее альтернативы, я получил возможность у каждой из них чему-то научиться, позаимствовать идеи и объединить их наилучшим образом для себя и для команд разработчиков, с которыми я работал.
|
||||
|
||||
Например, стало ясно, что необходимо брать за основу стандартные аннотации типов Python.
|
||||
|
||||
@@ -44,13 +44,13 @@
|
||||
|
||||
Я проверил несколько идей на самых популярных редакторах кода: 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% пользователей.
|
||||
Согласно последнему <a href="https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools" class="external-link" target="_blank">опросу Python-разработчиков</a>, который охватывает около 80% пользователей.
|
||||
|
||||
Это означает, что **FastAPI** был специально проверен на редакторах, используемых 80% Python-разработчиками. И поскольку большинство других редакторов, как правило, работают аналогичным образом, все его преимущества должны работать практически для всех редакторов.
|
||||
|
||||
Таким образом, я смог найти наилучшие способы сократить дублирование кода, обеспечить повсеместное автозавершение, проверку типов и ошибок и т.д.
|
||||
|
||||
И все это, чтобы все пользователи могли получать наилучший опыт разработки.
|
||||
И все это, чтобы все разработчики могли получать наилучший опыт разработки.
|
||||
|
||||
## Зависимости { #requirements }
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
По моим предложениям был изменён код этого фреймворка, чтобы сделать его полностью совместимым с JSON Schema, поддержать различные способы определения ограничений и улучшить поддержку в редакторах кода (проверки типов, автозавершение) на основе тестов в нескольких редакторах.
|
||||
|
||||
В то же время, я принимал участие в разработке <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>, ещё один из основных компонентов FastAPI.
|
||||
Во время разработки я также внес вклад в <a href="https://www.starlette.dev/" class="external-link" target="_blank">**Starlette**</a>, другую ключевую зависимость.
|
||||
|
||||
## Разработка { #development }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
Например, вы можете создать подкласс `HTTPBearer`, который будет возвращать ошибку `403 Forbidden` вместо стандартной `401 Unauthorized`:
|
||||
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
## О безопасности, API и документации { #about-security-apis-and-docs }
|
||||
|
||||
Скрытие пользовательских интерфейсов документации в продакшн *не должно* быть способом защиты вашего API.
|
||||
Скрытие пользовательских интерфейсов документации в продакшн не должно быть способом защиты вашего API.
|
||||
|
||||
Это не добавляет дополнительной безопасности вашему API, *операции пути* (обработчики пути) всё равно будут доступны по своим путям.
|
||||
Это не добавляет дополнительной безопасности вашему API, операции пути (обработчики пути) всё равно будут доступны по своим путям.
|
||||
|
||||
Если в вашем коде есть уязвимость, она всё равно останется.
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
Например:
|
||||
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
|
||||
|
||||
Здесь мы объявляем настройку `openapi_url` с тем же значением по умолчанию — `"/openapi.json"`.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ FastAPI преобразует эти настройки в **JSON**, чтобы
|
||||
|
||||
Но вы можете отключить её, установив `syntaxHighlight` в `False`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
|
||||
|
||||
…и после этого Swagger UI больше не будет показывать подсветку синтаксиса:
|
||||
|
||||
@@ -28,7 +28,7 @@ FastAPI преобразует эти настройки в **JSON**, чтобы
|
||||
|
||||
Аналогично вы можете задать тему подсветки синтаксиса с ключом "syntaxHighlight.theme" (обратите внимание, что посередине стоит точка):
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Эта настройка изменит цветовую тему подсветки синтаксиса:
|
||||
|
||||
@@ -46,7 +46,7 @@ FastAPI включает некоторые параметры конфигур
|
||||
|
||||
Например, чтобы отключить `deepLinking`, можно передать такие настройки в `swagger_ui_parameters`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
|
||||
|
||||
## Другие параметры Swagger UI { #other-swagger-ui-parameters }
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
|
||||
|
||||
### Подключить пользовательскую документацию { #include-the-custom-docs }
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
Аналогично и для ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -50,7 +50,7 @@ Swagger UI сделает это за вас «за кулисами», но д
|
||||
|
||||
Чтобы убедиться, что всё работает, создайте *операцию пути*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
|
||||
|
||||
### Тестирование { #test-it }
|
||||
|
||||
@@ -118,7 +118,7 @@ Swagger UI сделает это за вас «за кулисами», но д
|
||||
* Импортируйте `StaticFiles`.
|
||||
* Смонтируйте экземпляр `StaticFiles()` в определённый путь.
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
|
||||
|
||||
### Протестируйте статические файлы { #test-the-static-files }
|
||||
|
||||
@@ -144,7 +144,7 @@ Swagger UI сделает это за вас «за кулисами», но д
|
||||
|
||||
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
|
||||
|
||||
### Подключить пользовательскую документацию со статическими файлами { #include-the-custom-docs-for-static-files }
|
||||
|
||||
@@ -160,7 +160,7 @@ Swagger UI сделает это за вас «за кулисами», но д
|
||||
|
||||
Аналогично и для ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -176,7 +176,7 @@ Swagger UI сделает это за вас «за кулисами», но д
|
||||
|
||||
Чтобы убедиться, что всё работает, создайте *операцию пути*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
|
||||
|
||||
### Тестирование UI со статическими файлами { #test-static-files-ui }
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
* `version`: Версия вашего API, например `2.5.0`.
|
||||
* `openapi_version`: Версия используемой спецификации OpenAPI. По умолчанию — последняя: `3.1.0`.
|
||||
* `summary`: Краткое описание API.
|
||||
* `description`: Описание вашего API; может включать Markdown и будет отображается в документации.
|
||||
* `description`: Описание вашего API; может включать Markdown и будет отображаться в документации.
|
||||
* `routes`: Список маршрутов — это каждая зарегистрированная *операция пути*. Берутся из `app.routes`.
|
||||
|
||||
/// info | Информация
|
||||
@@ -43,19 +43,19 @@
|
||||
|
||||
Сначала напишите приложение **FastAPI** как обычно:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
|
||||
|
||||
### Сгенерируйте схему OpenAPI { #generate-the-openapi-schema }
|
||||
|
||||
Затем используйте ту же вспомогательную функцию для генерации схемы OpenAPI внутри функции `custom_openapi()`:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
|
||||
|
||||
### Измените схему OpenAPI { #modify-the-openapi-schema }
|
||||
|
||||
Теперь можно добавить расширение ReDoc, добавив кастомный `x-logo` в «объект» `info` в схеме OpenAPI:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
|
||||
|
||||
### Кэшируйте схему OpenAPI { #cache-the-openapi-schema }
|
||||
|
||||
@@ -65,13 +65,13 @@
|
||||
|
||||
Она будет создана один раз, а затем тот же кэшированный вариант будет использоваться для последующих запросов.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
|
||||
|
||||
### Переопределите метод { #override-the-method }
|
||||
|
||||
Теперь вы можете заменить метод `.openapi()` на вашу новую функцию.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
|
||||
|
||||
### Проверьте { #check-it }
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
|
||||
|
||||
{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
|
||||
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
|
||||
|
||||
Подробнее о Strawberry можно узнать в <a href="https://strawberry.rocks/" class="external-link" target="_blank">документации Strawberry</a>.
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Как сделать — Рецепты { #how-to-recipes }
|
||||
|
||||
Здесь вы найдете разные рецепты и руководства «как сделать» по различным темам.
|
||||
Здесь вы найдете разные рецепты и руководства «как сделать» по **различным темам**.
|
||||
|
||||
Большинство из этих идей более-менее независимы, и в большинстве случаев вам стоит изучать их только если они напрямую относятся к вашему проекту.
|
||||
Большинство из этих идей более-менее **независимы**, и в большинстве случаев вам стоит изучать их только если они напрямую относятся к **вашему проекту**.
|
||||
|
||||
Если что-то кажется интересным и полезным для вашего проекта, смело изучайте; в противном случае, вероятно, можно просто пропустить.
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
Если вы хотите изучить FastAPI структурированно (рекомендуется), вместо этого читайте [Учебник — Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} по главам.
|
||||
Если вы хотите **изучить FastAPI** структурированно (рекомендуется), вместо этого читайте [Учебник — Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} по главам.
|
||||
|
||||
///
|
||||
|
||||
@@ -40,7 +40,7 @@ FastAPI — это современный, быстрый (высокопрои
|
||||
* **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых доступных фреймворков Python](#performance).
|
||||
* **Быстрота разработки**: Увеличьте скорость разработки фич примерно на 200–300%. *
|
||||
* **Меньше ошибок**: Сократите примерно на 40% количество ошибок, вызванных человеком (разработчиком). *
|
||||
* **Интуитивность**: Отличная поддержка редактора кода. <abbr title="также известное как: автодополнение, автозавершение, IntelliSense">Автозавершение</abbr> везде. Меньше времени на отладку.
|
||||
* **Интуитивность**: Отличная поддержка редактора кода. <dfn title="также известное как: автодополнение, автозавершение, IntelliSense">Автозавершение</dfn> везде. Меньше времени на отладку.
|
||||
* **Простота**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации.
|
||||
* **Краткость**: Минимизируйте дублирование кода. Несколько возможностей из каждого объявления параметров. Меньше ошибок.
|
||||
* **Надежность**: Получите код, готовый к продакшн. С автоматической интерактивной документацией.
|
||||
@@ -127,7 +127,7 @@ 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>
|
||||
|
||||
Если вы создаёте приложение <abbr title="Command Line Interface – Интерфейс командной строки">CLI</abbr> для использования в терминале вместо веб-API, посмотрите <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
Если вы создаёте приложение <abbr title="Command Line Interface - Интерфейс командной строки">CLI</abbr> для использования в терминале вместо веб-API, посмотрите <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
|
||||
|
||||
**Typer** — младший брат FastAPI. И он задуман как **FastAPI для CLI**. ⌨️ 🚀
|
||||
|
||||
@@ -368,7 +368,7 @@ item: Item
|
||||
* Валидацию данных:
|
||||
* Автоматические и понятные ошибки, когда данные некорректны.
|
||||
* Валидацию даже для глубоко вложенных объектов JSON.
|
||||
* <abbr title="также известное как: сериализация, парсинг, маршалинг">Преобразование</abbr> входных данных: из сети в данные и типы Python. Чтение из:
|
||||
* <dfn title="также известное как: сериализация, парсинг, маршалинг">Преобразование</dfn> входных данных: из сети в данные и типы Python. Чтение из:
|
||||
* JSON.
|
||||
* Параметров пути.
|
||||
* Параметров запроса.
|
||||
@@ -376,7 +376,7 @@ item: Item
|
||||
* HTTP-заголовков.
|
||||
* Форм.
|
||||
* Файлов.
|
||||
* <abbr title="также известное как: сериализация, парсинг, маршалинг">Преобразование</abbr> выходных данных: из данных и типов Python в данные сети (например, JSON):
|
||||
* <dfn title="также известное как: сериализация, парсинг, маршалинг">Преобразование</dfn> выходных данных: из данных и типов Python в данные сети (например, JSON):
|
||||
* Преобразование типов Python (`str`, `int`, `float`, `bool`, `list` и т.д.).
|
||||
* Объекты `datetime`.
|
||||
* Объекты `UUID`.
|
||||
@@ -439,7 +439,7 @@ item: Item
|
||||
|
||||
* Объявление **параметров** из других источников: **HTTP-заголовки**, **cookies**, **поля формы** и **файлы**.
|
||||
* Как задать **ограничения валидации** вроде `maximum_length` или `regex`.
|
||||
* Очень мощную и простую в использовании систему **<abbr title="также известна как: компоненты, ресурсы, провайдеры, сервисы, инъекции">внедрения зависимостей</abbr>**.
|
||||
* Очень мощную и простую в использовании систему **<dfn title="также известна как: компоненты, ресурсы, провайдеры, сервисы, инъекции">внедрения зависимостей</dfn>**.
|
||||
* Безопасность и аутентификацию, включая поддержку **OAuth2** с **JWT токенами** и **HTTP Basic** аутентификацию.
|
||||
* Более продвинутые (но столь же простые) приёмы объявления **глубоко вложенных JSON-моделей** (спасибо Pydantic).
|
||||
* Интеграцию **GraphQL** с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками.
|
||||
@@ -524,7 +524,7 @@ FastAPI зависит от Pydantic и Starlette.
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> — обязателен, если вы хотите использовать `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> — обязателен, если вы хотите использовать конфигурацию шаблонов по умолчанию.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - обязателен, если вы хотите поддерживать <abbr title="преобразование строки, полученной из HTTP-запроса, в данные Python">«парсинг»</abbr> форм через `request.form()`.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - обязателен, если вы хотите поддерживать <dfn title="преобразование строки, полученной из HTTP-запроса, в данные Python">«парсинг»</dfn> форм через `request.form()`.
|
||||
|
||||
Используется FastAPI:
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
- 🤖 Автоматически сгенерированный фронтенд‑клиент.
|
||||
- 🧪 [Playwright](https://playwright.dev) для End‑to‑End тестирования.
|
||||
- 🦇 Поддержка тёмной темы.
|
||||
- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшна.
|
||||
- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшн.
|
||||
- 🔒 Безопасное хэширование паролей по умолчанию.
|
||||
- 🔑 Аутентификация по JWT‑токенам.
|
||||
- 📫 Восстановление пароля по электронной почте.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Python поддерживает необязательные «подсказки типов» (их также называют «аннотациями типов»).
|
||||
|
||||
Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять <abbr title="например: str, int, float, bool">тип</abbr> переменной.
|
||||
Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять <dfn title="например: str, int, float, bool">тип</dfn> переменной.
|
||||
|
||||
Объявляя типы для ваших переменных, редакторы кода и инструменты смогут лучше вас поддерживать.
|
||||
|
||||
@@ -22,7 +22,7 @@ Python поддерживает необязательные «подсказк
|
||||
|
||||
Давайте начнем с простого примера:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py *}
|
||||
|
||||
Вызов этой программы выводит:
|
||||
|
||||
@@ -34,9 +34,9 @@ John Doe
|
||||
|
||||
* Принимает `first_name` и `last_name`.
|
||||
* Преобразует первую букву каждого значения в верхний регистр с помощью `title()`.
|
||||
* <abbr title="Объединяет в одно целое. Содержимое одного — сразу после другого.">Соединяет</abbr> их пробелом посередине.
|
||||
* <dfn title="Соединяет их в одно. Содержимое одного — сразу после другого.">Соединяет</dfn> их пробелом посередине.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
|
||||
|
||||
### Отредактируем пример { #edit-it }
|
||||
|
||||
@@ -78,7 +78,7 @@ John Doe
|
||||
|
||||
Это и есть «подсказки типов»:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
|
||||
|
||||
Это не то же самое, что объявление значений по умолчанию, как, например:
|
||||
|
||||
@@ -106,7 +106,7 @@ John Doe
|
||||
|
||||
Посмотрите на эту функцию — у неё уже есть подсказки типов:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
|
||||
|
||||
Так как редактор кода знает типы переменных, вы получаете не только автозавершение, но и проверки ошибок:
|
||||
|
||||
@@ -114,7 +114,7 @@ John Doe
|
||||
|
||||
Теперь вы знаете, что нужно исправить — преобразовать `age` в строку с помощью `str(age)`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
|
||||
|
||||
## Объявление типов { #declaring-types }
|
||||
|
||||
@@ -133,29 +133,32 @@ John Doe
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
|
||||
|
||||
### Generic-типы с параметрами типов { #generic-types-with-type-parameters }
|
||||
### Модуль `typing` { #typing-module }
|
||||
|
||||
Есть структуры данных, которые могут содержать другие значения, например, `dict`, `list`, `set` и `tuple`. И внутренние значения тоже могут иметь свой тип.
|
||||
Для некоторых дополнительных сценариев может понадобиться импортировать что-то из стандартного модуля `typing`. Например, когда вы хотите объявить, что что-то имеет «любой тип», можно использовать `Any` из `typing`:
|
||||
|
||||
Такие типы, которые содержат внутренние типы, называют «**generic**»-типами. И их можно объявлять, в том числе с указанием внутренних типов.
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
Чтобы объявлять эти типы и их внутренние типы, вы можете использовать стандартный модуль Python `typing`. Он существует специально для поддержки подсказок типов.
|
||||
|
||||
#### Новые версии Python { #newer-versions-of-python }
|
||||
def some_function(data: Any):
|
||||
print(data)
|
||||
```
|
||||
|
||||
Синтаксис с использованием `typing` **совместим** со всеми версиями, от Python 3.6 до самых новых, включая Python 3.9, Python 3.10 и т.д.
|
||||
### Generic-типы { #generic-types }
|
||||
|
||||
По мере развития Python **новые версии** получают улучшенную поддержку этих аннотаций типов, и во многих случаях вам даже не нужно импортировать и использовать модуль `typing`, чтобы объявлять аннотации типов.
|
||||
Некоторые типы могут принимать «параметры типов» в квадратных скобках, чтобы определить их внутренние типы. Например, «список строк» объявляется как `list[str]`.
|
||||
|
||||
Если вы можете выбрать более свежую версию Python для проекта, вы получите дополнительную простоту.
|
||||
Такие типы, которые принимают параметры типов, называются **Generic-типами** или **Generics**.
|
||||
|
||||
Во всей документации есть примеры, совместимые с каждой версией Python (когда есть различия).
|
||||
Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
|
||||
|
||||
Например, «**Python 3.6+**» означает совместимость с Python 3.6 и выше (включая 3.7, 3.8, 3.9, 3.10 и т.д.). А «**Python 3.9+**» — совместимость с Python 3.9 и выше (включая 3.10 и т.п.).
|
||||
|
||||
Если вы можете использовать **последние версии Python**, используйте примеры для самой новой версии — у них будет **самый лучший и простой синтаксис**, например, «**Python 3.10+**».
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
#### List { #list }
|
||||
|
||||
@@ -167,7 +170,7 @@ John Doe
|
||||
|
||||
Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
|
||||
|
||||
/// info | Информация
|
||||
|
||||
@@ -193,7 +196,7 @@ John Doe
|
||||
|
||||
Аналогично вы бы объявили `tuple` и `set`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
|
||||
|
||||
Это означает:
|
||||
|
||||
@@ -208,7 +211,7 @@ John Doe
|
||||
|
||||
Второй параметр типа — для значений `dict`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
|
||||
|
||||
Это означает:
|
||||
|
||||
@@ -220,44 +223,20 @@ John Doe
|
||||
|
||||
Вы можете объявить, что переменная может быть **одним из нескольких типов**, например, `int` или `str`.
|
||||
|
||||
В Python 3.6 и выше (включая Python 3.10) вы можете использовать тип `Union` из `typing` и перечислить в квадратных скобках все допустимые типы.
|
||||
Чтобы это определить, используйте <dfn title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</dfn> для разделения обоих типов.
|
||||
|
||||
В Python 3.10 также появился **новый синтаксис**, где допустимые типы можно указать через <abbr title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</abbr>.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
Это называется «объединение» (union), потому что переменная может быть чем угодно из объединения этих двух множеств типов.
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
В обоих случаях это означает, что `item` может быть `int` или `str`.
|
||||
Это означает, что `item` может быть `int` или `str`.
|
||||
|
||||
#### Возможно `None` { #possibly-none }
|
||||
|
||||
Вы можете объявить, что значение может иметь определённый тип, например `str`, но также может быть и `None`.
|
||||
|
||||
В Python 3.6 и выше (включая Python 3.10) это можно объявить, импортировав и используя `Optional` из модуля `typing`.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
Использование `Optional[str]` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.
|
||||
|
||||
`Optional[Something]` — это на самом деле сокращение для `Union[Something, None]`, они эквивалентны.
|
||||
|
||||
Это также означает, что в Python 3.10 вы можете использовать `Something | None`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
@@ -266,96 +245,7 @@ John Doe
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ альтернативный вариант
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Использовать `Union` или `Optional` { #using-union-or-optional }
|
||||
|
||||
Если вы используете версию Python ниже 3.10, вот совет с моей весьма **субъективной** точки зрения:
|
||||
|
||||
* 🚨 Избегайте использования `Optional[SomeType]`
|
||||
* Вместо этого ✨ **используйте `Union[SomeType, None]`** ✨.
|
||||
|
||||
Оба варианта эквивалентны и внутри одинаковы, но я бы рекомендовал `Union` вместо `Optional`, потому что слово «**optional**» («необязательный») может навести на мысль, что значение необязательное, хотя на самом деле оно означает «может быть `None`», даже если параметр не является необязательным и всё ещё обязателен.
|
||||
|
||||
Мне кажется, `Union[SomeType, None]` более явно выражает смысл.
|
||||
|
||||
Речь только о словах и названиях. Но эти слова могут влиять на то, как вы и ваши коллеги думаете о коде.
|
||||
|
||||
В качестве примера возьмём эту функцию:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
|
||||
|
||||
Параметр `name` определён как `Optional[str]`, но он **не необязательный** — вы не можете вызвать функцию без этого параметра:
|
||||
|
||||
```Python
|
||||
say_hi() # О нет, это вызывает ошибку! 😱
|
||||
```
|
||||
|
||||
Параметр `name` всё ещё **обязателен** (не *optional*), потому что у него нет значения по умолчанию. При этом `name` принимает `None` как значение:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # Это работает, None допустим 🎉
|
||||
```
|
||||
|
||||
Хорошая новость: как только вы перейдёте на Python 3.10, об этом можно не переживать — вы сможете просто использовать `|` для объединения типов:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
|
||||
|
||||
И тогда вам не придётся задумываться о названиях вроде `Optional` и `Union`. 😎
|
||||
|
||||
#### Generic-типы { #generic-types }
|
||||
|
||||
Типы, которые принимают параметры типов в квадратных скобках, называются **Generic-типами** или **Generics**, например:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
И, как и в предыдущих версиях Python, из модуля `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...и другие.
|
||||
|
||||
В Python 3.10, как альтернативу generics `Union` и `Optional`, можно использовать <abbr title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</abbr> для объявления объединений типов — это гораздо лучше и проще.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
И generics из модуля `typing`:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...и другие.
|
||||
|
||||
////
|
||||
Использование `str | None` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.
|
||||
|
||||
### Классы как типы { #classes-as-types }
|
||||
|
||||
@@ -363,11 +253,11 @@ say_hi(name=None) # Это работает, None допустим 🎉
|
||||
|
||||
Допустим, у вас есть класс `Person` с именем:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
|
||||
|
||||
Тогда вы можете объявить переменную типа `Person`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
|
||||
|
||||
И снова вы получите полную поддержку редактора кода:
|
||||
|
||||
@@ -401,21 +291,15 @@ say_hi(name=None) # Это работает, None допустим 🎉
|
||||
|
||||
**FastAPI** целиком основан на Pydantic.
|
||||
|
||||
Вы увидите намного больше всего этого на практике в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
У Pydantic есть особое поведение, когда вы используете `Optional` или `Union[Something, None]` без значения по умолчанию. Подробнее читайте в документации Pydantic: <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>.
|
||||
|
||||
///
|
||||
Вы увидите намного больше всего этого на практике в [Учебник - Руководство пользователя](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Подсказки типов с аннотациями метаданных { #type-hints-with-metadata-annotations }
|
||||
|
||||
В Python также есть возможность добавлять **дополнительные <abbr title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</abbr>** к подсказкам типов с помощью `Annotated`.
|
||||
В Python также есть возможность добавлять **дополнительные <dfn title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</dfn>** к подсказкам типов с помощью `Annotated`.
|
||||
|
||||
Начиная с Python 3.9, `Annotated` входит в стандартную библиотеку, поэтому вы можете импортировать его из `typing`.
|
||||
Вы можете импортировать `Annotated` из `typing`.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
|
||||
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
|
||||
|
||||
Сам Python ничего не делает с `Annotated`. А для редакторов кода и других инструментов тип по-прежнему `str`.
|
||||
|
||||
@@ -453,7 +337,7 @@ say_hi(name=None) # Это работает, None допустим 🎉
|
||||
* **Документирования** API с использованием OpenAPI:
|
||||
* что затем используется пользовательскими интерфейсами автоматической интерактивной документации.
|
||||
|
||||
Всё это может звучать абстрактно. Не волнуйтесь. Вы увидите всё это в действии в [Руководстве пользователя](tutorial/index.md){.internal-link target=_blank}.
|
||||
Всё это может звучать абстрактно. Не волнуйтесь. Вы увидите всё это в действии в [Учебник - Руководство пользователя](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
Важно то, что, используя стандартные типы Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.), **FastAPI** сделает за вас большую часть работы.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
Сначала импортируйте `BackgroundTasks` и объявите параметр в вашей функции‑обработчике пути с типом `BackgroundTasks`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
|
||||
|
||||
**FastAPI** создаст объект типа `BackgroundTasks` для вас и передаст его через этот параметр.
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
|
||||
Так как операция записи не использует `async` и `await`, мы определим функцию как обычную `def`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
|
||||
|
||||
## Добавление фоновой задачи { #add-the-background-task }
|
||||
|
||||
Внутри вашей функции‑обработчика пути передайте функцию задачи объекту фоновых задач методом `.add_task()`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
|
||||
|
||||
`.add_task()` принимает следующие аргументы:
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
## Встраивание зависимостей { #dependency-injection }
|
||||
|
||||
Использование `BackgroundTasks` также работает с системой встраивания зависимостей, вы можете объявить параметр типа `BackgroundTasks` на нескольких уровнях: в функции‑обработчике пути, в зависимости (dependable), в подзависимости и т. д.
|
||||
Использование `BackgroundTasks` также работает с системой встраивания зависимостей, вы можете объявить параметр типа `BackgroundTasks` на нескольких уровнях: в функции‑обработчике пути, в зависимости (dependable), в подзависимости и т.д.
|
||||
|
||||
**FastAPI** знает, что делать в каждом случае и как переиспользовать один и тот же объект, так чтобы все фоновые задачи были объединены и затем выполнены в фоне:
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
## Предостережение { #caveat }
|
||||
|
||||
Если вам нужно выполнять тяжелые вычисления в фоне, и при этом они не обязательно должны запускаться тем же процессом (например, вам не нужно делиться памятью, переменными и т. п.), вам могут подойти более мощные инструменты, такие как <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
|
||||
Если вам нужно выполнять тяжелые вычисления в фоне, и при этом они не обязательно должны запускаться тем же процессом (например, вам не нужно делиться памятью, переменными и т.п.), вам могут подойти более мощные инструменты, такие как <a href="https://docs.celeryq.dev" class="external-link" target="_blank">Celery</a>.
|
||||
|
||||
Они обычно требуют более сложной конфигурации, менеджера очереди сообщений/заданий (например, RabbitMQ или Redis), но позволяют запускать фоновые задачи в нескольких процессах и, что особенно важно, на нескольких серверах.
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ from app.routers import items
|
||||
|
||||
Точно так же, как и в случае с классом `FastAPI`, вам нужно импортировать и создать его «экземпляр»:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
|
||||
### *Операции пути* с `APIRouter` { #path-operations-with-apirouter }
|
||||
|
||||
@@ -93,7 +93,7 @@ from app.routers import items
|
||||
|
||||
Используйте его так же, как вы использовали бы класс `FastAPI`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
|
||||
Вы можете думать об `APIRouter` как об «мини-классе `FastAPI`».
|
||||
|
||||
@@ -117,7 +117,7 @@ from app.routers import items
|
||||
|
||||
Теперь мы воспользуемся простой зависимостью, чтобы прочитать кастомный HTTP-заголовок `X-Token`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -149,7 +149,7 @@ from app.routers import items
|
||||
|
||||
Таким образом, вместо того чтобы добавлять всё это в каждую *операцию пути*, мы можем добавить это в `APIRouter`.
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
|
||||
Так как путь каждой *операции пути* должен начинаться с `/`, как здесь:
|
||||
|
||||
@@ -208,7 +208,7 @@ async def read_item(item_id: str):
|
||||
|
||||
Поэтому мы используем относительный импорт с `..` для зависимостей:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
|
||||
#### Как работает относительный импорт { #how-relative-imports-work }
|
||||
|
||||
@@ -279,7 +279,7 @@ from ...dependencies import get_token_header
|
||||
|
||||
Но мы всё равно можем добавить _ещё_ `tags`, которые будут применяться к конкретной *операции пути*, а также дополнительные `responses`, специфичные для этой *операции пути*:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -305,13 +305,13 @@ from ...dependencies import get_token_header
|
||||
|
||||
И мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
|
||||
### Импорт `APIRouter` { #import-the-apirouter }
|
||||
|
||||
Теперь мы импортируем другие подмодули, содержащие `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
|
||||
|
||||
Так как файлы `app/routers/users.py` и `app/routers/items.py` являются подмодулями, входящими в один и тот же Python-пакет `app`, мы можем использовать одну точку `.` для импорта через «относительные импорты».
|
||||
|
||||
@@ -374,13 +374,13 @@ from .routers.users import router
|
||||
|
||||
Поэтому, чтобы иметь возможность использовать обе в одном файле, мы импортируем подмодули напрямую:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
|
||||
|
||||
### Подключение `APIRouter` для `users` и `items` { #include-the-apirouters-for-users-and-items }
|
||||
|
||||
Теперь давайте подключим `router` из подмодулей `users` и `items`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
|
||||
|
||||
/// info | Примечание
|
||||
|
||||
@@ -420,13 +420,13 @@ from .routers.users import router
|
||||
|
||||
Для этого примера всё будет очень просто. Но допустим, что поскольку он используется совместно с другими проектами в организации, мы не можем модифицировать его и добавить `prefix`, `dependencies`, `tags` и т.д. непосредственно в `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
Но мы всё равно хотим задать пользовательский `prefix` при подключении `APIRouter`, чтобы все его *операции пути* начинались с `/admin`, хотим защитить его с помощью `dependencies`, которые у нас уже есть для этого проекта, и хотим включить `tags` и `responses`.
|
||||
|
||||
Мы можем объявить всё это, не изменяя исходный `APIRouter`, передав эти параметры в `app.include_router()`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
|
||||
|
||||
Таким образом исходный `APIRouter` не будет модифицирован, и мы сможем использовать файл `app/internal/admin.py` сразу в нескольких проектах организации.
|
||||
|
||||
@@ -447,7 +447,7 @@ from .routers.users import router
|
||||
|
||||
Здесь мы делаем это... просто чтобы показать, что можем 🤷:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
|
||||
|
||||
и это будет работать корректно вместе со всеми другими *операциями пути*, добавленными через `app.include_router()`.
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
/// note | Внимание
|
||||
/// note | Заметка
|
||||
|
||||
Обратите внимание, что хотя параметр `item` был объявлен таким же способом, как и раньше, теперь предполагается, что он находится внутри тела с ключом `item`.
|
||||
|
||||
@@ -104,12 +104,6 @@
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Или в Python 3.9:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Например:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
||||
@@ -163,7 +163,7 @@ images: list[Image]
|
||||
|
||||
например так:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
|
||||
|
||||
## Поддержка редактора кода везде { #editor-support-everywhere }
|
||||
|
||||
@@ -193,7 +193,7 @@ images: list[Image]
|
||||
|
||||
В этом случае вы принимаете любой `dict`, пока у него есть ключи типа `int` со значениями типа `float`:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
* Проведёт валидацию данных.
|
||||
* Если данные некорректны, вернёт понятную и наглядную ошибку, указывающую, где именно и что было некорректно.
|
||||
* Передаст полученные данные в параметр `item`.
|
||||
* Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т. п.) для всех атрибутов и их типов.
|
||||
* Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т.п.) для всех атрибутов и их типов.
|
||||
* Сгенерирует определения <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта.
|
||||
* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией <abbr title="User Interfaces - Пользовательские интерфейсы">UIs</abbr>.
|
||||
|
||||
@@ -148,14 +148,14 @@ JSON Schema ваших моделей будет частью сгенериро
|
||||
Параметры функции будут распознаны следующим образом:
|
||||
|
||||
* Если параметр также объявлен в **пути**, он будет использоваться как path-параметр.
|
||||
* Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т. п.), он будет интерпретирован как параметр **запроса**.
|
||||
* Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т.п.), он будет интерпретирован как параметр **запроса**.
|
||||
* Если параметр объявлен как тип **модели Pydantic**, он будет интерпретирован как **тело** запроса.
|
||||
|
||||
/// note | Заметка
|
||||
|
||||
FastAPI понимает, что значение `q` не является обязательным из-за значения по умолчанию `= None`.
|
||||
|
||||
Аннотации типов `str | None` (Python 3.10+) или `Union` в `Union[str, None]` (Python 3.9+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
|
||||
Аннотация типов `str | None` не используется FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
|
||||
|
||||
Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки.
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** cookies, которые вы хотите получать.
|
||||
|
||||
Теперь ваш API сам решает, <abbr title="Это шутка, на всякий случай. Это не имеет никакого отношения к согласию на использование cookie, но забавно, что даже API теперь может отклонять несчастные cookies. Съешьте печеньку. 🍪">принимать ли cookies</abbr>. 🤪🍪
|
||||
Теперь у вашего API есть возможность контролировать своё <dfn title="Это шутка, на всякий случай. Это не имеет никакого отношения к согласию на использование cookie, но забавно, что даже API теперь может отклонять несчастные cookies. Съешьте печеньку. 🍪">согласие на использование cookie</dfn>. 🤪🍪
|
||||
|
||||
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) любые дополнительные (`extra`) поля:
|
||||
|
||||
@@ -54,9 +54,9 @@
|
||||
|
||||
Если клиент попробует отправить **дополнительные cookies**, то в ответ он получит **ошибку**.
|
||||
|
||||
Бедные баннеры cookies, они всеми силами пытаются получить ваше согласие — и всё ради того, чтобы <abbr title="Это ещё одна шутка. Не обращайте на меня внимания. Выпейте кофе со своей печенькой. ☕">API его отклонил</abbr>. 🍪
|
||||
Бедные баннеры cookies, они всеми силами пытаются получить ваше согласие — и всё ради того, чтобы <dfn title="Это ещё одна шутка. Не обращайте на меня внимания. Выпейте кофе со своей печенькой. ☕">API его отклонил</dfn>. 🍪
|
||||
|
||||
Например, если клиент попытается отправить cookie `santa_tracker` со значением `good-list-please`, то в ответ он получит **ошибку**, сообщающую ему, что cookie `santa_tracker` <abbr title="Санта не одобряет пропажу печенья. 🎅 Ладно, больше никаких шуток про печенье.">не разрешён</abbr>:
|
||||
Например, если клиент попытается отправить cookie `santa_tracker` со значением `good-list-please`, то в ответ он получит **ошибку**, сообщающую ему, что `santa_tracker` <dfn title="Санта не одобряет пропажу печенья. 🎅 Ладно, больше никаких шуток про печенье.">cookie не разрешён</dfn>:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -73,4 +73,4 @@
|
||||
|
||||
## Заключение { #summary }
|
||||
|
||||
Вы можете использовать **Pydantic-модели** для объявления <abbr title="Съешьте последнюю печеньку, прежде чем уйти. 🍪">**cookies**</abbr> в **FastAPI**. 😎
|
||||
Вы можете использовать **Pydantic-модели** для объявления <dfn title="Съешьте последнюю печеньку, прежде чем уйти. 🍪">**cookies**</dfn> в **FastAPI**. 😎
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
|
||||
/// info | Дополнительная информация
|
||||
|
||||
Имейте в виду, что, поскольку браузеры обрабатывают cookies особым образом и «за кулисами», они не позволяют JavaScript просто так получать к ним доступ.
|
||||
Имейте в виду, что, поскольку **браузеры обрабатывают cookies** особым образом и «за кулисами», они **не** позволяют **JavaScript** просто так получать к ним доступ.
|
||||
|
||||
Если вы откроете интерфейс документации API на `/docs`, вы сможете увидеть документацию по cookies для ваших операций пути.
|
||||
Если вы откроете **интерфейс документации API** на `/docs`, вы сможете увидеть **документацию** по cookies для ваших *операции пути*.
|
||||
|
||||
Но даже если вы заполните данные и нажмёте «Execute», поскольку UI документации работает с JavaScript, cookies отправлены не будут, и вы увидите сообщение об ошибке, как будто вы не указали никаких значений.
|
||||
Но даже если вы **заполните данные** и нажмёте «Execute», поскольку UI документации работает с **JavaScript**, cookies отправлены не будут, и вы увидите сообщение об **ошибке**, как будто вы не указали никаких значений.
|
||||
|
||||
///
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
* Отдельных HTTP-методов (`POST`, `PUT`) или всех вместе, используя `"*"`.
|
||||
* Отдельных HTTP-заголовков или всех вместе, используя `"*"`.
|
||||
|
||||
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
|
||||
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
|
||||
|
||||
`CORSMiddleware` использует "запрещающие" значения по умолчанию, поэтому вам нужно явным образом разрешить использование отдельных источников, методов или заголовков, чтобы браузеры могли использовать их в кросс-доменном контексте.
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
## Больше информации { #more-info }
|
||||
|
||||
Для получения более подробной информации о <abbr title="Cross-Origin Resource Sharing – совместное использование ресурсов между источниками">CORS</abbr> обратитесь к <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">документации CORS от Mozilla</a>.
|
||||
Для получения более подробной информации о <abbr title="Cross-Origin Resource Sharing - совместное использование ресурсов между источниками">CORS</abbr> обратитесь к <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">документации CORS от Mozilla</a>.
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
В вашем FastAPI приложении, импортируйте и вызовите `uvicorn` напрямую:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
|
||||
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
|
||||
|
||||
### Описание `__name__ == "__main__"` { #about-name-main }
|
||||
|
||||
@@ -42,7 +42,7 @@ $ python myapp.py
|
||||
|
||||
то встроенная переменная `__name__`, автоматически создаваемая Python в вашем файле, будет иметь значение строкового типа `"__main__"`.
|
||||
|
||||
Тогда выполнится условие и эта часть кода:
|
||||
Тогда эта часть кода:
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
@@ -59,7 +59,7 @@ $ python myapp.py
|
||||
```Python
|
||||
from myapp import app
|
||||
|
||||
# Some more code
|
||||
# Еще немного кода
|
||||
```
|
||||
|
||||
то автоматическая создаваемая внутри файла `myapp.py` переменная `__name__` будет иметь значение отличающееся от `"__main__"`.
|
||||
@@ -99,7 +99,7 @@ from myapp import app
|
||||
|
||||
---
|
||||
|
||||
Если используете Pycharm, вы можете выполнить следующие шаги:
|
||||
Если используете PyCharm, вы можете выполнить следующие шаги:
|
||||
|
||||
* Открыть "Run" меню.
|
||||
* Выбрать опцию "Debug...".
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## `dict` из предыдущего примера { #a-dict-from-the-previous-example }
|
||||
|
||||
В предыдущем примере мы возвращали `dict` из нашей зависимости:
|
||||
В предыдущем примере мы возвращали `dict` из нашей зависимости («dependable»):
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
@@ -67,7 +67,7 @@ fluffy = Cat(name="Mr Fluffy")
|
||||
|
||||
Это относится и к вызываемым объектам без параметров. Работа с ними происходит точно так же, как и для *функций-обработчиков пути* без параметров.
|
||||
|
||||
Теперь мы можем изменить зависимость `common_parameters`, указанную выше, на класс `CommonQueryParams`:
|
||||
Теперь мы можем изменить зависимость («dependable») `common_parameters`, указанную выше, на класс `CommonQueryParams`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
|
||||
|
||||
@@ -101,7 +101,7 @@ fluffy = Cat(name="Mr Fluffy")
|
||||
|
||||
Обратите внимание, что в приведенном выше коде мы два раза пишем `CommonQueryParams`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ без Annotated
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -137,7 +137,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
|
||||
В этом случае первый `CommonQueryParams`, в:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, ...
|
||||
@@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ без Annotated
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -163,7 +163,7 @@ commons: CommonQueryParams ...
|
||||
|
||||
На самом деле можно написать просто:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
@@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ без Annotated
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -197,7 +197,7 @@ commons = Depends(CommonQueryParams)
|
||||
|
||||
Но вы видите, что здесь мы имеем некоторое повторение кода, дважды написав `CommonQueryParams`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ без Annotated
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -225,7 +225,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
|
||||
Вместо того чтобы писать:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ без Annotated
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
|
||||
...следует написать:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends()]
|
||||
@@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ без Annotated
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
Это должен быть `list` состоящий из `Depends()`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
|
||||
|
||||
Зависимости из dependencies выполнятся так же, как и обычные зависимости. Но их значения (если они были) не будут переданы в *функцию операции пути*.
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
/// info | Примечание
|
||||
|
||||
В этом примере мы используем выдуманные пользовательские заголовки `X-Key` и `X-Token`.
|
||||
В этом примере мы используем выдуманные пользовательские HTTP-заголовки `X-Key` и `X-Token`.
|
||||
|
||||
Но в реальных проектах, при внедрении системы безопасности, вы получите больше пользы используя интегрированные [средства защиты (следующая глава)](../security/index.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -42,15 +42,15 @@
|
||||
|
||||
### Требования к зависимостям { #dependency-requirements }
|
||||
|
||||
Они могут объявлять требования к запросу (например заголовки) или другие подзависимости:
|
||||
Они могут объявлять требования к запросу (например HTTP-заголовки) или другие подзависимости:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
|
||||
|
||||
### Вызов исключений { #raise-exceptions }
|
||||
|
||||
Зависимости из dependencies могут вызывать исключения с помощью `raise`, как и обычные зависимости:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
|
||||
|
||||
### Возвращаемые значения { #return-values }
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
Таким образом, вы можете переиспользовать обычную зависимость (возвращающую значение), которую вы уже используете где-то в другом месте, и хотя значение не будет использоваться, зависимость будет выполнена:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
|
||||
|
||||
## Зависимости для группы *операций путей* { #dependencies-for-a-group-of-path-operations }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Зависимости с yield { #dependencies-with-yield }
|
||||
|
||||
FastAPI поддерживает зависимости, которые выполняют некоторые <abbr title='иногда также называемые "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code" и т.п.'>дополнительные шаги после завершения</abbr>.
|
||||
FastAPI поддерживает зависимости, которые выполняют некоторые <dfn title='иногда также называемые "код выхода", "код очистки", "код завершения", "код закрытия", "код выхода менеджера контекста" и т.п.'>дополнительные шаги после завершения</dfn>.
|
||||
|
||||
Для этого используйте `yield` вместо `return`, а дополнительные шаги (код) напишите после него.
|
||||
|
||||
@@ -29,15 +29,15 @@ FastAPI поддерживает зависимости, которые выпо
|
||||
|
||||
Перед созданием ответа будет выполнен только код до и включая оператор `yield`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
|
||||
|
||||
Значение, полученное из `yield`, внедряется в *операции пути* и другие зависимости:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
|
||||
|
||||
Код, следующий за оператором `yield`, выполняется после ответа:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -57,7 +57,7 @@ FastAPI поддерживает зависимости, которые выпо
|
||||
|
||||
Точно так же можно использовать `finally`, чтобы убедиться, что обязательные шаги при выходе выполнены независимо от того, было ли исключение или нет.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
|
||||
|
||||
## Подзависимости с `yield` { #sub-dependencies-with-yield }
|
||||
|
||||
@@ -67,7 +67,7 @@ FastAPI поддерживает зависимости, которые выпо
|
||||
|
||||
Например, `dependency_c` может зависеть от `dependency_b`, а `dependency_b` — от `dependency_a`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
|
||||
|
||||
И все они могут использовать `yield`.
|
||||
|
||||
@@ -75,7 +75,7 @@ FastAPI поддерживает зависимости, которые выпо
|
||||
|
||||
И, в свою очередь, `dependency_b` нуждается в том, чтобы значение из `dependency_a` (здесь `dep_a`) было доступно для её кода выхода.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
|
||||
|
||||
Точно так же можно иметь часть зависимостей с `yield`, часть — с `return`, и какие-то из них могут зависеть друг от друга.
|
||||
|
||||
@@ -109,7 +109,7 @@ FastAPI поддерживает зависимости, которые выпо
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
|
||||
|
||||
Если вы хотите перехватывать исключения и формировать на их основе пользовательский ответ, создайте [Пользовательский обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
@@ -117,7 +117,7 @@ FastAPI поддерживает зависимости, которые выпо
|
||||
|
||||
Если вы ловите исключение с помощью `except` в зависимости с `yield` и не вызываете его снова (или не вызываете новое исключение), FastAPI не сможет заметить, что было исключение — так же, как это происходит в обычном Python:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
|
||||
|
||||
В этом случае клиент получит *HTTP 500 Internal Server Error*, как и должно быть, поскольку мы не вызываем `HTTPException` или что-то подобное, но на сервере **не будет никаких логов** или других указаний на то, какая была ошибка. 😱
|
||||
|
||||
@@ -127,7 +127,7 @@ FastAPI поддерживает зависимости, которые выпо
|
||||
|
||||
Вы можете повторно вызвать то же самое исключение с помощью `raise`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
|
||||
|
||||
Теперь клиент получит тот же *HTTP 500 Internal Server Error*, но на сервере в логах будет наше пользовательское `InternalError`. 😎
|
||||
|
||||
@@ -190,7 +190,7 @@ participant tasks as Background tasks
|
||||
|
||||
Но если вы знаете, что не будете использовать зависимость после возврата из *функции-обработчика пути*, вы можете использовать `Depends(scope="function")`, чтобы сообщить FastAPI, что он должен закрыть зависимость после возврата из *функции-обработчика пути*, но **до того**, как **ответ будет отправлен**.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
|
||||
|
||||
`Depends()` принимает параметр `scope`, который может быть:
|
||||
|
||||
@@ -269,7 +269,7 @@ with open("./somefile.txt") as f:
|
||||
Их также можно использовать внутри зависимостей **FastAPI** с `yield`, применяя операторы
|
||||
`with` или `async with` внутри функции зависимости:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
В этом случае они будут применяться ко всем *операциям пути* в приложении:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
|
||||
|
||||
Все способы [добавления `dependencies` (зависимостей) в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} по-прежнему применимы, но в данном случае зависимости применяются ко всем *операциям пути* приложения.
|
||||
|
||||
## Зависимости для групп *операций пути* { #dependencies-for-groups-of-path-operations }
|
||||
|
||||
Позднее, читая о том, как структурировать более крупные [приложения, содержащие много файлов](../../tutorial/bigger-applications.md){.internal-link target=_blank}, вы узнаете, как объявить один параметр `dependencies` для целой группы *операций пути*.
|
||||
Позднее, читая о том, как структурировать более крупные приложения ([приложения, содержащие много файлов](../../tutorial/bigger-applications.md){.internal-link target=_blank}), возможно, состоящие из нескольких файлов, вы узнаете, как объявить один параметр `dependencies` для целой группы *операций пути*.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Зависимости { #dependencies }
|
||||
|
||||
**FastAPI** имеет очень мощную, но интуитивную систему **<abbr title="также известно как: компоненты, ресурсы, провайдеры, сервисы, внедряемые зависимости">Инъекция зависимостей</abbr>**.
|
||||
**FastAPI** имеет очень мощную, но интуитивную систему **<dfn title="также известно как: компоненты, ресурсы, провайдеры, сервисы, внедряемые зависимости">Инъекция зависимостей</dfn>**.
|
||||
|
||||
Она спроектирована так, чтобы быть очень простой в использовании и облегчать любому разработчику интеграцию других компонентов с **FastAPI**.
|
||||
|
||||
|
||||
@@ -58,11 +58,11 @@ query_extractor --> query_or_cookie_extractor --> read_query
|
||||
|
||||
Если одна из ваших зависимостей объявлена несколько раз для одной и той же *функции операции пути*, например, несколько зависимостей имеют общую подзависимость, **FastAPI** будет знать, что вызывать эту подзависимость нужно только один раз за запрос.
|
||||
|
||||
При этом возвращаемое значение будет сохранено в <abbr title="Система для хранения значений, сгенерированных компьютером, для их повторного использования вместо повторного вычисления.">"кэш"</abbr> и будет передано всем "зависимым" функциям, которые нуждаются в нем внутри этого конкретного запроса, вместо того, чтобы вызывать зависимость несколько раз для одного и того же запроса.
|
||||
При этом возвращаемое значение будет сохранено в <dfn title="Вспомогательная система для хранения вычисленных/сгенерированных значений, чтобы переиспользовать их вместо повторного вычисления.">«кэш»</dfn> и будет передано всем "зависимым" функциям, которые нуждаются в нем внутри этого конкретного запроса, вместо того, чтобы вызывать зависимость несколько раз для одного и того же запроса.
|
||||
|
||||
В расширенном сценарии, когда вы знаете, что вам нужно, чтобы зависимость вызывалась на каждом шаге (возможно, несколько раз) в одном и том же запросе, вместо использования "кэшированного" значения, вы можете установить параметр `use_cache=False` при использовании `Depends`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
@@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ без Annotated
|
||||
//// tab | Python 3.10+ без Annotated
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
Представим, что у вас есть база данных `fake_db`, которая принимает только JSON-совместимые данные.
|
||||
|
||||
Например, он не принимает объекты `datetime`, так как они не совместимы с JSON.
|
||||
Например, она не принимает объекты `datetime`, так как они не совместимы с JSON.
|
||||
|
||||
В таком случае объект `datetime` следует преобразовать в строку соответствующую <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">формату ISO</a>.
|
||||
В таком случае объект `datetime` следует преобразовать в строку, соответствующую <a href="https://en.wikipedia.org/wiki/ISO_8601" class="external-link" target="_blank">формату ISO</a>.
|
||||
|
||||
Точно так же эта база данных не может принять Pydantic-модель (объект с атрибутами), а только `dict`.
|
||||
|
||||
|
||||
@@ -190,9 +190,9 @@ some_variable: PlaneItem | CarItem
|
||||
|
||||
Таким же образом вы можете объявлять HTTP-ответы, возвращающие списки объектов.
|
||||
|
||||
Для этого используйте стандартный `typing.List` в Python (или просто `list` в Python 3.9 и выше):
|
||||
Для этого используйте стандартный `list` в Python:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
|
||||
|
||||
## Ответ с произвольным `dict` { #response-with-arbitrary-dict }
|
||||
|
||||
@@ -200,9 +200,9 @@ some_variable: PlaneItem | CarItem
|
||||
|
||||
Это полезно, если вы заранее не знаете корректных названий полей/атрибутов (которые будут нужны при использовании Pydantic-модели).
|
||||
|
||||
В этом случае вы можете использовать `typing.Dict` (или просто `dict` в Python 3.9 и выше):
|
||||
В этом случае вы можете использовать `dict`:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
|
||||
|
||||
## Резюме { #recap }
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Самый простой файл FastAPI может выглядеть так:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py *}
|
||||
|
||||
Скопируйте это в файл `main.py`.
|
||||
|
||||
@@ -183,7 +183,7 @@ Deploying to FastAPI Cloud...
|
||||
|
||||
### Шаг 1: импортируйте `FastAPI` { #step-1-import-fastapi }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
|
||||
|
||||
`FastAPI` — это класс на Python, который предоставляет всю функциональность для вашего API.
|
||||
|
||||
@@ -197,7 +197,7 @@ Deploying to FastAPI Cloud...
|
||||
|
||||
### Шаг 2: создайте экземпляр `FastAPI` { #step-2-create-a-fastapi-instance }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
|
||||
|
||||
Здесь переменная `app` будет экземпляром класса `FastAPI`.
|
||||
|
||||
@@ -266,12 +266,12 @@ https://example.com/items/foo
|
||||
|
||||
#### Определите *декоратор операции пути (path operation decorator)* { #define-a-path-operation-decorator }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
|
||||
|
||||
`@app.get("/")` сообщает **FastAPI**, что функция прямо под ним отвечает за обработку запросов, поступающих:
|
||||
|
||||
* по пути `/`
|
||||
* с использованием <abbr title="метод HTTP GET"><code>get</code> операции</abbr>
|
||||
* с использованием <dfn title="метод HTTP GET"><code>get</code> операции</dfn>
|
||||
|
||||
/// info | Информация о `@decorator`
|
||||
|
||||
@@ -320,7 +320,7 @@ https://example.com/items/foo
|
||||
* **операция**: `get`.
|
||||
* **функция**: функция ниже «декоратора» (ниже `@app.get("/")`).
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
|
||||
|
||||
Это функция на Python.
|
||||
|
||||
@@ -332,7 +332,7 @@ https://example.com/items/foo
|
||||
|
||||
Вы также можете определить её как обычную функцию вместо `async def`:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
@@ -342,7 +342,7 @@ https://example.com/items/foo
|
||||
|
||||
### Шаг 5: верните содержимое { #step-5-return-the-content }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Вы можете вернуть `dict`, `list`, отдельные значения `str`, `int` и т.д.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Элемент, к которому клиент пытался получить доступ, не существует.
|
||||
* и т.д.
|
||||
|
||||
В таких случаях обычно возвращается **HTTP-код статуса ответа** в диапазоне **400** (от 400 до 499).
|
||||
В таких случаях обычно возвращают **HTTP статус-код** в диапазоне **400** (от 400 до 499).
|
||||
|
||||
Они похожи на двухсотые HTTP статус-коды (от 200 до 299), которые означают, что запрос обработан успешно.
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
### Импортируйте `HTTPException` { #import-httpexception }
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
|
||||
|
||||
### Вызовите `HTTPException` в своем коде { #raise-an-httpexception-in-your-code }
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
В данном примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
|
||||
|
||||
### Возвращаемый ответ { #the-resulting-response }
|
||||
|
||||
@@ -71,13 +71,13 @@
|
||||
|
||||
## Добавление пользовательских заголовков { #add-custom-headers }
|
||||
|
||||
В некоторых ситуациях полезно иметь возможность добавлять пользовательские заголовки к ошибке HTTP. Например, для некоторых типов безопасности.
|
||||
В некоторых ситуациях полезно иметь возможность добавлять пользовательские HTTP-заголовки к ошибке HTTP. Например, для некоторых типов безопасности.
|
||||
|
||||
Скорее всего, вам не потребуется использовать его непосредственно в коде.
|
||||
|
||||
Но в случае, если это необходимо для продвинутого сценария, можно добавить пользовательские заголовки:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
|
||||
|
||||
## Установка пользовательских обработчиков исключений { #install-custom-exception-handlers }
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
Можно добавить собственный обработчик исключений с помощью `@app.exception_handler()`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
|
||||
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
|
||||
|
||||
Здесь, если запросить `/unicorns/yolo`, то *операция пути* вызовет `UnicornException`.
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
Обработчик исключения получит объект `Request` и исключение.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
|
||||
|
||||
Теперь, если перейти к `/items/foo`, то вместо стандартной JSON-ошибки с:
|
||||
|
||||
@@ -159,7 +159,7 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
|
||||
|
||||
Например, для этих ошибок можно вернуть обычный текстовый ответ вместо JSON:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
@@ -183,7 +183,7 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
|
||||
|
||||
Вы можете использовать его при разработке приложения для регистрации тела и его отладки, возврата пользователю и т.д.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
|
||||
|
||||
Теперь попробуйте отправить недействительный элемент, например:
|
||||
|
||||
@@ -239,6 +239,6 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
|
||||
Если вы хотите использовать исключение вместе с теми же обработчиками исключений по умолчанию из **FastAPI**, вы можете импортировать и повторно использовать обработчики исключений по умолчанию из `fastapi.exception_handlers`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
|
||||
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
|
||||
|
||||
В этом примере вы просто `выводите в терминал` ошибку с очень выразительным сообщением, но идея вам понятна. Вы можете использовать исключение, а затем просто повторно использовать стандартные обработчики исключений.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
Вы можете задать их следующим образом:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
|
||||
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
К примеру:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
|
||||
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
|
||||
|
||||
## Метаданные для тегов { #metadata-for-tags }
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
Создайте метаданные для ваших тегов и передайте их в параметре `openapi_tags`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
|
||||
|
||||
Помните, что вы можете использовать Markdown внутри описания, к примеру "login" будет отображен жирным шрифтом (**login**) и "fancy" будет отображаться курсивом (_fancy_).
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
|
||||
Используйте параметр `tags` с вашими *операциями пути* (и `APIRouter`ами), чтобы присвоить им различные теги:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
|
||||
|
||||
/// info | Дополнительная информация
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
|
||||
К примеру, чтобы задать её отображение по адресу `/api/v1/openapi.json`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Если вы хотите отключить схему OpenAPI полностью, вы можете задать `openapi_url=None`, это также отключит пользовательские интерфейсы документации, которые её используют.
|
||||
|
||||
@@ -117,4 +117,4 @@
|
||||
|
||||
К примеру, чтобы задать отображение Swagger UI по адресу `/documentation` и отключить ReDoc:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# Middleware (Промежуточный слой) { #middleware }
|
||||
|
||||
Вы можете добавить промежуточный слой (middleware) в **FastAPI** приложение.
|
||||
|
||||
"Middleware" это функция, которая выполняется с каждым запросом до его обработки какой-либо конкретной *операцией пути*.
|
||||
А также с каждым ответом перед его возвращением.
|
||||
Вы можете добавить middleware (промежуточный слой) в **FastAPI** приложение.
|
||||
|
||||
"Middleware" - это функция, которая выполняется с каждым **запросом** до его обработки какой-либо конкретной *операцией пути*. А также с каждым **ответом** перед его возвращением.
|
||||
|
||||
* Она принимает каждый поступающий **запрос**.
|
||||
* Может что-то сделать с этим **запросом** или выполнить любой нужный код.
|
||||
@@ -23,23 +21,23 @@
|
||||
|
||||
## Создание middleware { #create-a-middleware }
|
||||
|
||||
Для создания middleware используйте декоратор `@app.middleware("http")`.
|
||||
Для создания middleware используйте декоратор `@app.middleware("http")` поверх функции.
|
||||
|
||||
Функция middleware получает:
|
||||
|
||||
* `request` (объект запроса).
|
||||
* `request`.
|
||||
* Функцию `call_next`, которая получает `request` в качестве параметра.
|
||||
* Эта функция передаёт `request` соответствующей *операции пути*.
|
||||
* Затем она возвращает ответ `response`, сгенерированный *операцией пути*.
|
||||
* Также имеется возможность видоизменить `response`, перед тем как его вернуть.
|
||||
* Затем она возвращает `response`, сгенерированный соответствующей *операцией пути*.
|
||||
* Также имеется возможность видоизменить `response` перед тем как его вернуть.
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip | Примечание
|
||||
/// tip | Совет
|
||||
|
||||
Имейте в виду, что можно добавлять свои собственные заголовки <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">при помощи префикса 'X-'</a>.
|
||||
Имейте в виду, что можно добавлять проприетарные HTTP-заголовки <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}), используя параметр `expose_headers`, см. документацию <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's CORS docs</a>.
|
||||
Но если вы хотите, чтобы клиент в браузере мог видеть ваши пользовательские заголовки, необходимо добавить их в настройки CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}), используя параметр `expose_headers`, описанный в <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">документации по CORS Starlette</a>.
|
||||
|
||||
///
|
||||
|
||||
@@ -53,17 +51,17 @@
|
||||
|
||||
### До и после `response` { #before-and-after-the-response }
|
||||
|
||||
Вы можете добавить код, использующий `request` до передачи его какой-либо *операции пути*.
|
||||
Вы можете добавить код, использующий `request`, до передачи его какой-либо *операции пути*.
|
||||
|
||||
А также после формирования `response`, до того, как вы его вернёте.
|
||||
|
||||
Например, вы можете добавить собственный заголовок `X-Process-Time`, содержащий время в секундах, необходимое для обработки запроса и генерации ответа:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
|
||||
|
||||
/// tip | Примечание
|
||||
/// tip | Совет
|
||||
|
||||
Мы используем <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> вместо `time.time()` для обеспечения большей точности наших примеров. 🤓
|
||||
Мы используем <a href="https://docs.python.org/3/library/time.html#time.perf_counter" class="external-link" target="_blank">`time.perf_counter()`</a> вместо `time.time()` для обеспечения большей точности в таких случаях. 🤓
|
||||
|
||||
///
|
||||
|
||||
@@ -94,4 +92,4 @@ app.add_middleware(MiddlewareB)
|
||||
|
||||
О других middleware вы можете узнать больше в разделе [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
|
||||
|
||||
В следующем разделе вы можете прочитать, как настроить <abbr title="Cross-Origin Resource Sharing – совместное использование ресурсов между источниками">CORS</abbr> с помощью middleware.
|
||||
В следующем разделе вы можете прочитать, как настроить <abbr title="Cross-Origin Resource Sharing - совместное использование ресурсов между источниками">CORS</abbr> с помощью middleware.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
/// warning | Внимание
|
||||
|
||||
Помните, что эти параметры передаются непосредственно *декоратору операций пути*, а не вашей *функции-обработчику операций пути*.
|
||||
Помните, что эти параметры передаются непосредственно *декоратору операций пути*, а не вашей *функции-обработчику пути*.
|
||||
|
||||
///
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
**FastAPI** поддерживает это так же, как и в случае с обычными строками:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
|
||||
|
||||
## Краткое и развёрнутое содержание { #summary-and-description }
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
## Описание из строк документации { #description-from-docstring }
|
||||
|
||||
Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в функции <abbr title="многострочный текст, первое выражение внутри функции (не присвоенный какой-либо переменной), используемый для документации">строки документации</abbr> и **FastAPI** прочитает её отсюда.
|
||||
Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в <dfn title="многострочная строка, первое выражение внутри функции (не присвоенное какой-либо переменной), используемая для документации">строке документации</dfn> функции, и **FastAPI** прочитает её оттуда.
|
||||
|
||||
Вы можете использовать <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> в строке документации, и он будет интерпретирован и отображён корректно (с учетом отступа в строке документации).
|
||||
|
||||
@@ -90,9 +90,9 @@ OpenAPI указывает, что каждой *операции пути* не
|
||||
|
||||
## Обозначение *операции пути* как устаревшей { #deprecate-a-path-operation }
|
||||
|
||||
Если вам необходимо пометить *операцию пути* как <abbr title="устаревшее, не рекомендовано к использованию">устаревшую</abbr>, при этом не удаляя её, передайте параметр `deprecated`:
|
||||
Если вам необходимо пометить *операцию пути* как <dfn title="устаревшая, рекомендуется не использовать">устаревшую</dfn>, при этом не удаляя её, передайте параметр `deprecated`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
|
||||
|
||||
Он будет четко помечен как устаревший в интерактивной документации:
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ Path-параметр всегда является обязательным, п
|
||||
|
||||
И если вам больше ничего не нужно указывать для этого параметра, то нет необходимости использовать `Query`.
|
||||
|
||||
Но вам по-прежнему нужно использовать `Path` для path-параметра `item_id`. И если по какой-либо причине вы не хотите использовать `Annotated`, то могут возникнуть небольшие сложности.
|
||||
Но вам по-прежнему нужно использовать `Path` для path-параметра `item_id`. И по какой-либо причине вы не хотите использовать `Annotated`.
|
||||
|
||||
Если вы поместите параметр со значением по умолчанию перед другим параметром, у которого нет значения по умолчанию, то Python укажет на ошибку.
|
||||
|
||||
@@ -54,11 +54,11 @@ Path-параметр всегда является обязательным, п
|
||||
|
||||
Поэтому вы можете определить функцию так:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Но имейте в виду, что если вы используете `Annotated`, вы не столкнётесь с этой проблемой, так как вы не используете значения по умолчанию параметров функции для `Query()` или `Path()`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
|
||||
|
||||
## Задайте нужный вам порядок параметров, полезные приёмы { #order-the-parameters-as-you-need-tricks }
|
||||
|
||||
@@ -83,13 +83,13 @@ Path-параметр всегда является обязательным, п
|
||||
|
||||
Python не будет ничего делать с `*`, но он будет знать, что все следующие параметры являются именованными аргументами (парами ключ-значение), также известными как <abbr title="От: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>, даже если у них нет значений по умолчанию.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
|
||||
|
||||
### Лучше с `Annotated` { #better-with-annotated }
|
||||
|
||||
Имейте в виду, что если вы используете `Annotated`, то, поскольку вы не используете значений по умолчанию для параметров функции, у вас не возникнет подобной проблемы и вам, вероятно, не придётся использовать `*`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## Валидация числовых данных: больше или равно { #number-validations-greater-than-or-equal }
|
||||
|
||||
@@ -97,7 +97,7 @@ Python не будет ничего делать с `*`, но он будет з
|
||||
|
||||
В этом примере при указании `ge=1`, параметр `item_id` должен быть целым числом "`g`reater than or `e`qual" — больше или равно `1`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
|
||||
|
||||
## Валидация числовых данных: больше и меньше или равно { #number-validations-greater-than-and-less-than-or-equal }
|
||||
|
||||
@@ -106,7 +106,7 @@ Python не будет ничего делать с `*`, но он будет з
|
||||
* `gt`: больше (`g`reater `t`han)
|
||||
* `le`: меньше или равно (`l`ess than or `e`qual)
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
|
||||
|
||||
## Валидация числовых данных: числа с плавающей точкой, больше и меньше { #number-validations-floats-greater-than-and-less-than }
|
||||
|
||||
@@ -118,7 +118,7 @@ Python не будет ничего делать с `*`, но он будет з
|
||||
|
||||
То же самое справедливо и для <abbr title="less than – меньше чем"><code>lt</code></abbr>.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
|
||||
|
||||
## Резюме { #recap }
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Вы можете определить "параметры" или "переменные" пути, используя синтаксис форматированных строк Python:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
|
||||
|
||||
Значение параметра пути `item_id` будет передано в функцию в качестве аргумента `item_id`.
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
Вы можете объявить тип параметра пути в функции, используя стандартные аннотации типов Python:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
Здесь, `item_id` объявлен типом `int`.
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
///
|
||||
|
||||
## <abbr title="также известное как: сериализация, парсинг, маршаллинг">Преобразование</abbr> данных { #data-conversion }
|
||||
## <dfn title="также известно как: сериализация, парсинг, маршаллинг">Преобразование</dfn> данных { #data-conversion }
|
||||
|
||||
Если запустите этот пример и перейдёте по адресу: <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, то увидите ответ:
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
Обратите внимание на значение `3`, которое получила (и вернула) функция. Это целочисленный Python `int`, а не строка `"3"`.
|
||||
|
||||
Используя такое объявление типов, **FastAPI** выполняет автоматический <abbr title="преобразование строк из HTTP-запроса в типы данных Python">"парсинг"</abbr> запросов.
|
||||
Используя такое объявление типов, **FastAPI** выполняет автоматический HTTP-запрос <dfn title="преобразование строки, которая приходит из HTTP-запроса, в данные Python">"парсинг"</dfn>.
|
||||
|
||||
///
|
||||
|
||||
@@ -118,13 +118,13 @@
|
||||
|
||||
Поскольку *операции пути* выполняются в порядке их объявления, необходимо, чтобы путь для `/users/me` был объявлен раньше, чем путь для `/users/{user_id}`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
|
||||
|
||||
Иначе путь для `/users/{user_id}` также будет соответствовать `/users/me`, "подразумевая", что он получает параметр `user_id` со значением `"me"`.
|
||||
|
||||
Аналогично, вы не можете переопределить операцию с путем:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
|
||||
|
||||
Первый будет выполняться всегда, так как путь совпадает первым.
|
||||
|
||||
@@ -140,11 +140,11 @@
|
||||
|
||||
Затем создайте атрибуты класса с фиксированными допустимыми значениями:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия <abbr title="Технически, архитектуры моделей глубокого обучения">моделей</abbr> Машинного обучения.
|
||||
Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия <dfn title="Технически, архитектуры моделей глубокого обучения">моделей</dfn> Машинного обучения.
|
||||
|
||||
///
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
|
||||
Определите *параметр пути*, используя в аннотации типа класс перечисления (`ModelName`), созданный ранее:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
|
||||
|
||||
### Проверьте документацию { #check-the-docs }
|
||||
|
||||
@@ -168,13 +168,13 @@
|
||||
|
||||
Вы можете сравнить это значение с *элементом перечисления* класса `ModelName`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
|
||||
|
||||
#### Получение *значения перечисления* { #get-the-enumeration-value }
|
||||
|
||||
Можно получить фактическое значение (в данном случае - `str`) с помощью `model_name.value` или в общем случае `your_enum_member.value`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -188,8 +188,8 @@
|
||||
|
||||
Они будут преобразованы в соответствующие значения (в данном случае - строки) перед их возвратом клиенту:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
|
||||
Вы отправите клиенту такой JSON-ответ:
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
|
||||
На стороне клиента вы получите такой JSON-ответ:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -226,7 +226,7 @@ OpenAPI не поддерживает способов объявления *п
|
||||
|
||||
Можете использовать так:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -241,7 +241,7 @@ OpenAPI не поддерживает способов объявления *п
|
||||
Используя **FastAPI** вместе со стандартными объявлениями типов Python (короткими и интуитивно понятными), вы получаете:
|
||||
|
||||
* Поддержку редактора кода (проверку ошибок, автозавершение и т.п.)
|
||||
* "<abbr title="преобразование строк из HTTP-запроса в типы данных Python">Парсинг</abbr>" данных
|
||||
* "<dfn title="преобразование строки, которая приходит из HTTP-запроса, в данные Python">Парсинг</dfn>" данных
|
||||
* Валидацию данных
|
||||
* Аннотации API и автоматическую документацию
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
/// note | Заметка
|
||||
|
||||
Этот функционал доступен с версии `0.115.0`. 🤓
|
||||
Это поддерживается начиная с версии FastAPI `0.115.0`. 🤓
|
||||
|
||||
///
|
||||
|
||||
|
||||
@@ -47,40 +47,16 @@ FastAPI поймёт, что значение `q` не обязательно,
|
||||
|
||||
У нас была такая аннотация типа:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Мы «обернём» это в `Annotated`, и получится:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Обе версии означают одно и то же: `q` — параметр, который может быть `str` или `None`, и по умолчанию равен `None`.
|
||||
|
||||
А теперь к самому интересному. 🎉
|
||||
@@ -109,7 +85,7 @@ q: Annotated[Union[str, None]] = None
|
||||
|
||||
## Альтернатива (устаревшее): `Query` как значение по умолчанию { #alternative-old-query-as-the-default-value }
|
||||
|
||||
В предыдущих версиях FastAPI (до <abbr title="до 2023-03">0.95.0</abbr>) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
|
||||
В предыдущих версиях FastAPI (до <dfn title="до 2023-03">0.95.0</dfn>) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -191,7 +167,7 @@ q: str = Query(default="rick")
|
||||
|
||||
## Регулярные выражения { #add-regular-expressions }
|
||||
|
||||
Вы можете определить <abbr title="Регулярное выражение (regex, regexp) - это последовательность символов, задающая шаблон поиска для строк.">регулярное выражение</abbr> `pattern`, которому должен соответствовать параметр:
|
||||
Вы можете определить <dfn title="Регулярное выражение (regex, regexp) — это последовательность символов, задающая шаблон поиска для строк.">регулярное выражение</dfn> `pattern`, которому должен соответствовать параметр:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
|
||||
|
||||
@@ -211,7 +187,7 @@ q: str = Query(default="rick")
|
||||
|
||||
Допустим, вы хотите объявить, что query-параметр `q` должен иметь `min_length` равный `3` и значение по умолчанию `"fixedquery"`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
@@ -241,7 +217,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
|
||||
|
||||
Поэтому, когда вам нужно объявить значение как обязательное при использовании `Query`, просто не указывайте значение по умолчанию:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
|
||||
|
||||
### Обязательный, но может быть `None` { #required-can-be-none }
|
||||
|
||||
@@ -292,7 +268,7 @@ http://localhost:8000/items/?q=foo&q=bar
|
||||
|
||||
Можно также определить значение по умолчанию как `list`, если ничего не передано:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
|
||||
|
||||
Если вы перейдёте по адресу:
|
||||
|
||||
@@ -315,7 +291,7 @@ http://localhost:8000/items/
|
||||
|
||||
Можно использовать `list` напрямую вместо `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
@@ -371,7 +347,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
|
||||
Предположим, этот параметр вам больше не нравится.
|
||||
|
||||
Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как <abbr title="устаревший, не рекомендуется использовать">устаревший</abbr>.
|
||||
Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как <dfn title="устаревший, не рекомендуется использовать">устаревший</dfn>.
|
||||
|
||||
Тогда передайте параметр `deprecated=True` в `Query`:
|
||||
|
||||
@@ -401,7 +377,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
|
||||
///
|
||||
|
||||
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbr title="ISBN означает International Standard Book Number - Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма на <abbr title="IMDB (Internet Movie Database) - веб‑сайт с информацией о фильмах">IMDB</abbr>:
|
||||
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbr title="International Standard Book Number - Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма на <abbr title="Internet Movie Database - Интернетная база данных фильмов: веб‑сайт с информацией о фильмах">IMDB</abbr>:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
@@ -435,7 +411,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
|
||||
#### Случайный элемент { #a-random-item }
|
||||
|
||||
С помощью `data.items()` мы получаем <abbr title="Объект, по которому можно итерироваться циклом for, например список, множество и т. п.">итерируемый объект</abbr> с кортежами, содержащими ключ и значение для каждого элемента словаря.
|
||||
С помощью `data.items()` мы получаем <dfn title="Объект, по которому можно итерироваться циклом for, например список, множество и т.п.">итерируемый объект</dfn> с кортежами, содержащими ключ и значение для каждого элемента словаря.
|
||||
|
||||
Мы превращаем этот итерируемый объект в обычный `list` через `list(data.items())`.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Когда вы объявляете параметры функции, которые не являются параметрами пути, они автоматически интерпретируются как "query"-параметры.
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
|
||||
|
||||
Query-параметры представляют из себя набор пар ключ-значение, которые идут после знака `?` в URL-адресе, разделенные символами `&`.
|
||||
|
||||
@@ -24,7 +24,7 @@ http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
Все те же правила, которые применяются к path-параметрам, также применяются и query-параметрам:
|
||||
|
||||
* Поддержка от редактора кода (очевидно)
|
||||
* <abbr title="преобразование строки, полученной из HTTP запроса в Python данные">"Парсинг"</abbr> данных
|
||||
* <dfn title="преобразование строки, полученной из HTTP-запроса в данные Python">"Парсинг"</dfn> данных
|
||||
* Проверка на соответствие данных (Валидация)
|
||||
* Автоматическая документация
|
||||
|
||||
@@ -121,13 +121,13 @@ http://127.0.0.1:8000/items/foo?short=yes
|
||||
|
||||
## Обязательные query-параметры { #required-query-parameters }
|
||||
|
||||
Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе, мы пока что познакомились только с path-параметрами), то он не является обязательным.
|
||||
Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе мы пока что рассмотрели только query-параметры), то он не является обязательным.
|
||||
|
||||
Если вы не хотите задавать конкретное значение, но хотите сделать параметр необязательным, вы можете установить значение по умолчанию равным `None`.
|
||||
|
||||
Но если вы хотите сделать query-параметр обязательным, вы можете просто не указывать значение по умолчанию:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
|
||||
|
||||
Здесь параметр запроса `needy` является обязательным параметром с типом данных `str`.
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ $ pip install python-multipart
|
||||
|
||||
Импортируйте `File` и `UploadFile` из модуля `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Определите параметры `File` { #define-file-parameters }
|
||||
|
||||
Создайте параметры `File` так же, как вы это делаете для `Body` или `Form`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// info | Дополнительная информация
|
||||
|
||||
@@ -54,7 +54,7 @@ $ pip install python-multipart
|
||||
|
||||
Определите параметр файла с типом `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
|
||||
|
||||
Использование `UploadFile` имеет ряд преимуществ перед `bytes`:
|
||||
|
||||
@@ -122,7 +122,7 @@ contents = myfile.file.read()
|
||||
|
||||
Но когда форма включает файлы, она кодируется как multipart/form-data. Если вы используете `File`, **FastAPI** будет знать, что ему нужно получить файлы из нужной части тела.
|
||||
|
||||
Если вы хотите узнать больше об этих кодировках и полях форм, перейдите по ссылке <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla">MDN</abbr> web docs for <code>POST</code></a>.
|
||||
Если вы хотите узнать больше об этих кодировках и полях форм, перейдите по ссылке <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla">MDN</abbr> web docs for <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
@@ -144,7 +144,7 @@ contents = myfile.file.read()
|
||||
|
||||
Вы также можете использовать `File()` вместе с `UploadFile`, например, для установки дополнительных метаданных:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
|
||||
|
||||
## Загрузка нескольких файлов { #multiple-file-uploads }
|
||||
|
||||
@@ -154,7 +154,7 @@ contents = myfile.file.read()
|
||||
|
||||
Для этого необходимо объявить список `bytes` или `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
|
||||
|
||||
Вы получите, как и было объявлено, список `list` из `bytes` или `UploadFile`.
|
||||
|
||||
@@ -170,7 +170,7 @@ contents = myfile.file.read()
|
||||
|
||||
Так же, как и раньше, вы можете использовать `File()` для задания дополнительных параметров, даже для `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
|
||||
|
||||
## Резюме { #recap }
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ $ pip install python-multipart
|
||||
|
||||
Вам просто нужно объявить **Pydantic-модель** с полями, которые вы хотите получить как **поля формы**, а затем объявить параметр как `Form`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
|
||||
|
||||
**FastAPI** **извлечёт** данные для **каждого поля** из **данных формы** в запросе и выдаст вам объявленную Pydantic-модель.
|
||||
|
||||
@@ -48,7 +48,7 @@ $ pip install python-multipart
|
||||
|
||||
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) все дополнительные (`extra`) поля:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
|
||||
|
||||
Если клиент попробует отправить дополнительные данные, то в ответ он получит **ошибку**.
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ $ pip install python-multipart
|
||||
|
||||
## Импортируйте `File` и `Form` { #import-file-and-form }
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Определите параметры `File` и `Form` { #define-file-and-form-parameters }
|
||||
|
||||
Создайте параметры файла и формы таким же образом, как для `Body` или `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
|
||||
|
||||
Файлы и поля формы будут загружены в виде данных формы, и вы получите файлы и поля формы.
|
||||
|
||||
|
||||
@@ -18,17 +18,17 @@ $ pip install python-multipart
|
||||
|
||||
Импортируйте `Form` из `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Определение параметров `Form` { #define-form-parameters }
|
||||
|
||||
Создайте параметры формы так же, как это делается для `Body` или `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
Например, в одном из способов использования спецификации OAuth2 (называемом «потоком пароля») требуется отправить `username` и `password` в виде полей формы.
|
||||
Например, в одном из способов использования спецификации OAuth2 (называемом «password flow» - аутентификация по паролю) требуется отправить `username` и `password` в виде полей формы.
|
||||
|
||||
<abbr title="specification – спецификация">spec</abbr> требует, чтобы поля были строго названы `username` и `password` и отправлялись как поля формы, а не JSON.
|
||||
<dfn title="спецификация">спецификация</dfn> требует, чтобы поля были строго названы `username` и `password` и отправлялись как поля формы, а не JSON.
|
||||
|
||||
С помощью `Form` вы можете объявить те же настройки, что и с `Body` (и `Query`, `Path`, `Cookie`), включая валидацию, примеры, псевдоним (например, `user-name` вместо `username`) и т.д.
|
||||
|
||||
@@ -56,7 +56,7 @@ $ pip install python-multipart
|
||||
|
||||
Но когда форма содержит файлы, она кодируется как `multipart/form-data`. О работе с файлами вы прочтёте в следующей главе.
|
||||
|
||||
Если вы хотите узнать больше про эти кодировки и поля формы, обратитесь к <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla">MDN</abbr> веб-документации для `POST`</a>.
|
||||
Если вы хотите узнать больше про эти кодировки и поля формы, обратитесь к <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla">MDN</abbr> веб-документации для `POST`</a>.
|
||||
|
||||
///
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ FastAPI делает несколько вещей внутри вместе с
|
||||
|
||||
Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах документации для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
|
||||
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
|
||||
|
||||
Этот простой случай обрабатывается FastAPI автоматически, потому что аннотация возвращаемого типа — это класс (или подкласс) `Response`.
|
||||
|
||||
@@ -193,7 +193,7 @@ FastAPI делает несколько вещей внутри вместе с
|
||||
|
||||
Вы также можете использовать подкласс `Response` в аннотации типа:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
|
||||
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
|
||||
|
||||
Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот простой случай.
|
||||
|
||||
@@ -201,7 +201,7 @@ FastAPI делает несколько вещей внутри вместе с
|
||||
|
||||
Но когда вы возвращаете произвольный объект, не являющийся валидным типом Pydantic (например, объект базы данных), и аннотируете его таким образом в функции, FastAPI попытается создать модель ответа Pydantic из этой аннотации типа и потерпит неудачу.
|
||||
|
||||
То же произойдёт, если у вас будет что-то вроде <abbr title='Объединение нескольких типов означает «любой из этих типов».'>union</abbr> разных типов, где один или несколько не являются валидными типами Pydantic, например, это приведёт к ошибке 💥:
|
||||
То же произойдёт, если у вас будет что-то вроде <dfn title="Объединение нескольких типов означает «любой из этих типов».">union</dfn> разных типов, где один или несколько не являются валидными типами Pydantic, например, это приведёт к ошибке 💥:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* `@app.delete()`
|
||||
* и других.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
@@ -66,7 +66,7 @@ FastAPI знает об этом и создаст документацию Open
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Чтобы узнать больше о HTTP кодах статуса и о том, для чего каждый из них предназначен, ознакомьтесь с <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla">MDN</abbr> документацией об HTTP статус-кодах</a>.
|
||||
Чтобы узнать больше о HTTP кодах статуса и о том, для чего каждый из них предназначен, ознакомьтесь с <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla">MDN</abbr> документацией об HTTP статус-кодах</a>.
|
||||
|
||||
///
|
||||
|
||||
@@ -74,7 +74,7 @@ FastAPI знает об этом и создаст документацию Open
|
||||
|
||||
Рассмотрим предыдущий пример еще раз:
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
`201` – это код статуса "Создано".
|
||||
|
||||
@@ -82,7 +82,7 @@ FastAPI знает об этом и создаст документацию Open
|
||||
|
||||
Для удобства вы можете использовать переменные из `fastapi.status`.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
|
||||
|
||||
Они содержат те же числовые значения, но позволяют использовать автозавершение редактора кода для выбора кода статуса:
|
||||
|
||||
@@ -90,7 +90,7 @@ FastAPI знает об этом и создаст документацию Open
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
Вы также можете использовать `from starlette import status` вместо `from fastapi import status`.
|
||||
Вы также можете использовать `from starlette import status`.
|
||||
|
||||
**FastAPI** позволяет использовать как `starlette.status`, так и `fastapi.status` исключительно для удобства разработчиков. Но поставляется fastapi.status непосредственно из Starlette.
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) доб
|
||||
|
||||
Когда вы делаете это, примеры становятся частью внутренней **JSON Schema** для данных тела запроса.
|
||||
|
||||
Тем не менее, на <abbr title="2023-08-26">момент написания этого</abbr> Swagger UI, инструмент, отвечающий за отображение UI документации, не поддерживает показ нескольких примеров для данных в **JSON Schema**. Но ниже есть обходной путь.
|
||||
Тем не менее, на <dfn title="2023-08-26">момент написания этого</dfn> Swagger UI, инструмент, отвечающий за отображение UI документации, не поддерживает показ нескольких примеров для данных в **JSON Schema**. Но ниже есть обходной путь.
|
||||
|
||||
### Специфические для OpenAPI `examples` { #openapi-specific-examples }
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
Скопируйте пример в файл `main.py`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py *}
|
||||
|
||||
## Запуск { #run-it }
|
||||
|
||||
@@ -132,7 +132,7 @@ OAuth2 был спроектирован так, чтобы бэкенд или
|
||||
|
||||
При создании экземпляра класса `OAuth2PasswordBearer` мы передаем параметр `tokenUrl`. Этот параметр содержит URL, который клиент (фронтенд, работающий в браузере пользователя) будет использовать для отправки `username` и `password`, чтобы получить токен.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -170,7 +170,7 @@ oauth2_scheme(some, parameters)
|
||||
|
||||
Теперь вы можете передать `oauth2_scheme` как зависимость с `Depends`.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
Эта зависимость предоставит `str`, который будет присвоен параметру `token` *функции-обработчика пути*.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
В предыдущей главе система безопасности (основанная на системе внедрения зависимостей) передавала *функции-обработчику пути* `token` типа `str`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
Но это всё ещё не слишком полезно.
|
||||
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
# Настройка авторизации { #security }
|
||||
# Безопасность { #security }
|
||||
|
||||
Существует множество способов обеспечения безопасности, аутентификации и авторизации.
|
||||
|
||||
Обычно эта тема является достаточно сложной и трудной.
|
||||
Обычно эта тема является достаточно сложной и «трудной».
|
||||
|
||||
Во многих фреймворках и системах только работа с определением доступов к приложению и аутентификацией требует значительных затрат усилий и написания множества кода (во многих случаях его объём может составлять более 50% от всего написанного кода).
|
||||
Во многих фреймворках и системах только работа с безопасностью и аутентификацией требует значительных затрат усилий и написания множества кода (во многих случаях его объём может составлять 50% или более от всего написанного кода).
|
||||
|
||||
**FastAPI** предоставляет несколько инструментов, которые помогут вам настроить **Авторизацию** легко, быстро, стандартным способом, без необходимости изучать все её тонкости.
|
||||
**FastAPI** предоставляет несколько инструментов, которые помогут вам работать с **безопасностью** легко, быстро, стандартным способом, без необходимости изучать и разбираться во всех спецификациях по безопасности.
|
||||
|
||||
Но сначала давайте рассмотрим некоторые небольшие концепции.
|
||||
Но сначала давайте рассмотрим несколько небольших концепций.
|
||||
|
||||
## Куда-то торопишься? { #in-a-hurry }
|
||||
## Нет времени? { #in-a-hurry }
|
||||
|
||||
Если вам не нужна информация о каких-либо из следующих терминов и вам просто нужно добавить защиту с аутентификацией на основе логина и пароля *прямо сейчас*, переходите к следующим главам.
|
||||
Если вам не важны какие-либо из этих терминов и вам просто нужно добавить защиту с аутентификацией на основе имени пользователя и пароля прямо сейчас, переходите к следующим главам.
|
||||
|
||||
## OAuth2 { #oauth2 }
|
||||
|
||||
OAuth2 - это протокол, который определяет несколько способов обработки аутентификации и авторизации.
|
||||
OAuth2 - это спецификация, которая определяет несколько способов обработки аутентификации и авторизации.
|
||||
|
||||
Он довольно обширен и охватывает несколько сложных вариантов использования.
|
||||
Это довольно обширная спецификация, охватывающая несколько сложных вариантов использования.
|
||||
|
||||
OAuth2 включает в себя способы аутентификации с использованием "третьей стороны".
|
||||
Она включает способы аутентификации с использованием «третьей стороны».
|
||||
|
||||
Это то, что используют под собой все кнопки "вход с помощью Facebook, Google, X (Twitter), GitHub" на страницах авторизации.
|
||||
Именно это используется во всех системах с кнопками «войти с помощью Facebook, Google, X (Twitter), GitHub».
|
||||
|
||||
### OAuth 1 { #oauth-1 }
|
||||
|
||||
Ранее использовался протокол OAuth 1, который сильно отличается от OAuth2 и является более сложным, поскольку он включал прямые описания того, как шифровать сообщение.
|
||||
Ранее использовался OAuth 1, который сильно отличается от OAuth2 и является более сложным, поскольку он включал прямые спецификации того, как шифровать обмен данными.
|
||||
|
||||
В настоящее время он не очень популярен и не используется.
|
||||
|
||||
OAuth2 не указывает, как шифровать сообщение, он ожидает, что ваше приложение будет обслуживаться по протоколу HTTPS.
|
||||
OAuth2 не указывает, как шифровать обмен данными, он ожидает, что ваше приложение будет обслуживаться по протоколу HTTPS.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
В разделе **Развертывание** вы увидите как настроить протокол HTTPS бесплатно, используя Traefik и Let's Encrypt.
|
||||
В разделе о **развертывании** вы увидите, как настроить HTTPS бесплатно, используя Traefik и Let's Encrypt.
|
||||
|
||||
///
|
||||
|
||||
## OpenID Connect { #openid-connect }
|
||||
|
||||
OpenID Connect - это еще один протокол, основанный на **OAuth2**.
|
||||
OpenID Connect — это ещё одна спецификация, основанная на **OAuth2**.
|
||||
|
||||
Он просто расширяет OAuth2, уточняя некоторые вещи, не имеющие однозначного определения в OAuth2, в попытке сделать его более совместимым.
|
||||
Она просто расширяет OAuth2, уточняя некоторые вещи, которые относительно неоднозначны в OAuth2, стараясь сделать его более совместимым.
|
||||
|
||||
Например, для входа в Google используется OpenID Connect (который под собой использует OAuth2).
|
||||
Например, для входа в Google используется OpenID Connect (который под капотом использует OAuth2).
|
||||
|
||||
Но вход в Facebook не поддерживает OpenID Connect. У него есть собственная вариация OAuth2.
|
||||
|
||||
### OpenID (не "OpenID Connect") { #openid-not-openid-connect }
|
||||
### OpenID (не «OpenID Connect») { #openid-not-openid-connect }
|
||||
|
||||
Также ранее использовался стандарт "OpenID", который пытался решить ту же проблему, что и **OpenID Connect**, но не был основан на OAuth2.
|
||||
Также ранее использовалась спецификация «OpenID», которая пыталась решить ту же задачу, что и **OpenID Connect**, но не была основана на OAuth2.
|
||||
|
||||
Таким образом, это была полноценная дополнительная система.
|
||||
|
||||
@@ -62,45 +62,45 @@ OpenAPI (ранее известный как Swagger) - это открытая
|
||||
|
||||
**FastAPI** основан на **OpenAPI**.
|
||||
|
||||
Это то, что делает возможным наличие множества автоматических интерактивных интерфейсов документирования, сгенерированного кода и т.д.
|
||||
Это то, что делает возможными несколько автоматических интерактивных интерфейсов документации, генерацию кода и т.д.
|
||||
|
||||
В OpenAPI есть способ использовать несколько "схем" безопасности.
|
||||
В OpenAPI есть способ определить несколько «схем» безопасности.
|
||||
|
||||
Таким образом, вы можете воспользоваться преимуществами Всех этих стандартных инструментов, включая интерактивные системы документирования.
|
||||
Используя их, вы можете воспользоваться преимуществами всех этих инструментов, основанных на стандартах, включая интерактивные системы документирования.
|
||||
|
||||
OpenAPI может использовать следующие схемы авторизации:
|
||||
OpenAPI определяет следующие схемы безопасности:
|
||||
|
||||
* `apiKey`: уникальный идентификатор для приложения, который может быть получен из:
|
||||
* Параметров запроса.
|
||||
* Заголовка.
|
||||
* Cookies.
|
||||
* `http`: стандартные системы аутентификации по протоколу HTTP, включая:
|
||||
* `bearer`: заголовок `Authorization` со значением `Bearer {уникальный токен}`. Это унаследовано от OAuth2.
|
||||
* Базовая аутентификация по протоколу HTTP.
|
||||
* `apiKey`: специфичный для приложения ключ, который может поступать из:
|
||||
* параметра запроса.
|
||||
* HTTP-заголовка.
|
||||
* cookie.
|
||||
* `http`: стандартные системы аутентификации по HTTP, включая:
|
||||
* `bearer`: HTTP-заголовок `Authorization` со значением `Bearer ` плюс токен. Это унаследовано от OAuth2.
|
||||
* Базовая аутентификация по HTTP.
|
||||
* HTTP Digest и т.д.
|
||||
* `oauth2`: все способы обеспечения безопасности OAuth2 называемые "потоки" (англ. "flows").
|
||||
* Некоторые из этих "потоков" подходят для реализации аутентификации через сторонний сервис использующий OAuth 2.0 (например, Google, Facebook, X (Twitter), GitHub и т.д.):
|
||||
* `oauth2`: все способы OAuth2 для обеспечения безопасности (называются «потоками»).
|
||||
* Несколько из этих «потоков» подходят для построения провайдера аутентификации OAuth 2.0 (например, Google, Facebook, X (Twitter), GitHub и т.д.):
|
||||
* `implicit`
|
||||
* `clientCredentials`
|
||||
* `authorizationCode`
|
||||
* Но есть один конкретный "поток", который может быть идеально использован для обработки аутентификации непосредственно в том же приложении:
|
||||
* `password`: в некоторых следующих главах будут рассмотрены примеры этого.
|
||||
* `openIdConnect`: способ определить, как автоматически обнаруживать данные аутентификации OAuth2.
|
||||
* Это автоматическое обнаружение определено в спецификации OpenID Connect.
|
||||
* Но есть один конкретный «поток», который можно идеально использовать для обработки аутентификации непосредственно в этом же приложении:
|
||||
* `password`: в некоторых следующих главах будут приведены примеры.
|
||||
* `openIdConnect`: имеет способ определить, как автоматически обнаруживать данные аутентификации OAuth2.
|
||||
* Именно это автоматическое обнаружение определено в спецификации OpenID Connect.
|
||||
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Интеграция сторонних сервисов для аутентификации/авторизации таких как Google, Facebook, X (Twitter), GitHub и т.д. осуществляется достаточно легко.
|
||||
Интеграция сторонних провайдеров аутентификации/авторизации, таких как Google, Facebook, X (Twitter), GitHub и т.д., также возможна и относительно проста.
|
||||
|
||||
Самой сложной проблемой является создание такого провайдера аутентификации/авторизации, но **FastAPI** предоставляет вам инструменты, позволяющие легко это сделать, выполняя при этом всю тяжелую работу за вас.
|
||||
Самой сложной задачей является создание такого провайдера аутентификации/авторизации, но **FastAPI** предоставляет вам инструменты, позволяющие легко это сделать, выполняя при этом всю тяжёлую работу за вас.
|
||||
|
||||
///
|
||||
|
||||
## Преимущества **FastAPI** { #fastapi-utilities }
|
||||
## Инструменты **FastAPI** { #fastapi-utilities }
|
||||
|
||||
Fast API предоставляет несколько инструментов для каждой из этих схем безопасности в модуле `fastapi.security`, которые упрощают использование этих механизмов безопасности.
|
||||
FastAPI предоставляет несколько инструментов для каждой из этих схем безопасности в модуле `fastapi.security`, которые упрощают использование этих механизмов безопасности.
|
||||
|
||||
В следующих главах вы увидите, как обезопасить свой API, используя инструменты, предоставляемые **FastAPI**.
|
||||
В следующих главах вы увидите, как добавить безопасность в ваш API, используя инструменты, предоставляемые **FastAPI**.
|
||||
|
||||
И вы также увидите, как он автоматически интегрируется в систему интерактивной документации.
|
||||
И вы также увидите, как это автоматически интегрируется в систему интерактивной документации.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# OAuth2 с паролем (и хешированием), Bearer с JWT-токенами { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
|
||||
|
||||
Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены <abbr title="JSON Web Tokens – веб‑токены JSON">JWT</abbr> и безопасное хеширование паролей.
|
||||
Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены <abbr title="JSON Web Tokens - веб‑токены JSON">JWT</abbr> и безопасное хеширование паролей.
|
||||
|
||||
Этот код можно реально использовать в своем приложении, сохранять хэши паролей в базе данных и т.д.
|
||||
|
||||
Мы продолжим разбираться, начиная с того места, на котором остановились в предыдущей главе.
|
||||
Мы продолжим разбираться, начиная с того места, на котором остановились в предыдущей главе, и расширим его.
|
||||
|
||||
## Про JWT { #about-jwt }
|
||||
|
||||
@@ -43,9 +43,11 @@ $ pip install pyjwt
|
||||
</div>
|
||||
|
||||
/// info | Дополнительная информация
|
||||
|
||||
Если вы планируете использовать алгоритмы цифровой подписи, такие как RSA или ECDSA, вам следует установить зависимость библиотеки криптографии `pyjwt[crypto]`.
|
||||
|
||||
Подробнее об этом можно прочитать в <a href="https://pyjwt.readthedocs.io/en/latest/installation.html" class="external-link" target="_blank">документации по установке PyJWT</a>.
|
||||
|
||||
///
|
||||
|
||||
## Хеширование паролей { #password-hashing }
|
||||
@@ -83,11 +85,13 @@ $ pip install "pwdlib[argon2]"
|
||||
</div>
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
С помощью `pwdlib` можно даже настроить его на чтение паролей, созданных **Django**, плагином безопасности **Flask** или многими другими библиотеками.
|
||||
|
||||
Таким образом, вы сможете, например, совместно использовать одни и те же данные из приложения Django в базе данных с приложением FastAPI. Или постепенно мигрировать Django-приложение, используя ту же базу данных.
|
||||
|
||||
При этом пользователи смогут одновременно входить в систему как из приложения Django, так и из приложения **FastAPI**.
|
||||
|
||||
///
|
||||
|
||||
## Хеширование и проверка паролей { #hash-and-verify-the-passwords }
|
||||
@@ -97,11 +101,13 @@ $ pip install "pwdlib[argon2]"
|
||||
Создайте экземпляр PasswordHash с рекомендованными настройками — он будет использоваться для хэширования и проверки паролей.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
pwdlib также поддерживает алгоритм хеширования bcrypt, но не включает устаревшие алгоритмы — для работы с устаревшими хэшами рекомендуется использовать библиотеку passlib.
|
||||
|
||||
Например, вы можете использовать ее для чтения и проверки паролей, сгенерированных другой системой (например, Django), но хэшировать все новые пароли другим алгоритмом, например Argon2 или Bcrypt.
|
||||
|
||||
И при этом быть совместимым со всеми этими системами.
|
||||
|
||||
///
|
||||
|
||||
Создайте служебную функцию для хэширования пароля, поступающего от пользователя.
|
||||
@@ -110,10 +116,16 @@ pwdlib также поддерживает алгоритм хешировани
|
||||
|
||||
И еще одну — для аутентификации и возврата пользователя.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
|
||||
|
||||
Когда `authenticate_user` вызывается с именем пользователя, которого нет в базе данных, мы все равно запускаем `verify_password` с использованием фиктивного хэша.
|
||||
|
||||
Это гарантирует, что эндпоинт отвечает примерно за одно и то же время вне зависимости от того, существует имя пользователя или нет, предотвращая тайминговые атаки (атака по времени), с помощью которых можно было бы перечислять существующие имена пользователей.
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
Если проверить новую (фальшивую) базу данных `fake_users_db`, то можно увидеть, как теперь выглядит хэшированный пароль: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
|
||||
|
||||
///
|
||||
|
||||
## Работа с JWT токенами { #handle-jwt-tokens }
|
||||
@@ -144,7 +156,7 @@ $ openssl rand -hex 32
|
||||
|
||||
Создайте служебную функцию для генерации нового токена доступа.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
|
||||
|
||||
## Обновление зависимостей { #update-the-dependencies }
|
||||
|
||||
@@ -154,7 +166,7 @@ $ openssl rand -hex 32
|
||||
|
||||
Если токен недействителен, то сразу же верните HTTP-ошибку.
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
|
||||
|
||||
## Обновление *операции пути* `/token` { #update-the-token-path-operation }
|
||||
|
||||
@@ -162,7 +174,7 @@ $ openssl rand -hex 32
|
||||
|
||||
Создайте реальный токен доступа JWT и верните его
|
||||
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *}
|
||||
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
|
||||
|
||||
### Технические подробности о JWT ключе `sub` { #technical-details-about-the-jwt-subject-sub }
|
||||
|
||||
@@ -202,7 +214,9 @@ Username: `johndoe`
|
||||
Password: `secret`
|
||||
|
||||
/// check | Проверка
|
||||
|
||||
Обратите внимание, что нигде в коде не используется открытый текст пароля "`secret`", мы используем только его хэшированную версию.
|
||||
|
||||
///
|
||||
|
||||
<img src="/img/tutorial/security/image08.png">
|
||||
@@ -225,7 +239,9 @@ Password: `secret`
|
||||
<img src="/img/tutorial/security/image10.png">
|
||||
|
||||
/// note | Техническая информация
|
||||
|
||||
Обратите внимание на HTTP-заголовок `Authorization`, значение которого начинается с `Bearer `.
|
||||
|
||||
///
|
||||
|
||||
## Продвинутое использование `scopes` { #advanced-usage-with-scopes }
|
||||
|
||||
@@ -236,7 +236,7 @@ UserInDB(
|
||||
|
||||
<img src="/img/tutorial/security/image06.png">
|
||||
|
||||
Если щёлкнуть на значке замка и выйти из системы, а затем попытаться выполнить ту же операцию ещё раз, будет выдана ошибка HTTP 401:
|
||||
Если щёлкнуть на значке замка и выйти из системы, а затем попробовать выполнить ту же операцию ещё раз, будет выдана ошибка HTTP 401:
|
||||
|
||||
```JSON
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют <abbr title="Object Relational Mapper – Объектно-реляционный маппер: термин для библиотеки, где некоторые классы представляют SQL-таблицы, а экземпляры представляют строки в этих таблицах">"ORMs"</abbr>), FastAPI ничего не навязывает. 😎
|
||||
Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют <abbr title="Object Relational Mapper - Объектно-реляционный маппер: термин для библиотеки, где некоторые классы представляют SQL-таблицы, а экземпляры представляют строки в этих таблицах">"ORMs"</abbr>), FastAPI ничего не навязывает. 😎
|
||||
|
||||
///
|
||||
|
||||
@@ -344,7 +344,7 @@ $ fastapi dev main.py
|
||||
|
||||
</div>
|
||||
|
||||
Если вы перейдёте в UI API `/docs`, вы увидите, что он обновился: теперь при создании героя он не ожидает получить `id` от клиента и т. д.
|
||||
Если вы перейдёте в UI API `/docs`, вы увидите, что он обновился: теперь при создании героя он не ожидает получить `id` от клиента и т.д.
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/sql-databases/image02.png">
|
||||
@@ -354,4 +354,4 @@ $ fastapi dev main.py
|
||||
|
||||
Вы можете использовать <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> для взаимодействия с SQL базой данных и упростить код с помощью *моделей данных* и *моделей-таблиц*.
|
||||
|
||||
Гораздо больше вы можете узнать в документации **SQLModel**, там есть более подробный мини-<a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">туториал по использованию SQLModel с **FastAPI**</a>. 🚀
|
||||
Гораздо больше вы можете узнать в документации **SQLModel**, там есть более подробное мини-<a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">руководство по использованию SQLModel с **FastAPI**</a>. 🚀
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Импортируйте `StaticFiles`.
|
||||
* "Примонтируйте" экземпляр `StaticFiles()` к определённому пути.
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ $ pip install httpx
|
||||
|
||||
Напишите простое утверждение с `assert` дабы проверить истинность Python-выражения (это тоже стандарт `pytest`).
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
|
||||
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -76,7 +76,7 @@ $ pip install httpx
|
||||
В файле `main.py` находится Ваше приложение **FastAPI**:
|
||||
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/main.py *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/main.py *}
|
||||
|
||||
### Файл тестов { #testing-file }
|
||||
|
||||
@@ -92,7 +92,7 @@ $ pip install httpx
|
||||
|
||||
Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт:
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
|
||||
|
||||
|
||||
...и писать дальше тесты, как и раньше.
|
||||
@@ -119,7 +119,7 @@ $ pip install httpx
|
||||
|
||||
Ещё есть операция `POST`, и она может вернуть несколько ошибок.
|
||||
|
||||
Обе *операции пути* требуют наличия в запросе заголовка `X-Token`.
|
||||
Обе *операции пути* требуют наличия в запросе HTTP-заголовка `X-Token`.
|
||||
|
||||
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
|
||||
|
||||
@@ -139,7 +139,7 @@ $ pip install httpx
|
||||
* Передаёте *path*-параметры или *query*-параметры, вписав их непосредственно в строку URL.
|
||||
* Передаёте JSON в теле запроса, передав Python-объект (например: `dict`) через именованный параметр `json`.
|
||||
* Если же Вам необходимо отправить *форму с данными* вместо JSON, то используйте параметр `data` вместо `json`.
|
||||
* Для передачи *заголовков*, передайте объект `dict` через параметр `headers`.
|
||||
* Для передачи *HTTP-заголовков*, передайте объект `dict` через параметр `headers`.
|
||||
* Для передачи *cookies* также передайте `dict`, но через параметр `cookies`.
|
||||
|
||||
Для получения дополнительной информации о передаче данных на бэкенд с помощью `httpx` или `TestClient` ознакомьтесь с <a href="https://www.python-httpx.org" class="external-link" target="_blank">документацией HTTPX</a>.
|
||||
|
||||
@@ -53,7 +53,7 @@ $ cd awesome-project
|
||||
|
||||
## Создание виртуального окружения { #create-a-virtual-environment }
|
||||
|
||||
Когда вы начинаете работать над Python‑проектом **впервые**, создайте виртуальное окружение **<abbr title="есть и другие опции, но это простой ориентир">внутри вашего проекта</abbr>**.
|
||||
Когда вы начинаете работать над Python‑проектом **впервые**, создайте виртуальное окружение **<dfn title="есть и другие опции, но это простой ориентир">внутри вашего проекта</dfn>**.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -166,7 +166,7 @@ $ source .venv/Scripts/activate
|
||||
|
||||
Каждый раз, когда вы устанавливаете **новый пакет** в это окружение, **активируйте** окружение снова.
|
||||
|
||||
Это гарантирует, что если вы используете **программу терминала (<abbr title="command line interface – интерфейс командной строки">CLI</abbr>)**, установленную этим пакетом, вы будете использовать именно ту, что из вашего виртуального окружения, а не какую‑то глобально установленную, возможно другой версии, чем вам нужна.
|
||||
Это гарантирует, что если вы используете **программу терминала (<abbr title="command line interface - интерфейс командной строки">CLI</abbr>)**, установленную этим пакетом, вы будете использовать именно ту, что из вашего виртуального окружения, а не какую‑то глобально установленную, возможно другой версии, чем вам нужна.
|
||||
|
||||
///
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ class UserInDB(User):
|
||||
|
||||
password_hash = PasswordHash.recommended()
|
||||
|
||||
DUMMY_HASH = password_hash.hash("dummypassword")
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||
|
||||
app = FastAPI()
|
||||
@@ -70,6 +72,7 @@ def get_user(db, username: str):
|
||||
def authenticate_user(fake_db, username: str, password: str):
|
||||
user = get_user(fake_db, username)
|
||||
if not user:
|
||||
verify_password(password, DUMMY_HASH)
|
||||
return False
|
||||
if not verify_password(password, user.hashed_password):
|
||||
return False
|
||||
|
||||
@@ -47,6 +47,8 @@ class UserInDB(User):
|
||||
|
||||
password_hash = PasswordHash.recommended()
|
||||
|
||||
DUMMY_HASH = password_hash.hash("dummypassword")
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||
|
||||
app = FastAPI()
|
||||
@@ -69,6 +71,7 @@ def get_user(db, username: str):
|
||||
def authenticate_user(fake_db, username: str, password: str):
|
||||
user = get_user(fake_db, username)
|
||||
if not user:
|
||||
verify_password(password, DUMMY_HASH)
|
||||
return False
|
||||
if not verify_password(password, user.hashed_password):
|
||||
return False
|
||||
|
||||
@@ -60,6 +60,8 @@ class UserInDB(User):
|
||||
|
||||
password_hash = PasswordHash.recommended()
|
||||
|
||||
DUMMY_HASH = password_hash.hash("dummypassword")
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(
|
||||
tokenUrl="token",
|
||||
scopes={"me": "Read information about the current user.", "items": "Read items."},
|
||||
@@ -85,6 +87,7 @@ def get_user(db, username: str):
|
||||
def authenticate_user(fake_db, username: str, password: str):
|
||||
user = get_user(fake_db, username)
|
||||
if not user:
|
||||
verify_password(password, DUMMY_HASH)
|
||||
return False
|
||||
if not verify_password(password, user.hashed_password):
|
||||
return False
|
||||
|
||||
@@ -59,6 +59,8 @@ class UserInDB(User):
|
||||
|
||||
password_hash = PasswordHash.recommended()
|
||||
|
||||
DUMMY_HASH = password_hash.hash("dummypassword")
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(
|
||||
tokenUrl="token",
|
||||
scopes={"me": "Read information about the current user.", "items": "Read items."},
|
||||
@@ -84,6 +86,7 @@ def get_user(db, username: str):
|
||||
def authenticate_user(fake_db, username: str, password: str):
|
||||
user = get_user(fake_db, username)
|
||||
if not user:
|
||||
verify_password(password, DUMMY_HASH)
|
||||
return False
|
||||
if not verify_password(password, user.hashed_password):
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user