Browse Source

Merge 3f6da3c679 into 6df50d40fe

pull/13934/merge
Sebastián Ramírez 4 days ago
committed by GitHub
parent
commit
880cf57138
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 247
      docs/ru/docs/advanced/additional-responses.md
  2. 65
      docs/ru/docs/advanced/advanced-dependencies.md
  3. 361
      docs/ru/docs/advanced/behind-a-proxy.md
  4. 313
      docs/ru/docs/advanced/custom-response.md
  5. 95
      docs/ru/docs/advanced/dataclasses.md
  6. 165
      docs/ru/docs/advanced/events.md
  7. 261
      docs/ru/docs/advanced/generate-clients.md
  8. 96
      docs/ru/docs/advanced/middleware.md
  9. 186
      docs/ru/docs/advanced/openapi-callbacks.md
  10. 55
      docs/ru/docs/advanced/openapi-webhooks.md
  11. 204
      docs/ru/docs/advanced/path-operation-advanced-configuration.md
  12. 41
      docs/ru/docs/advanced/response-headers.md
  13. 107
      docs/ru/docs/advanced/security/http-basic-auth.md
  14. 19
      docs/ru/docs/advanced/security/index.md
  15. 274
      docs/ru/docs/advanced/security/oauth2-scopes.md
  16. 346
      docs/ru/docs/advanced/settings.md
  17. 67
      docs/ru/docs/advanced/sub-applications.md
  18. 126
      docs/ru/docs/advanced/templates.md
  19. 53
      docs/ru/docs/advanced/testing-dependencies.md
  20. 5
      docs/ru/docs/advanced/testing-events.md
  21. 13
      docs/ru/docs/advanced/testing-websockets.md
  22. 56
      docs/ru/docs/advanced/using-request-directly.md
  23. 35
      docs/ru/docs/advanced/wsgi.md
  24. 17
      docs/ru/docs/deployment/cloud.md
  25. 137
      docs/ru/docs/deployment/server-workers.md
  26. 56
      docs/ru/docs/how-to/conditional-openapi.md
  27. 70
      docs/ru/docs/how-to/configure-swagger-ui.md
  28. 185
      docs/ru/docs/how-to/custom-docs-ui-assets.md
  29. 109
      docs/ru/docs/how-to/custom-request-and-route.md
  30. 80
      docs/ru/docs/how-to/extending-openapi.md
  31. 39
      docs/ru/docs/how-to/general.md
  32. 60
      docs/ru/docs/how-to/graphql.md
  33. 13
      docs/ru/docs/how-to/index.md
  34. 104
      docs/ru/docs/how-to/separate-openapi-schemas.md
  35. 7
      docs/ru/docs/how-to/testing-database.md
  36. 3
      docs/ru/docs/resources/index.md

247
docs/ru/docs/advanced/additional-responses.md

@ -0,0 +1,247 @@
# Дополнительные ответы в OpenAPI
/// warning | Предупреждение
Это достаточно сложная тема.
Если вы только начинаете работать с **FastAPI**, вам это может не понадобиться.
///
Вы можете объявлять дополнительные ответы с дополнительными статус-кодами, типами содержимого, описаниями и т.д.
Эти дополнительные ответы будут включены в схему OpenAPI, так что они также появятся в документации API.
Но для этих дополнительных ответов вы должны убедиться, что возвращаете `Response`, например, `JSONResponse` напрямую, с вашим статус-кодом и содержимым.
## Дополнительный ответ с `model`
Вы можете передать своим декораторам *операций пути* параметр `responses`.
Он принимает `dict`: ключами являются статус-коды для каждого ответа (например, `200`), а значениями являются другие `dict` с информацией для каждого из них.
Каждый из этих `dict` ответа может содержать ключ `model`, содержащий Pydantic-модель, как и `response_model`.
**FastAPI** возьмет эту модель, сгенерирует ее JSON Schema и включит в нужное место в OpenAPI.
Например, чтобы объявить другой ответ с кодом состояния `404` и Pydantic-моделью `Message`, вы можете написать:
{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
/// note | Заметка
Имейте в виду, что вы должны вернуть `JSONResponse` напрямую.
///
/// info | Информация
Ключ `model` не является частью OpenAPI.
**FastAPI** возьмет Pydantic-модель оттуда, сгенерирует JSON Schema и поместит в нужное место.
Правильное место:
* В ключе `content`, который имеет значение другой JSON объект (`dict`), содержащий:
* Ключ с типом содержимого, например `application/json`, который в качестве значения содержит другой JSON объект, содержащий:
* Ключ `schema`, который имеет в качестве значения JSON Schema из модели, вот куда нужно поместить.
* **FastAPI** добавляет сюда ссылку на глобальные JSON Schemas в другом месте вашего OpenAPI вместо того, чтобы включать его напрямую. Таким образом, другие приложения и клиенты могут использовать эти JSON Schemas напрямую, предоставлять лучшие инструменты генерации кода и другие функции.
///
Сгенерированные ответы в OpenAPI для этой *операции пути* будут:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Дополнительный ответ",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Успешный ответ",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Ошибка валидации",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Схемы ссылаются на другое место внутри схемы OpenAPI:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Сообщение",
"required": [
"сообщение"
],
"type": "object",
"properties": {
"сообщение": {
"title": "Сообщение",
"type": "string"
}
}
},
"Item": {
"title": "Предмет",
"required": [
"идентификатор",
"значение"
],
"type": "object",
"properties": {
"идентификатор": {
"title": "Идентификатор",
"type": "string"
},
"значение": {
"title": "Значение",
"type": "string"
}
}
},
"ValidationError": {
"title": "Ошибка валидации",
"required": [
"местоположение",
"сообщение",
"тип"
],
"type": "object",
"properties": {
"местоположение": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"сообщение": {
"title": "Сообщение",
"type": "string"
},
"тип": {
"title": "Тип ошибки",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "Ошибка валидации HTTP",
"type": "object",
"properties": {
"детали": {
"title": "Детали",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Дополнительные типы содержимого для основного ответа
Вы можете использовать этот же параметр `responses`, чтобы добавить разные типы содержимого для одного и того же основного ответа.
Например, вы можете добавить дополнительный медиа-тип `image/png`, объявив, что ваши *операции пути* могут возвращать JSON объект (с медиа-типом `application/json`) или изображение PNG:
{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
/// note | Заметка
Обратите внимание, что вы должны возвращать изображение, используя `FileResponse` напрямую.
///
/// info | Информация
Если вы явно не указываете другой тип содержимого в вашем параметре `responses`, FastAPI предположит, что ответ имеет тот же тип содержимого, что и основной класс ответа (по умолчанию `application/json`).
Но если вы задали пользовательский класс ответа с `None` в качестве типа содержимого, FastAPI будет использовать `application/json` для любого дополнительного ответа, у которого есть ассоциированная модель.
///
## Комбинирование информации
Вы также можете комбинировать информацию о ответах из разных мест, включая параметры `response_model`, `status_code` и `responses`.
Вы можете объявить `response_model`, используя стандартный статус-код `200` (или пользовательский, если необходимо), а затем объявить дополнительную информацию для этого же ответа в `responses`, напрямую в схеме OpenAPI.
**FastAPI** сохранит дополнительную информацию из `responses` и объединит ее с JSON Schema из вашей модели.
Например, вы можете объявить ответ с кодом состояния `404`, который использует Pydantic-модель и имеет пользовательское `description`.
И ответ с кодом состояния `200`, который использует ваш `response_model`, но включает пользовательский `example`:
{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
Все это будет объединено и включено в ваш OpenAPI, и показано в документации API:
<img src="/img/tutorial/additional-responses/image01.png">
## Комбинирование предопределенных и пользовательских ответов
Возможно, вы захотите иметь некоторые предопределенные ответы, которые применяются ко многим *операциям пути*, но хотите объединить их с пользовательскими ответами, необходимыми для каждой *операции пути*.
Для таких случаев вы можете использовать технику Python "распаковка" `dict` с помощью `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Здесь, `new_dict` будет содержать все пары `ключ-значение` из `old_dict` плюс новую пару `ключ-значение`:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Вы можете использовать эту технику для повторного использования некоторых предопределенных ответов в ваших *операциях пути* и объединить их с дополнительными пользовательскими.
Например:
{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
## Больше информации об ответах OpenAPI
Чтобы узнать, что именно можно включить в ответы, вы можете посмотреть эти разделы в спецификации OpenAPI:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">OpenAPI Responses Object</a>, включает в себя `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">OpenAPI Response Object</a>, вы можете включить что угодно из этого напрямую в каждый ответ внутри вашего параметра `responses`. Включая `description`, `headers`, `content` (внутри этого вы указываете разные типы медиа и JSON Schemas), и `links`.

65
docs/ru/docs/advanced/advanced-dependencies.md

@ -0,0 +1,65 @@
# Продвинутые зависимости
## Параметризуемые зависимости
Все зависимости, которые мы видели, являются фиксированной функцией или классом.
Но могут быть случаи, когда вы захотите иметь возможность задавать параметры для зависимости, не объявляя множество разных функций или классов.
Представим, что мы хотим иметь зависимость, которая проверяет, содержит ли параметр запроса `q` некоторое фиксированное содержимое.
Но нам нужно иметь возможность параметризовать это фиксированное содержимое.
## "Вызываемый" экземпляр
В Python есть способ сделать экземпляр класса "вызываемым".
Не сам класс (он уже может быть вызываемым), а экземпляр этого класса.
Для этого мы объявляем метод `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
В этом случае **FastAPI** будет использовать этот `__call__` для проверки дополнительных параметров и под-зависимостей, и он будет вызываться для передачи значения в параметр вашей *функции-обработчика пути* позже.
## Параметризация экземпляра
Теперь мы можем использовать `__init__`, чтобы объявить параметры экземпляра, которые мы можем использовать для "параметризации" зависимости:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
В данном случае **FastAPI** никогда не будет касаться или заботиться об `__init__`, мы будем использовать его напрямую в нашем коде.
## Создание экземпляра
Мы можем создать экземпляр этого класса с помощью:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
Таким образом, мы можем "параметризовать" нашу зависимость, в которой теперь есть `"bar"`, как атрибут `checker.fixed_content`.
## Использование экземпляра в качестве зависимости
Затем мы могли бы использовать этот `checker` в `Depends(checker)`, вместо `Depends(FixedContentQueryChecker)`, потому что зависимость — это экземпляр `checker`, а не сам класс.
И при разрешении зависимости, **FastAPI** вызовет этот `checker` так:
```Python
checker(q="somequery")
```
...и передаст то, что это возвращает, как значение зависимости в нашей *функции-обработчике пути* как параметр `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
/// tip | Совет
Все это может показаться надуманным. И пока не совсем ясно, насколько это полезно.
Эти примеры намеренно просты, но показывают, как все работает.
В главах о безопасности есть утилиты-функции, которые реализованы таким же образом.
Если вы все это поняли, вы уже знаете, как работают эти утилиты для безопасности изнутри.
///

361
docs/ru/docs/advanced/behind-a-proxy.md

@ -0,0 +1,361 @@
# За прокси-сервером
В некоторых ситуациях вам может понадобиться использовать **прокси**-сервер, такой как Traefik или Nginx, с конфигурацией, добавляющей дополнительный префикс к path, который ваше приложение не видит.
В таких случаях вы можете использовать `root_path` для настройки вашего приложения.
`root_path` — это механизм, предоставляемый спецификацией ASGI (на основе которой построен FastAPI через Starlette).
`root_path` используется для обработки этих конкретных случаев.
Он также используется внутренне при монтировании подприложений.
## Прокси с вырезанным префиксом path
Использование прокси с вырезанным префиксом path означает, что вы можете объявить путь как `/app` в своем коде. Но затем вы добавляете слой сверху (прокси), который помещает ваше приложение **FastAPI** под путь, такой как `/api/v1`.
В этом случае оригинальный путь `/app` будет фактически работать как `/api/v1/app`.
Несмотря на то, что весь ваш код написан с учетом использования только `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
И прокси будет **"вырезать"** **префикс пути** на лету перед передачей запроса серверу приложения (вероятно, Uvicorn через CLI FastAPI), оставляя ваше приложение уверенным, что оно работает на `/app`, чтобы вам не пришлось обновлять весь код для включения префикса `/api/v1`.
До этого момента все будет работать как обычно.
Но затем, когда вы откроете встроенный интерфейс документации (фронтенд), он будет ожидать получить OpenAPI-схему на `/openapi.json`, вместо `/api/v1/openapi.json`.
Поэтому фронтенд (который работает в браузере) попытается достичь `/openapi.json` и не сможет получить OpenAPI-схему.
Поскольку у нас есть прокси с префиксом пути `/api/v1` для нашего приложения, фронтенд должен получить OpenAPI-схему на `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Браузер")
proxy["Прокси на http://0.0.0.0:9999/api/v1/app"]
server["Сервер на http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// совет | Совет
IP `0.0.0.0` обычно используется, чтобы указать, что программа слушает все доступные IP-адреса на этой машине/сервере.
///
Интерфейс документации также будет нуждаться в OpenAPI-схеме для декларации, что этот API `server` расположен на `/api/v1` (за прокси). Например:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Другие данные здесь
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Другие данные здесь
}
}
```
В этом примере "Прокси" может быть чем-то вроде **Traefik**. А сервером может быть что-то вроде CLI FastAPI с **Uvicorn**, который запускает ваше приложение FastAPI.
### Указание `root_path`
Чтобы достичь этого, вы можете использовать командную строку с опцией `--root-path`, например:
<div class="termy">
```console
$ fastapi run main.py --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn запущен на http://127.0.0.1:8000 (Нажмите CTRL+C для остановки)
```
</div>
Если вы используете Hypercorn, у него также есть опция `--root-path`.
/// note | Технические подробности
Спецификация ASGI определяет `root_path` для этого случая использования.
И опция командной строки `--root-path` предоставляет этот `root_path`.
///
### Проверка текущего `root_path`
Вы можете получить текущий `root_path`, используемый вашим приложением для каждого запроса, он является частью словаря `scope` (это часть спецификации ASGI).
Здесь мы включаем его в сообщение просто для демонстрации.
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
Затем, если вы запустите Uvicorn с:
<div class="termy">
```console
$ fastapi run main.py --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn запущен на http://127.0.0.1:8000 (Нажмите CTRL+C для остановки)
```
</div>
Ответ будет что-то вроде:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Установка `root_path` в приложении FastAPI
Альтернативно, если у вас нет возможности предоставить опцию командной строки, такую как `--root-path` или аналогичную, вы можете установить параметр `root_path` при создании вашего приложения FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
Передача `root_path` в `FastAPI` будет эквивалентна передаче опции командной строки `--root-path` для Uvicorn или Hypercorn.
### О `root_path`
Учтите, что сервер (Uvicorn) не будет использовать `root_path` ни для чего, кроме передачи его приложению.
Но если вы зайдете через браузер по адресу <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, вы увидите обычный ответ:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Итак, он не будет ожидать доступа по адресу `http://127.0.0.1:8000/api/v1/app`.
Uvicorn будет ожидать, что прокси получит доступ к Uvicorn по адресу `http://127.0.0.1:8000/app`, а затем это будет уже ответственность прокси — добавить дополнительный префикс `/api/v1`.
## О прокси с вырезанным префиксом path
Имейте в виду, что прокси с вырезанным префиксом path — это всего лишь один из способов его настройки.
Вероятно, во многих случаях по умолчанию будет то, что у прокси нет вырезанного префикса path.
В случае, подобном этому (без вырезанного префикса path), прокси будет слушать что-то вроде `https://myawesomeapp.com`, и тогда, если браузер перейдет на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (например, Uvicorn) слушает на `http://127.0.0.1:8000`, прокси (без вырезанного префикса path) получит доступ к Uvicorn на том же пути: `http://127.0.0.1:8000/api/v1/app`.
## Тестирование локально с Traefik
Вы можете легко провести эксперимент локально с вырезанным префиксом path, используя <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>, это один исполняемый файл, вы можете извлечь сжатый файл и запустить его прямо из терминала.
Затем создайте файл `traefik.toml` с:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Это указывает Traefik слушать на порту 9999 и использовать другой файл `routes.toml`.
/// совет
Мы используем порт 9999 вместо стандартного порта HTTP 80, чтобы вам не пришлось запускать это с привилегиями администратора (`sudo`).
///
Теперь создайте другой файл `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Этот файл настраивает Traefik для использования префикса path `/api/v1`.
А затем Traefik перенаправит свои запросы на ваш Uvicorn, запущенный на `http://127.0.0.1:8000`.
Теперь запустите Traefik:
<div class="termy">
```console
$ ./traefik --configFile=traefik.toml
INFO[0000] Конфигурация загружена из файла: /home/user/awesomeapi/traefik.toml
```
</div>
И теперь запустите ваше приложение, используя опцию `--root-path`:
<div class="termy">
```console
$ fastapi run main.py --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn запущен на http://127.0.0.1:8000 (Нажмите CTRL+C для остановки)
```
</div>
### Проверьте ответы
Теперь, если вы перейдете по URL с портом для Uvicorn: <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, вы увидите нормальный ответ:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// совет | Совет
Обратите внимание, что, хотя вы получаете доступ к нему по адресу `http://127.0.0.1:8000/app`, он отображает `root_path` как `/api/v1`, взятый из опции `--root-path`.
///
А теперь откройте URL с портом для Traefik, включая префикс path: <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>.
Мы получаем такой же ответ:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
но на этот раз по URL с префиксом path, предоставленным прокси: `/api/v1`.
Конечно, идея здесь в том, что все должны получить доступ к приложению через прокси, так что версия с префиксом path `/api/v1` — это "правильная" версия.
И версия без префикса path (`http://127.0.0.1:8000/app`), предоставляемая напрямую Uvicorn, будет исключительно для _прокси_ (Traefik), чтобы получить к ней доступ.
Это демонстрирует, как прокси (Traefik) использует префикс path и как сервер (Uvicorn) использует `root_path` из опции `--root-path`.
### Проверьте интерфейс документации
Но вот где начинается веселье. ✨
"Официальный" способ доступа к приложению будет через прокси с префиксом path, который мы определили. Поэтому, как и ожидалось, если вы попытаетесь использовать интерфейс документации, обслуживаемый Uvicorn напрямую, без префикса path в URL, он не будет работать, потому что предполагается, что доступ будет осуществляться через прокси.
Вы можете проверить это по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image01.png">
Но если мы получим доступ к интерфейсу документации по "официальному" URL, используя прокси с портом `9999`, по адресу `/api/v1/docs`, он работает правильно! 🎉
Вы можете проверить это по адресу <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image02.png">
Как и хотели. ✔️
Это потому, что FastAPI использует этот `root_path` для создания `server` по умолчанию в OpenAPI с URL, предоставленным `root_path`.
## Дополнительные серверы
/// warning | Предупреждение
Это более сложный случай использования. Вы можете пропустить его, если хотите.
///
По умолчанию **FastAPI** создаст `server` в схеме OpenAPI с URL для `root_path`.
Но вы также можете предоставить другие альтернативные `servers`, например, если вы хотите, чтобы *этот же* интерфейс документации взаимодействовал как со средой тестирования, так и со средой продакшн.
Если вы передаете пользовательский список `servers` и существует `root_path` (потому что ваш API находится за прокси), **FastAPI** вставит "server" с этим `root_path` в начало списка.
Например:
{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
Создаст схему OpenAPI, как:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Другие данные здесь
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Тестовая среда"
},
{
"url": "https://prod.example.com",
"description": "Продакшн среда"
}
],
"paths": {
// Другие данные здесь
}
}
```
/// совет | Совет
Обратите внимание на автоматически сгенерированный сервер с `url` значением `/api/v1`, взятым из `root_path`.
///
В интерфейсе документации по адресу <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> это будет выглядеть так:
<img src="/img/tutorial/behind-a-proxy/image03.png">
/// совет | Совет
Интерфейс документации будет взаимодействовать с сервером, который вы выберете.
///
### Отключение автоматического сервера от `root_path`
Если вы не хотите, чтобы **FastAPI** включал автоматический сервер, используя `root_path`, вы можете использовать параметр `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
и тогда он не будет включен в схему OpenAPI.
## Монтирование подприложения
Если вам нужно смонтировать подприложение (как описано в [Подприложения - Монтирование](sub-applications.md){.internal-link target=_blank}), используя также прокси с `root_path`, вы можете сделать это обычным образом, как и ожидалось.
FastAPI будет использовать `root_path` умно, поэтому все будет работать, как и задумано. ✨

313
docs/ru/docs/advanced/custom-response.md

@ -0,0 +1,313 @@
# Пользовательский ответ - HTML, поток, файл и другие
По умолчанию **FastAPI** будет возвращать ответы, используя `JSONResponse`.
Вы можете переопределить это, возвращая `Response` напрямую, как показано в разделе [Возврат Response напрямую](response-directly.md){.internal-link target=_blank}.
Но если вы возвращаете `Response` напрямую (или любой подкласс, такой как `JSONResponse`), данные не будут автоматически конвертироваться (даже если вы объявите `response_model`), и документация не будет автоматически генерироваться (например, включая конкретный "тип содержимого", в HTTP-заголовке `Content-Type` как часть генерируемого OpenAPI).
Но вы также можете объявить `Response`, который хотите использовать (например, любой подкласс `Response`), в *декораторе операции пути*, используя параметр `response_class`.
Содержимое, которое вы возвращаете из вашей *функции-обработчика пути*, будет помещено внутрь этого `Response`.
И если этот `Response` имеет JSON тип содержимого (`application/json`), как в случае с `JSONResponse` и `UJSONResponse`, данные, которые вы возвращаете, будут автоматически конвертироваться (и фильтроваться) с помощью любого Pydantic `response_model`, который вы объявили в *декораторе операции пути*.
/// note | Замечание
Если вы используете класс ответа без типа содержимого, FastAPI ожидает, что ваш ответ не будет содержать контент, поэтому он не задокументирует формат ответа в автоматически генерируемой документации OpenAPI.
///
## Использование `ORJSONResponse`
Например, если вы заботитесь о производительности, вы можете установить и использовать <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> и установить ответ `ORJSONResponse`.
Импортируйте класс (подкласс) `Response`, который вы хотите использовать, и объявите его в *декораторе операции пути*.
Для больших ответов возврат `Response` напрямую значительно быстрее, чем возврат словаря.
Это происходит потому, что по умолчанию FastAPI будет проверять каждый элемент внутри и убеждаться, что он сериализуем как JSON, используя тот же [Совместимый с JSON кодировщик](../tutorial/encoder.md){.internal-link target=_blank}, о котором рассказано в руководстве. Это позволяет вам возвращать **произвольные объекты**, например, модели базы данных.
Но если вы уверены, что содержимое, которое вы возвращаете, **сериализуемо с JSON**, вы можете передать его напрямую в класс ответа и избежать дополнительной нагрузки, которую FastAPI имел бы, передавая возвращаемый контент через `jsonable_encoder` перед передачей его в класс ответа.
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
/// info | Информация
Параметр `response_class` также будет использоваться для определения "типа содержимого" ответа.
В данном случае HTTP-заголовок `Content-Type` будет установлен в `application/json`.
И он будет задокументирован как таковой в OpenAPI.
///
/// tip | Совет
`ORJSONResponse` доступен только в FastAPI, не в Starlette.
///
## HTML-ответ
Чтобы вернуть ответ с HTML напрямую из **FastAPI**, используйте `HTMLResponse`.
* Импортируйте `HTMLResponse`.
* Передайте `HTMLResponse` в качестве параметра `response_class` вашего *декоратора операции пути*.
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
/// info | Информация
Параметр `response_class` также будет использоваться для определения "типа содержимого" ответа.
В этом случае HTTP-заголовок `Content-Type` будет установлен в `text/html`.
И он будет задокументирован как таковой в OpenAPI.
///
### Возврат `Response`
Как показано в разделе [Возврат Response напрямую](response-directly.md){.internal-link target=_blank}, вы также можете переопределить ответ напрямую в вашей *операции пути*, возвращая его.
Тот же пример, что и выше, возвращающий `HTMLResponse`, может выглядеть так:
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
/// warning | Предупреждение
`Response`, возвращенный напрямую вашей *функцией-обработчиком пути*, не будет задокументирован в OpenAPI (например, `Content-Type` не будет задокументирован) и не будет виден в автоматически генерируемой интерактивной документации.
///
/// info | Информация
Конечно, фактический заголовок `Content-Type`, статус-код и др., будут из объекта `Response`, который вы вернули.
///
### Документирование в OpenAPI и переопределение `Response`
Если вы хотите переопределить ответ из функции, но при этом документация должна содержать "тип содержимого" в OpenAPI, вы можете использовать параметр `response_class` И вернуть объект `Response`.
Тогда `response_class` будет использоваться только для документирования OpenAPI *операции пути*, но ваш `Response` будет использоваться как есть.
#### Возврат `HTMLResponse` напрямую
Например, это может быть что-то вроде:
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
В этом примере функция `generate_html_response()` уже генерирует и возвращает `Response` вместо возврата HTML в виде `str`.
Возвращая результат вызова `generate_html_response()`, вы уже возвращаете `Response`, который переопределит поведение **FastAPI** по умолчанию.
Но так как вы также передали `HTMLResponse` в `response_class`, **FastAPI** будет знать, как задокументировать это в OpenAPI и интерактивной документации как HTML с `text/html`:
<img src="/img/tutorial/custom-response/image01.png">
## Доступные ответы
Вот некоторые из доступных ответов.
Имейте в виду, что вы можете использовать `Response` для возврата чего-либо еще или даже создать собственный подкласс.
/// note | Технические детали
Вы также можете использовать `from starlette.responses import HTMLResponse`.
**FastAPI** предоставляет те же `starlette.responses`, что и `fastapi.responses`, просто для вашего удобства, как разработчика. Но большинство доступных ответов поступают напрямую из Starlette.
///
### `Response`
Основной класс `Response`, все остальные ответы наследуются от него.
Вы можете вернуть его напрямую.
Он принимает следующие параметры:
* `content` - `str` или `bytes`.
* `status_code` - `int` HTTP статус-код.
* `headers` - `dict` строк.
* `media_type` - `str`, который задает тип содержимого, например `"text/html"`.
FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Он также включит заголовок Content-Type, основанный на `media_type` и добавит кодировку для текстовых типов.
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
### `HTMLResponse`
Принимает текст или байты и возвращает HTML-ответ, как вы прочли выше.
### `PlainTextResponse`
Принимает текст или байты и возвращает ответ с простым текстом.
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
### `JSONResponse`
Принимает данные и возвращает ответ, кодированный как `application/json`.
Это ответ по умолчанию, используемый в **FastAPI**, как вы прочли выше.
### `ORJSONResponse`
Быстрая альтернатива JSON-ответа, использующая <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, как вы прочли выше.
/// info | Информация
Для этого требуется установка `orjson`, например с помощью `pip install orjson`.
///
### `UJSONResponse`
Альтернативный JSON-ответ, использующий <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
/// info | Информация
Для этого требуется установка `ujson`, например с помощью `pip install ujson`.
///
/// warning | Предупреждение
`ujson` менее осторожна, чем встроенная реализация Python, в том, как она обрабатывает некоторые крайние случаи.
///
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
/// tip | Совет
Возможно, `ORJSONResponse` может быть более быстрой альтернативой.
///
### `RedirectResponse`
Возвращает HTTP-перенаправление. Использует статус-код 307 (Временное перенаправление) по умолчанию.
Вы можете вернуть `RedirectResponse` напрямую:
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
---
Или вы можете использовать его в параметре `response_class`:
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
Если вы сделаете это, то можете вернуть URL непосредственно из вашей *функции-обработчика пути*.
В этом случае, используемый `status_code` будет тем, что по умолчанию для `RedirectResponse`, а именно `307`.
---
Вы также можете использовать параметр `status_code` в сочетании с параметром `response_class`:
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
### `StreamingResponse`
Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа в потоке.
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
#### Использование `StreamingResponse` с объектами, похожими на файлы
Если у вас есть объект, похожий на файл (например, объект, возвращаемый `open()`), вы можете создать функцию-генератор для итерации по этому объекту.
Таким образом, вам не нужно предварительно загружать все в память, и вы можете передать эту функцию-генератор в `StreamingResponse` и вернуть ее.
Это включает множество библиотек для взаимодействия с облачным хранилищем, обработки видео и других.
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
1. Это функция-генератор. Это "функция-генератор", потому что она содержит оператор `yield`.
2. Используя блок `with`, мы обеспечиваем закрытие объекта, похожего на файл, после завершения выполнения функции-генератора. То есть после того, как отправка ответа завершена.
3. Этот `yield from` указывает функции итерироваться по объекту `file_like`. Затем для каждой части, которую итерация проходит, возвращать эту часть из функции-генератора (`iterfile`).
Таким образом, это функция-генератор, которая передает работу по "генерации" чему-то еще во внутренности.
Делая так, мы можем поместить это в блок `with` и таким образом обеспечить закрытие объекта, похожего на файл, после завершения.
/// tip | Совет
Обратите внимание, что здесь, так как мы используем стандартный `open()`, который не поддерживает `async` и `await`, мы объявляем операцию пути с использованием обычного `def`.
///
### `FileResponse`
Асинхронно отправляет файл в поток как ответ.
Принимает другой набор аргументов для создания экземпляра, чем другие типы ответов:
* `path` - Путь к файлу, который необходимо отправить в поток.
* `headers` - Любые пользовательские заголовки, которые необходимо включить, в виде словаря.
* `media_type` - Строка, задающая тип содержимого. Если не установлено, для определения типа содержимого будет использоваться имя файла или путь.
* `filename` - Если установлено, это будет включено в заголовок `Content-Disposition` ответа.
Ответы на файлы будут включать соответствующие заголовки `Content-Length`, `Last-Modified` и `ETag`.
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
Вы также можете использовать параметр `response_class`:
{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
В этом случае, вы можете вернуть путь к файлу напрямую из вашей *функции-обработчика пути*.
## Класс пользовательского ответа
Вы можете создать свой собственный класс пользовательского ответа, наследуя от `Response` и используя его.
Например, предположим, вы хотите использовать <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, но с некоторыми пользовательскими настройками, не используемыми в включенном классе `ORJSONResponse`.
Допустим, вы хотите, чтобы он возвращал JSON с отступами и форматированием, поэтому вы хотите использовать опцию orjson `orjson.OPT_INDENT_2`.
Вы могли бы создать `CustomORJSONResponse`. Главное, что вам нужно сделать, это создать метод `Response.render(content)`, который возвращает содержимое как `bytes`:
{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
Теперь вместо возвращения:
```json
{"message": "Hello World"}
```
...этот ответ вернет:
```json
{
"message": "Hello World"
}
```
Конечно, вы, вероятно, найдете гораздо лучшие способы использовать это, чем форматирование JSON. 😉
## Класс ответа по умолчанию
При создании экземпляра класса **FastAPI** или `APIRouter` вы можете указать, какой класс ответа использовать по умолчанию.
Параметр, который это определяет, называется `default_response_class`.
В приведенном ниже примере **FastAPI** будет использовать `ORJSONResponse` по умолчанию во всех *операциях пути* вместо `JSONResponse`.
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
/// tip | Совет
Вы все еще можете переопределять `response_class` в *операциях пути*, как и раньше.
///
## Дополнительная документация
Вы также можете объявить тип содержимого и многие другие детали в OpenAPI, используя `responses`: [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.

95
docs/ru/docs/advanced/dataclasses.md

@ -0,0 +1,95 @@
# Использование Dataclasses
FastAPI построен на основе **Pydantic**, и я уже показывал вам, как использовать модели Pydantic для объявления HTTP-запросов и HTTP-ответов.
Но FastAPI также поддерживает использование <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> таким же образом:
{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
Это по-прежнему поддерживается благодаря **Pydantic**, так как в нем есть <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">внутренняя поддержка `dataclasses`</a>.
Итак, даже с приведенным выше кодом, который явно не использует Pydantic, FastAPI использует Pydantic для преобразования стандартных dataclasses в собственный вариант dataclasses от Pydantic.
И, конечно же, он поддерживает то же самое:
* валидация данных
* сериализация данных
* документация данных и т.д.
Это работает так же, как и с моделями Pydantic. И это фактически достигается таким же образом, под капотом, с использованием Pydantic.
/// info | Информация
Имейте в виду, что dataclasses не могут выполнять все то, что могут делать модели Pydantic.
Поэтому, возможно, вам все равно придется использовать модели Pydantic.
Но если у вас есть много dataclasses, это отличный трюк, чтобы использовать их для создания веб-API с помощью FastAPI. 🤓
///
## Dataclasses в `response_model`
Вы также можете использовать `dataclasses` в параметре `response_model`:
{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
Dataclass будет автоматически преобразован в Pydantic dataclass.
Таким образом, его схема будет отображаться в интерфейсе документации API:
<img src="/img/tutorial/dataclasses/image01.png">
## Dataclasses в Вложенных Структурах Данных
Вы также можете комбинировать `dataclasses` с другими аннотациями типов для создания вложенных структур данных.
В некоторых случаях, возможно, вам все равно придется использовать версию `dataclasses` от Pydantic. Например, если у вас возникли ошибки с автоматической генерацией документации API.
В этом случае вы можете просто заменить стандартные `dataclasses` на `pydantic.dataclasses`, что является заменой "на лету":
{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
1. Мы по-прежнему импортируем `field` из стандартных `dataclasses`.
2. `pydantic.dataclasses` является заменой "на лету" для `dataclasses`.
3. Dataclass `Author` включает список dataclasses `Item`.
4. Dataclass `Author` используется как параметр `response_model`.
5. Вы можете использовать другие стандартные аннотации типов с dataclasses как HTTP-тело запроса.
В этом случае это список dataclasses `Item`.
6. Здесь мы возвращаем словарь, содержащий `items`, который является списком dataclasses.
FastAPI по-прежнему способен <abbr title="конвертирование данных в формат, который может быть передан">сериализовать</abbr> данные в JSON.
7. Здесь `response_model` использует аннотацию типа список dataclasses `Author`.
Снова вы можете комбинировать `dataclasses` с стандартными аннотациями типов.
8. Обратите внимание, что эта *функция-обработчик пути* использует обычный `def` вместо `async def`.
Как всегда, в FastAPI вы можете сочетать `def` и `async def` в зависимости от необходимости.
Если вы забыли, когда использовать какой, посмотрите раздел _"Нет времени?"_ в документации о [`async` и `await`](../async.md#in-a-hurry){.internal-link target=_blank}.
9. Эта *функция-обработчик пути* не возвращает dataclasses (хотя могла бы), а список словарей с внутренними данными.
FastAPI будет использовать параметр `response_model` (который включает dataclasses), чтобы преобразовать ответ.
Вы можете комбинировать `dataclasses` с другими аннотациями типов в различных комбинациях, чтобы формировать сложные структуры данных.
Посмотрите советы по аннотации в коде выше, чтобы узнать больше деталей.
## Узнать больше
Вы также можете комбинировать `dataclasses` с другими моделями Pydantic, наследовать их, включать их в свои собственные модели и т.д.
Чтобы узнать больше, ознакомьтесь с <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">документацией Pydantic о dataclasses</a>.
## Версия
Это доступно начиная с версии FastAPI `0.67.0`. 🔖

165
docs/ru/docs/advanced/events.md

@ -0,0 +1,165 @@
# События жизненного цикла
Вы можете определить логику (код), которая должна быть выполнена перед запуском приложения. Это значит, что данный код будет выполнен **единожды**, **перед** тем, как приложение **начнет принимать HTTP-запросы**.
Аналогично, вы можете определить логику (код), которая должна быть выполнена при остановке приложения. В этом случае код будет выполнен **единожды**, **после** обработки, возможно, **множества HTTP-запросов**.
Поскольку этот код выполняется до того, как приложение **начинает** принимать запросы, и непосредственно после того, как оно **заканчивает** обработку запросов, он охватывает весь **lifespan** приложения (слово "lifespan" сейчас станет важным 😉).
Это может быть очень полезно для настройки **ресурсов**, которые вам нужно использовать для всего приложения, и которые **разделяются** между запросами, и/или которые требуют **освобождения** после завершения работы. Например, пул подключений к базе данных или загрузка общей модели машинного обучения.
## Пример использования
Давайте начнем с примера **использования**, а затем посмотрим, как его решить.
Предположим, у вас есть несколько **моделей машинного обучения**, которые вы хотите использовать для обработки запросов. 🤖
Те же самые модели разделяются между всеми запросами, то есть это не одна модель на запрос или одна на пользователя или что-то подобное.
Представим, что загрузка модели может **занять значительное время**, потому что необходимо прочитать много **данных с диска**. Поэтому вы не хотите делать это для каждого запроса.
Вы можете загрузить ее на верхнем уровне модуля/файла, но это также означало бы, что модель **загрузится**, даже если вы просто запускаете простой автоматизированный тест, и такой тест был бы **медленный**, так как пришлось бы ждать, пока модель загрузится, прежде чем можно было бы выполнить независимую часть кода.
Это то, что мы решаем: давайте загрузим модель до того, как запросы начнут обрабатываться, но только непосредственно перед тем, как приложение начнет принимать запросы, а не в процессе загрузки кода.
## Lifespan
Вы можете определить эту логику *запуска* и *выключения* с помощью параметра `lifespan` в приложении `FastAPI` и "менеджера контекста" (я покажу вам, что это такое, через мгновение).
Давайте начнем с примера и затем разберем его подробно.
Создаем асинхронную функцию `lifespan()` с `yield` следующим образом:
{* ../../docs_src/events/tutorial003.py hl[16,19] *}
Здесь мы симулируем дорогостоящую операцию *запуска*, загружая модель, помещая (фальшивую) функцию модели в словарь с моделями машинного обучения перед `yield`. Этот код будет выполнен **до** того, как приложение **начнет принимать запросы**, в процессе *запуска*.
И затем, сразу после `yield`, мы выгружаем модель. Этот код будет выполнен **после** того, как приложение **завершит обработку запросов**, непосредственно перед *выключением*. Это может, например, освободить такие ресурсы, как память или GPU.
/// tip | Совет
Выключение происходит, когда вы **останавливаете** приложение.
Возможно, вам нужно запустить новую версию, или вы просто устали от его работы. 🤷
///
### Функция Lifespan
Первое, что следует заметить, это то, что мы определяем асинхронную функцию с `yield`. Это очень похоже на зависимости с `yield`.
{* ../../docs_src/events/tutorial003.py hl[14:19] *}
Первая часть функции, до `yield`, будет выполнена **до** того, как приложение начнет свою работу.
А часть после `yield` будет выполнена **после** завершения работы приложения.
### Асинхронный менеджер контекста
Если вы посмотрите, функция декорирована с помощью `@asynccontextmanager`.
Это преобразует функцию в нечто, называемое "**асинхронным менеджером контекста**".
{* ../../docs_src/events/tutorial003.py hl[1,13] *}
**Менеджер контекста** в Python - это нечто, что вы можете использовать в операторе `with`, например, `open()` может быть использован как менеджер контекста:
```Python
with open("file.txt") as file:
file.read()
```
В последних версиях Python также существует **асинхронный менеджер контекста**. Вы бы использовали его с `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
Когда вы создаете менеджер контекста или асинхронный менеджер контекста, как выше, его работа заключается в том, что, до входа в блок `with`, он выполняет код перед `yield`, и после выхода из блока `with`, он выполняет код после `yield`.
В нашем примере кода выше мы не используем его напрямую, но передаем его FastAPI для использования.
Параметр `lifespan` приложения `FastAPI` принимает **асинхронный менеджер контекста**, так что мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
{* ../../docs_src/events/tutorial003.py hl[22] *}
## Альтернативные события (устаревшие)
/// warning | Предупреждение
Рекомендуемый способ обработки *запуска* и *выключения* — это использование параметра `lifespan` приложения `FastAPI`, как описано выше. Если вы предоставите параметр `lifespan`, обработчики событий `startup` и `shutdown` больше не будут вызываться. Вы можете использовать либо `lifespan`, либо события, но не оба.
Эту часть можно, вероятно, пропустить.
///
Существует альтернативный способ определения логики, которая будет выполнена во время *запуска* и во время *выключения*.
Вы можете определить обработчики событий (функции), которые необходимо выполнить до запуска приложения или при его остановке.
Эти функции могут быть объявлены как `async def`, так и обычный `def`.
### Событие `startup`
Чтобы добавить функцию, которую следует запустить перед запуском приложения, объявите ее с событием `"startup"`:
{* ../../docs_src/events/tutorial001.py hl[8] *}
В этом случае функция-обработчик события `startup` инициализирует "базу данных" элементов (просто `dict`) с некоторыми значениями.
Вы можете добавить более одной функции-обработчика события.
И ваше приложение не начнет принимать запросы до тех пор, пока все обработчики событий `startup` не будут выполнены.
### Событие `shutdown`
Чтобы добавить функцию, которую следует запустить при остановке приложения, объявите ее с событием `"shutdown"`:
{* ../../docs_src/events/tutorial002.py hl[6] *}
Здесь функция-обработчик события `shutdown` запишет строку текста `"Application shutdown"` в файл `log.txt`.
/// info | Информация
В функции `open()`, `mode="a"` означает "добавить", так что строка будет добавлена после того, что уже есть в этом файле, без перезаписи предыдущего содержимого.
///
/// tip | Совет
Обратите внимание, что в данном случае мы используем стандартную функцию Python `open()`, которая взаимодействует с файлом.
Таким образом, это включает в себя ввод/вывод (I/O), который требует "ожидания" для записи данных на диск.
Но `open()` не использует `async` и `await`.
Поэтому мы объявляем функцию-обработчик события как стандартный `def` вместо `async def`.
///
### `startup` и `shutdown` вместе
Существует большая вероятность, что логика ваших *запуска* и *выключения* связана; возможно, вы хотите что-то запустить, а затем завершить, получить ресурс, а затем освободить его и т. д.
Делать это в раздельных функциях, которые не используют совместно логику или переменные, сложнее, так как придется сохранять значения в глобальных переменных или использовать подобные уловки.
Из-за этого теперь рекомендуется вместо этого использовать `lifespan`, как объяснено выше.
## Технические детали
Просто техническая деталь для любознательных гиков. 🤓
В основе, в технической спецификации ASGI, это часть <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Протокола Lifespan</a>, и он определяет события, называемые `startup` и `shutdown`.
/// info | Информация
Вы можете прочитать больше об обработчиках `lifespan` в Starlette в <a href="https://www.starlette.io/lifespan/" class="external-link" target="_blank">документации "Lifespan" Starlette</a>.
Включая информацию о том, как обрабатывать состояние lifespan, которое может быть использовано в других частях вашего кода.
///
## Подприложения
🚨 Помните, что эти события жизненного цикла (запуска и завершения) будут выполнены только для основного приложения, а не для [подприложений - монтировок](sub-applications.md){.internal-link target=_blank}.

261
docs/ru/docs/advanced/generate-clients.md

@ -0,0 +1,261 @@
# Генерация клиентов
Так как **FastAPI** основан на спецификации OpenAPI, вы автоматически получаете совместимость со многими инструментами, включая автоматическую документацию API (предоставляемую Swagger UI).
Один конкретный плюс, который не всегда очевиден, заключается в том, что вы можете **сгенерировать клиентов** (иногда их называют <abbr title="Software Development Kits">**SDKs**</abbr> ) для вашего API для многих разных **языков программирования**.
## Генераторы клиентов OpenAPI
Существует множество инструментов для генерации клиентов из **OpenAPI**.
Один из популярных инструментов — это <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>.
Если вы создаете **фронтенд**, очень интересной альтернативой является <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>.
## Генераторы клиентов и SDK - спонсоры
Существуют также некоторые **поддерживаемые компаниями** генераторы клиентов и SDK, основанные на OpenAPI (FastAPI), в некоторых случаях они могут предложить вам **дополнительные функции** поверх высококачественных сгенерированных SDK/клиентов.
Некоторые из них также ✨ [**спонсируют FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, что обеспечивает продолжительное и здоровое **развитие** FastAPI и его **экосистемы**.
Это также демонстрирует их истинную приверженность FastAPI и его **сообществу** (вам), ведь они не только хотят предоставить вам **качественное обслуживание**, но и стремятся обеспечить вам **качественный и здоровый фреймворк**, FastAPI. 🙇
Например, вам может быть интересно попробовать:
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
Также существует несколько других компаний, предлагающих аналогичные услуги, которые вы можете найти в интернете. 🤓
## Генерация TypeScript-клиента для фронтенда
Давайте начнем с простой FastAPI-программы:
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
Обратите внимание, что *операции пути* определяют модели, которые они используют для полезной нагрузки запроса и ответа, используя модели `Item` и `ResponseMessage`.
### Документация API
Если вы перейдете к документации API, вы увидите, что в ней содержатся **схемы** для данных, которые должны быть отправлены в запросах и получены в ответах:
<img src="/img/tutorial/generate-clients/image01.png">
Вы видите эти схемы, потому что они были объявлены с моделями в приложении.
Эта информация доступна в **схеме OpenAPI** приложения и затем отображается в документации API (с помощью Swagger UI).
И эта же информация из моделей, включенных в OpenAPI, может быть использована для **генерации кода клиента**.
### Генерация TypeScript-клиента
Теперь, когда у нас есть приложение с моделями, мы можем сгенерировать код клиента для фронтенда.
#### Установка `openapi-ts`
Вы можете установить `openapi-ts` в код своего фронтенда с помощью:
<div class="termy">
```console
$ npm install @hey-api/openapi-ts --save-dev
---> 100%
```
</div>
#### Генерация кода клиента
Чтобы сгенерировать код клиента, вы можете использовать командное приложение `openapi-ts`, которое теперь будет установлено.
Поскольку оно установлено в локальном проекте, вы, вероятно, не сможете вызвать эту команду напрямую, но вы можете поместить её в свой файл `package.json`.
Он может выглядеть следующим образом:
```JSON hl_lines="7"
{
"name": "frontend-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
},
"author": "",
"license": "",
"devDependencies": {
"@hey-api/openapi-ts": "^0.27.38",
"typescript": "^4.6.2"
}
}
```
После добавления этого скрипта NPM `generate-client` вы можете запустить его с помощью:
<div class="termy">
```console
$ npm run generate-client
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
```
</div>
Эта команда сгенерирует код в `./src/client` и будет использовать `axios` (фронтенд HTTP-библиотеку) для внутренних нужд.
### Попробуйте код клиента
Теперь вы можете импортировать и использовать код клиента, он может выглядеть следующим образом, обратите внимание, что вы получаете автозавершение для методов:
<img src="/img/tutorial/generate-clients/image02.png">
Вы также получите автозавершение для отправляемой полезной нагрузки:
<img src="/img/tutorial/generate-clients/image03.png">
/// tip | Совет
Обратите внимание на автозавершение для `name` и `price`, которые были определены в приложении FastAPI, в модели `Item`.
///
Вы получите ошибки на лету для данных, которые вы отправляете:
<img src="/img/tutorial/generate-clients/image04.png">
Объект-ответ также будет иметь автозавершение:
<img src="/img/tutorial/generate-clients/image05.png">
## FastAPI-приложение с тегами
Во многих случаях ваше FastAPI-приложение будет больше, и вы, скорее всего, будете использовать теги для разделения разных групп *операций пути*.
Например, у вас может быть раздел для **товаров** и другой раздел для **пользователей**, и они могут быть разделены с помощью тегов:
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
### Генерация TypeScript-клиента с тегами
Если вы генерируете клиента для FastAPI-приложения с использованем тегов, то обычно клиентский код также будет разделен по тегам.
Таким образом, вы сможете правильно упорядочить и сгруппировать клиентский код:
<img src="/img/tutorial/generate-clients/image06.png">
В данном случае у вас есть:
* `ItemsService`
* `UsersService`
### Имена методов клиента
Сейчас сгенерированные имена методов, такие как `createItemItemsPost`, не выглядят очень чистыми:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...это потому, что генератор клиента использует внутренний **идентификатор операции** OpenAPI для каждой *операции пути*.
OpenAPI требует, чтобы каждый идентификатор операции был уникальным среди всех *операций пути*, поэтому FastAPI использует **название функции**, **путь** и **метод/операцию HTTP** для генерации этого идентификатора операции, поскольку таким образом он может обеспечить уникальность идентификаторов операций.
Но я покажу вам, как улучшить это. 🤓
## Пользовательские идентификаторы операций и лучшие имена методов
Вы можете **изменить** способ **генерации** этих идентификаторов операций, чтобы упростить их и получить **более простые названия методов** в клиентах.
В этом случае вы должны обеспечить уникальность каждого идентификатора операции другим способом.
Например, вы можете убедиться, что каждая *операция пути* имеет тег, а затем генерировать идентификатор операции на основе **тега** и **названия операции пути** (названия функции).
### Пользовательская функция генерации уникального идентификатора
FastAPI использует **уникальный ID** для каждой *операции пути*, он используется для **идентификатора операции**, а также для имен любых необходимых пользовательских моделей для запросов или ответов.
Вы можете настроить эту функцию. Она принимает `APIRoute` и возвращает строку.
Например, здесь используется первый тег (обычно у вас будет только один тег) и название *операции пути* (название функции).
Вы можете передать эту пользовательскую функцию **FastAPI** в параметре `generate_unique_id_function`:
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
### Генерация TypeScript-клиента с пользовательскими идентификаторами операций
Теперь, если вы снова сгенерируете клиент, вы увидите, что у него улучшенные названия методов:
<img src="/img/tutorial/generate-clients/image07.png">
Как видите, названия методов теперь содержат тег, а затем имя функции, теперь они не включают информацию из URL-пути и HTTP-операции.
### Предобработка спецификации OpenAPI для генератора клиентов
Сгенерированный код все равно содержит некоторую **дублирующую информацию**.
Мы уже знаем, что этот метод связан с **товарами (items)**, потому что это слово появилось в `ItemsService` (взято из тега), но у нас все равно есть имя тега в имени метода. 😕
Вероятно, мы все равно захотим сохранить это для OpenAPI в целом, так как это обеспечит **уникальность** идентификаторов операций.
Но для сгенерированного клиента мы можем **изменить** идентификаторы операций OpenAPI прямо перед генерацией клиентов, только чтобы сделать эти названия методов более приятными и **чистыми**.
Мы можем загрузить OpenAPI JSON в файл `openapi.json`, а затем **удалить** этот префикс тега с помощью такого скрипта:
{* ../../docs_src/generate_clients/tutorial004.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Таким образом, идентификаторы операций будут переименованы из `items-get_items` в просто `get_items`, так генератор клиентов сможет генерировать более простые имена методов.
### Генерация TypeScript-клиента с предобработанным OpenAPI
Теперь, так как конечный результат находится в файле `openapi.json`, вы измените файл `package.json`, чтобы использовать этот локальный файл, например:
```JSON hl_lines="7"
{
"name": "frontend-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
},
"author": "",
"license": "",
"devDependencies": {
"@hey-api/openapi-ts": "^0.27.38",
"typescript": "^4.6.2"
}
}
```
После генерации нового клиента у вас теперь будут **чистые названия методов**, с всеми **подсказками автозавершения**, **ошибками на лету** и т.д.:
<img src="/img/tutorial/generate-clients/image08.png">
## Преимущества
При использовании автоматически сгенерированных клиентов вы получите **автозавершение** для:
* Методов.
* Полезной нагрузки запроса в теле, параметрах запроса и т.д.
* Полезной нагрузки ответа.
Вы также получите **ошибки на лету** для всего.
И всякий раз, когда вы обновляете код на серверной стороне и **регенерируете** фронтенд, он будет содержать любые новые *операции пути* как методы, старые будут удалены, а любые другие изменения будут отражены в сгенерированном коде. 🤓
Это также означает, что если что-то изменится, это будет **отражено** в клиентском коде автоматически. И если вы **соберёте** клиент, у него возникнет ошибка, если у вас возникнет **несоответствие** в используемых данных.
Таким образом, вы **обнаружите множество ошибок** на раннем этапе цикла разработки, вместо того чтобы ждать, пока ошибки проявятся у ваших конечных пользователей в продакшне, и потом пытаться выяснить, в чем проблема. ✨

96
docs/ru/docs/advanced/middleware.md

@ -0,0 +1,96 @@
# Продвинутое использование Middleware
В основном руководстве вы узнали, как добавить [пользовательский Middleware](../tutorial/middleware.md){.internal-link target=_blank} в ваше приложение.
А также вы узнали, как обрабатывать [CORS с использованием `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
В этом разделе мы увидим, как использовать другие middleware.
## Добавление ASGI middleware
Так как **FastAPI** основан на Starlette и реализует спецификацию <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr>, вы можете использовать любой ASGI middleware.
Для работы middleware не обязательно должны быть созданы специально для FastAPI или Starlette, достаточно того, чтобы они следовали спецификации ASGI.
В общем случае, ASGI middleware представляют собой классы, которые ожидают получение ASGI приложения в качестве первого аргумента.
Таким образом, в документации для сторонних ASGI middleware, скорее всего, будет указано сделать следующее:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Но FastAPI (на самом деле Starlette) предоставляет более простой способ сделать это, обеспечивая правильную обработку ошибок сервера и работу пользовательских обработчиков ошибок.
Для этого используйте `app.add_middleware()` (как в примере для CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` принимает класс middleware в качестве первого аргумента и любые дополнительные аргументы, которые должны быть переданы в middleware.
## Интегрированные middleware
**FastAPI** включает несколько middleware для общих случаев использования, далее мы рассмотрим, как их использовать.
/// note | Технические детали
В следующих примерах вы также можете использовать `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** предоставляет несколько middleware в `fastapi.middleware` исключительно для удобства разработчика. Но большинство доступных middleware поступает непосредственно из Starlette.
///
## `HTTPSRedirectMiddleware`
Обеспечивает, чтобы все входящие запросы были перенаправлены на `https` или `wss`.
Любой входящий запрос на `http` или `ws` будет перенаправлен на защищенную схему.
{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
## `TrustedHostMiddleware`
Обеспечивает наличие корректно заданного HTTP-заголовка `Host` во всех входящих запросах для защиты от атак на основе HTTP-заголовка Host.
{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
Поддерживаются следующие аргументы:
* `allowed_hosts` - список доменных имен, которые разрешены в качестве имен хостов. Разрешены шаблоны доменов, такие как `*.example.com`, для соответствия поддоменам. Для разрешения всех имен хостов можно использовать `allowed_hosts=["*"]` или вовсе не добавлять middleware.
Если входящий запрос не валидируется, отправляется ответ с кодом `400`.
## `GZipMiddleware`
Обрабатывает GZip-ответы для любого запроса, который включает `"gzip"` в HTTP-заголовке `Accept-Encoding`.
Middleware будет обрабатывать как стандартные, так и потоковые HTTP-ответы.
{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
Поддерживаются следующие аргументы:
* `minimum_size` - не выполнять GZip сжатие для HTTP-ответов, которые меньше этого минимального размера в байтах. По умолчанию `500`.
* `compresslevel` - используется во время GZip сжатия. Это число в диапазоне от 1 до 9. По умолчанию `9`. Меньшее значение приводит к более быстрому сжатию, но большему размеру файлов, в то время как большее значение приводит к более медленному сжатию, но меньшему размеру файлов.
## Другие middleware
Существует много других ASGI middleware.
Например:
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">`ProxyHeadersMiddleware` от Uvicorn</a>
* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
Чтобы увидеть другие доступные middleware, ознакомьтесь с <a href="https://www.starlette.io/middleware/" class="external-link" target="_blank">документацией Starlette по Middleware</a> и <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">списком ASGI Awesome List</a>.

186
docs/ru/docs/advanced/openapi-callbacks.md

@ -0,0 +1,186 @@
# OpenAPI Callbacks
Вы можете создать API с *операцией пути*, которая может инициировать запрос к *внешнему API*, созданному кем-то другим (вероятно, тем же разработчиком, который будет *использовать* ваше API).
Процесс, который происходит, когда ваше API-приложение вызывает *внешний API*, называется "callback". Потому что ПО, написанное внешним разработчиком, отправляет запрос вашему API, а затем ваше API *отвечает*, отправляя запрос к *внешнему API* (который, возможно, был создан тем же разработчиком).
В этом случае, вы могли бы захотеть задокументировать, как этот внешний API *должен* выглядеть. Какие *операции пути* он должен содержать, какое тело ожидать, какой ответ должен возвращать и т.д.
## Приложение с callback'ами
Рассмотрим это на примере.
Представьте, что вы разрабатываете приложение, позволяющее создавать счета.
Эти счета будут иметь `id`, `title` (опционально), `customer` и `total`.
Пользователь вашего API (внешний разработчик) создаст счет в вашем API с помощью POST-запроса.
Затем ваше API будет (давайте представим):
* Отправлять счет какому-то клиенту внешнего разработчика.
* Собирать деньги.
* Отправлять уведомление обратно пользователю API (внешнему разработчику).
* Это будет сделано с помощью отправки POST-запроса (от *вашего API*) в некоторый *внешний API*, предоставленный этим внешним разработчиком (это и есть "callback").
## Обычное приложение **FastAPI**
Сначала посмотрим, как обычное приложение API выглядело бы до добавления callback'а.
Оно будет иметь *операцию пути*, которая будет принимать `Invoice` в теле запроса и параметр запроса `callback_url`, который будет содержать URL для callback'а.
Эта часть довольно обычная, и, вероятно, большая часть кода уже знакома вам:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
/// tip | Подсказка
Параметр запроса `callback_url` использует тип Url из Pydantic <a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a>.
///
Единственное новое здесь — это `callbacks=invoices_callback_router.routes` как аргумент к *декоратору операции пути*. Дальше мы увидим, что это значит.
## Документирование callback'а
Фактический код callback'а будет сильно зависеть от вашего API-приложения.
И, вероятно, значительно изменится от одного приложения к другому.
Это может быть всего одна или две строчки кода, такие как:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Но, возможно, наиболее важная часть callback'а — это убедиться, что пользователь вашего API (внешний разработчик) корректно реализует *внешний API*, согласно данным, которые *ваше API* собирается отправить в теле запроса callback'а и т.д.
Итак, следующее, что мы сделаем, это добавим код для документации, как этот *внешний API* должен выглядеть для получения callback'а от *вашего API*.
Эта документация отобразится в интерфейсе Swagger по адресу `/docs` в вашем API и позволит внешним разработчикам узнать, как создать *внешний API*.
Этот пример не реализует сам callback (это может быть всего одна строка кода), только часть документации.
/// tip | Подсказка
Фактический callback — это просто HTTP-запрос.
При реализации callback'а самостоятельно, вы можете использовать что-то вроде <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> или <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>.
///
## Написание кода документации для callback'а
Этот код не будет выполнен в вашем приложении, нам нужно его только чтобы *документировать*, как *внешний API* должен выглядеть.
Но вы уже знаете, как легко создавать автоматическую документацию для API с **FastAPI**.
Поэтому мы используем эти же знания, чтобы задокументировать, как *внешний API* должен выглядеть... создавая *операцию(ии) пути*, которые внешний API должен реализовать (те, которые ваше API вызовет).
/// tip | Подсказка
Когда пишете код для документации callback'а, может быть полезно представить, что вы — этот *внешний разработчик*. И что вы в данный момент реализуете *внешний API*, а не *ваше API*.
Временное принятие этой точки зрения (внешнего разработчика) поможет вам скорее определить, куда помещать параметры, Pydantic-модель для тела запроса, для ответа и т.д. для этого *внешнего API*.
///
### Создание callback `APIRouter`
Сначала создайте новый `APIRouter`, который будет содержать один или несколько callback'ов.
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
### Создание callback *операции пути*
Чтобы создать callback *операцию пути*, используйте тот же `APIRouter`, который вы создали выше.
Она должна выглядеть как обычная операция пути FastAPI:
* Она, вероятно, должна иметь объявление тела запроса, которое она должна принять, например `body: InvoiceEvent`.
* И она может также иметь объявление ответа, который она должна вернуть, например `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[16:18,21:22,28:32] *}
Есть 2 основных отличия от обычной *операции пути*:
* Реальный код не нужен, так как ваше приложение никогда не вызовет этот код. Он используется только для документирования *внешнего API*. Поэтому функция может просто содержать `pass`.
* В *пути* может содержаться <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">выражение OpenAPI 3</a> (подробнее ниже), где можно использовать переменные с параметрами и частями оригинального запроса, отправленного к *вашему API*.
### Выражение пути callback'а
Путь callback'а может содержать <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">выражение OpenAPI 3</a>, которое может включать части оригинального запроса, отправленного к *вашему API*.
В этом случае это `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Таким образом, если пользователь вашего API (внешний разработчик) отправляет запрос к *вашему API* по адресу:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
с JSON-телом:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
тогда *ваше API* обработает счет, и чуть позже отправит callback-запрос на `callback_url` (во *внешний API*):
```
https://www.external.org/events/invoices/2expen51ve
```
с JSON-телом, содержащим что-то вроде:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
и ожидает ответа от этого *внешнего API* с JSON-телом вроде:
```JSON
{
"ok": true
}
```
/// tip | Подсказка
Обратите внимание, как callback URL включает URL, полученный как параметр запроса в `callback_url` (`https://www.external.org/events`), и также `id` счета из тела JSON (`2expen51ve`).
///
### Добавление router для callback'ов
На данном этапе у вас есть *операция(ии) пути для callback'а*, необходимые (те, которые *внешний разработчик* должен реализовать во *внешнем API*) в callback router, который вы создали выше.
Теперь используйте параметр `callbacks` в *декораторе операции пути вашего API*, чтобы передать атрибут `.routes` (который на самом деле является просто `list`'ом маршрутов/*операций пути*) из этого callback router:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
/// tip | Подсказка
Обратите внимание, что вы не передаете сам router (`invoices_callback_router`) в `callback=`, а передаете атрибут `.routes`, как в `invoices_callback_router.routes`.
///
### Проверка документации
Теперь вы можете запустить ваше приложение и перейти на <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Вы увидите вашу документацию, включая раздел "Callbacks" для вашей *операции пути*, который показывает, как должен выглядеть *внешний API*:
<img src="/img/tutorial/openapi-callbacks/image01.png">

55
docs/ru/docs/advanced/openapi-webhooks.md

@ -0,0 +1,55 @@
# OpenAPI Webhooks
Существуют случаи, когда вы хотите сообщить пользователям вашего API, что ваше приложение может отправлять запросы их приложениям с данными, обычно для уведомления о каком-то событии.
Это означает, что вместо обычного процесса, когда ваши пользователи отправляют запросы вашему API, это ваш API (или ваше приложение) может отправлять запросы их системе (их API, их приложению).
Это обычно называется **webhook**.
## Шаги вебхуков
Процесс обычно состоит в том, что **вы определяете** в вашем коде сообщение, которое вы собираетесь отправить, **тело запроса**.
Вы также каким-либо образом определяете, в какие **моменты** ваше приложение будет отправлять эти запросы или события.
И **ваши пользователи** каким-либо образом (например, в веб-панели управления) определяют **URL**, куда ваше приложение должно отправлять эти запросы.
Вся **логика** регистрации URL-адресов для вебхуков и кода для фактической отправки этих запросов зависит от вас. Вы пишете это в **собственном коде** так, как хотите.
## Документирование вебхуков с помощью **FastAPI** и OpenAPI
С помощью **FastAPI** и OpenAPI вы можете определить названия этих вебхуков, типы HTTP операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), и **тела запросов**, которые ваше приложение будет отправлять.
Это может значительно упростить вашим пользователям **внедрение их API** для получения ваших **вебхуков**, они даже могут автоматически сгенерировать часть своего API кода.
/// info | Информация
Вебхуки доступны в OpenAPI 3.1.0 и выше, поддерживаемые FastAPI `0.99.0` и выше.
///
## Приложение с вебхуками
Когда вы создаете приложение **FastAPI**, есть атрибут `webhooks`, который вы можете использовать для определения *вебхуков* так же, как вы бы определяли *операции пути*, например с помощью `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
Вебхуки, которые вы определяете, попадут в **схему OpenAPI** и в **автоматическую документацию UI**.
/// info | Информация
Объект `app.webhooks` на самом деле является `APIRouter`, того же типа, который вы бы использовали при структурировании своего приложения с множеством файлов.
///
Обратите внимание, что с вебхуками вы на самом деле не объявляете *путь* (например, `/items/`), текст, который вы передаете, является просто **идентификатором** вебхука (название события), например в `@app.webhooks.post("new-subscription")`, имя вебхука — `new-subscription`.
Это потому, что ожидается, что **ваши пользователи** определят фактический **путь URL**, куда они хотят получать запрос webhook, каким-то другим образом (например, в веб-панели управления).
### Проверьте документацию
Теперь вы можете запустить ваше приложение и перейти по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Вы увидите, что ваша документация содержит обычные *операции пути* и теперь также некоторые **вебхуки**:
<img src="/img/tutorial/openapi-webhooks/image01.png">

204
docs/ru/docs/advanced/path-operation-advanced-configuration.md

@ -0,0 +1,204 @@
# Продвинутая конфигурация операций пути
## OpenAPI operationId
/// warning | Предупреждение
Если вы не "эксперт" в OpenAPI, то, скорее всего, вам это не понадобится.
///
Вы можете задать `operationId` для OpenAPI, который будет использоваться в вашей *операции пути* с помощью параметра `operation_id`.
Следует удостовериться, что он уникален для каждой операции.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
### Использование имени функции *операции пути* в качестве operationId
Если вы хотите использовать имена ваших функций API в качестве `operationId`, вы можете перебрать их все и переопределить `operation_id` каждой *операции пути*, используя `APIRoute.name`.
Необходимо сделать это после добавления всех ваших *операций пути*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
/// tip | Совет
Если вы вручную вызываете `app.openapi()`, обновите `operationId` до этого.
///
/// warning | Предупреждение
Если вы это делаете, необходимо удостовериться, что каждая из ваших *функций операций пути* имеет уникальное имя.
Даже если они находятся в разных модулях (файлах Python).
///
## Исключение из OpenAPI
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (и таким образом, из систем автоматической документации), используйте параметр `include_in_schema` и установите его значение как `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
## Расширенное описание из строки документации
Вы можете ограничить количество строк, используемых из строки документации функции *операции пути* для OpenAPI.
Добавление `\f` (экранированный символ "перевод страницы") приведет к тому, что **FastAPI** обрежет вывод, используемый для OpenAPI, на этом месте.
Этот символ не покажется в документации, но другие инструменты (такие как Sphinx) смогут использовать остальную часть.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
## Дополнительные ответы
Вы, вероятно, уже знаете, как объявить `response_model` и `status_code` для *операции пути*.
Это определяет метаданные об основном ответе *операции пути*.
Вы также можете объявить дополнительные ответы с их моделями, статус-кодами и т.д.
В документации есть целая глава об этом, которую вы можете прочитать в разделе [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
## Дополнения OpenAPI
Когда вы объявляете *операцию пути* в вашем приложении, **FastAPI** автоматически генерирует соответствующие метаданные об этой *операции пути*, чтобы включить их в схему OpenAPI.
/// note | Технические детали
В спецификации OpenAPI это называется <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Объект операции</a>.
///
Это содержит всю информацию о *операции пути* и используется для генерации автоматической документации.
Это включает в себя `tags`, `parameters`, `requestBody`, `responses` и т.д.
Эта схема OpenAPI для данной *операции пути* обычно генерируется автоматически **FastAPI**, но ее можно и расширить.
/// tip | Совет
Это точка низкоуровневого расширения.
Если вам нужно только объявить дополнительные ответы, более удобный способ сделать это — через [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
///
Вы можете расширить схему OpenAPI для *операции пути* с помощью параметра `openapi_extra`.
### Расширения OpenAPI
Этот `openapi_extra` может быть полезен, например, чтобы объявить [Расширения OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
Если вы откроете автоматическую документацию API, ваше расширение появится внизу конкретной *операции пути*.
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
И если вы увидите результат OpenAPI (по адресу `/openapi.json` в вашем API), вы увидите ваше расширение также как часть конкретной *операции пути*:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Пользовательская схема OpenAPI *операции пути*
Словарь в `openapi_extra` будет глубоко объединен с автоматически сгенерированной схемой OpenAPI для *операции пути*.
Так вы можете добавить дополнительные данные в автоматически сгенерированную схему.
Например, вы можете решить сами считывать и валидировать запрос без использования автоматических функций FastAPI с Pydantic, но вы все равно можете захотеть определить запрос в схеме OpenAPI.
Вы можете сделать это с помощью `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
В этом примере мы не объявили никакой Pydantic-модели. На самом деле тело запроса даже не <abbr title="преобразовано из какого-либо простого формата, как байты, в Python-объекты">проанализировано</abbr> как JSON, оно считывается напрямую как `bytes`, и функция `magic_data_reader()` будет отвечать за его парсинг.
Тем не менее, мы можем объявить ожидаемую схему для тела запроса.
### Пользовательский тип содержимого OpenAPI
Используя этот же трюк, вы можете использовать Pydantic-модель для определения JSON-схемы, которая затем будет включена в пользовательский раздел схемы OpenAPI для *операции пути*.
И вы можете сделать это даже если тип данных в запросе не JSON.
Например, в этом приложении мы не используем интегрированные функции FastAPI для извлечения JSON-схемы из Pydantic-моделей и автоматической валидации для JSON. В самом деле, мы объявляем тип содержимого запроса как YAML, а не JSON:
//// tab | Pydantic v2
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22, 24] *}
////
//// tab | Pydantic v1
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22, 24] *}
////
/// info | Информация
В версии Pydantic 1 метод для получения JSON-схемы для модели назывался `Item.schema()`, в версии Pydantic 2 метод называется `Item.model_json_schema()`.
///
Тем не менее, хотя мы не используем стандартную интегрированную функциональность, мы все же используем модель Pydantic, чтобы вручную сгенерировать JSON-схему для данных, которые мы хотим получить в YAML.
Затем мы используем запрос напрямую и извлекаем тело как `bytes`. Это означает, что FastAPI даже не будет пытаться парсить содержимое запроса как JSON.
И затем в нашем коде мы парсим это YAML-содержимое напрямую, и затем снова используем ту же модель Pydantic для валидации содержимого YAML:
//// tab | Pydantic v2
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
////
//// tab | Pydantic v1
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
////
/// info | Информация
В версии Pydantic 1 метод для парсинга и валидации объекта назывался `Item.parse_obj()`, в версии Pydantic 2 метод называется `Item.model_validate()`.
///
/// tip | Совет
Здесь мы повторно используем ту же модель Pydantic.
Но таким же образом можно было бы валидировать ее каким-то другим способом.
///

41
docs/ru/docs/advanced/response-headers.md

@ -0,0 +1,41 @@
# HTTP-заголовки ответа
## Использование параметра `Response`
Вы можете объявить параметр типа `Response` в вашей *функции-обработчике пути* (так же, как вы делаете для cookie).
Затем вы можете установить HTTP-заголовки в этом *временном* объекте ответа.
{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
После этого вы можете вернуть любой объект, который вам нужен, как обычно (например, `dict`, модель базы данных и т.д.).
Если вы объявили `response_model`, он все равно будет использован для фильтрации и преобразования объекта, который вы вернули.
**FastAPI** использует этот *временный* ответ для извлечения HTTP-заголовков (также cookie и статус-код ответа) и помещает их в окончательный ответ, который содержит значение, которое вы вернули и которое фильтруется любым `response_model`.
Вы также можете объявить параметр `Response` в зависимостях и установить HTTP-заголовки (и cookie) в них.
## Непосредственный возврат `Response`
Вы также можете добавить HTTP-заголовки при непосредственном возврате объекта типа `Response`.
Создайте ответ, как описано в разделе [Непосредственный возврат ответа](response-directly.md){.internal-link target=_blank}, и передайте HTTP-заголовки в качестве дополнительного параметра:
{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
/// note | Технические детали
Вы также могли бы использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет те же `starlette.responses`, что и `fastapi.responses`, для удобства разработчика. Но большинство доступных ответов поступает непосредственно из Starlette.
И так как `Response` может часто использоваться для установки HTTP-заголовков и cookie, **FastAPI** также предоставляет его в `fastapi.Response`.
///
## Пользовательские заголовки
Имейте в виду, что можно добавлять проприетарные пользовательские 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)](../tutorial/cors.md){.internal-link target=_blank}), используя параметр `expose_headers`, документированный в <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">документации Starlette по CORS</a>.

107
docs/ru/docs/advanced/security/http-basic-auth.md

@ -0,0 +1,107 @@
# HTTP Basic Auth
Для самых простых случаев можно использовать HTTP Basic Auth.
В HTTP Basic Auth приложение ожидает HTTP-заголовок, содержащий имя пользователя и пароль.
Если он не получит их, то вернет ошибку HTTP 401 "Unauthorized" (неавторизовано).
Также будет возвращен HTTP-заголовок `WWW-Authenticate` со значением `Basic` и опциональным параметром `realm`.
Это говорит браузеру показать встроенную панель для ввода имени пользователя и пароля.
Затем, когда вы вводите эти имя пользователя и пароль, браузер отправляет их в заголовке автоматически.
## Простая HTTP Basic Auth
* Импортируйте `HTTPBasic` и `HTTPBasicCredentials`.
* Создайте "`security` scheme" используя `HTTPBasic`.
* Используйте эту `security` зависимость в вашей *операции пути*.
* Она возвращает объект типа `HTTPBasicCredentials`:
* Содержит отправленные `username` и `password`.
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
Когда вы попытаетесь открыть URL в первый раз (или нажмете кнопку "Execute" в документации) браузер спросит вас имя пользователя и пароль:
<img src="/img/tutorial/security/image12.png">
## Проверка имени пользователя
Вот более полный пример.
Используйте зависимость, чтобы проверить, правильные ли имя пользователя и пароль.
Для этого используйте стандартный модуль Python <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> для проверки имени пользователя и пароля.
`secrets.compare_digest()` требует `bytes` или `str`, содержащий только ASCII-символы (те, что на английском), это значит, что он не будет работать с символами, такими как `á`, например в `Sebastián`.
Чтобы с этим справиться, мы сначала конвертируем `username` и `password` в `bytes`, кодируя их в UTF-8.
Затем мы можем использовать `secrets.compare_digest()` для гарантии, что `credentials.username` равен `"stanleyjobson"`, а `credentials.password` равен `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
Это будет аналогично:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Return some error
...
```
Но, используя `secrets.compare_digest()`, это будет защищено от атак под названием "тайминговые атаки".
### Тайминговые атаки
Что такое "тайминговая атака"?
Представим, что некоторая группа злоумышленников пытается угадать имя пользователя и пароль.
Они отправляют запрос с именем пользователя `johndoe` и паролем `love123`.
Тогда Python-код в вашем приложении будет эквивалентен чему-то вроде:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Но в тот момент, когда Python сравнивает первую `j` в `johndoe` с первой `s` в `stanleyjobson`, он вернет `False`, так как уже знает, что эти две строки не одинаковы, считая, что "нет смысла тратить больше вычислительных ресурсов на сравнение остальных букв". И ваше приложение скажет "Неправильное имя пользователя или пароль".
Но затем злоумышленники попробуют с именем пользователя `stanleyjobsox` и паролем `love123`.
И ваш код приложения делает что-то вроде:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python должен будет сравнить весь `stanleyjobso` в `stanleyjobsox` и `stanleyjobson`, чтобы понять, что строки не одинаковы. Так что это займет дополнительные микросекунды, чтобы ответить "Неправильное имя пользователя или пароль".
#### Время ответа помогает злоумышленникам
В этот момент, заметив, что серверу понадобилось немного больше времени, чтобы отправить ответ "Неправильное имя пользователя или пароль", злоумышленники узнают, что они что-то угадали, некоторые начальные буквы были правильными.
И тогда они могут попробовать снова, зная, что это, вероятно, что-то более похожее на `stanleyjobsox`, чем на `johndoe`.
#### "Профессиональная" атака
Конечно, злоумышленники не будут делать все это вручную, они напишут программу, чтобы делать это, возможно, с тысячами или миллионами тестов в секунду. И они будут получать лишь одну правильную букву за раз.
Но, делая так, за несколько минут или часов злоумышленники могли бы угадать правильное имя пользователя и пароль, с "помощью" нашего приложения, просто используя время, затраченное на ответ.
#### Исправьте это с помощью `secrets.compare_digest()`
Но в нашем коде мы фактически используем `secrets.compare_digest()`.
Вкратце, это займет одинаковое время, чтобы сравнить `stanleyjobsox` с `stanleyjobson`, как и `johndoe` с `stanleyjobson`. И то же самое для пароля.
Таким образом, используя `secrets.compare_digest()` в коде вашего приложения, оно будет защищено от этого целого спектра атак на безопасность.
### Возврат ошибки
После обнаружения, что учетные данные неверны, верните `HTTPException` с кодом состояния 401 (тот же, что возвращается, когда учетные данные не предоставлены) и добавьте заголовок `WWW-Authenticate`, чтобы браузер снова отобразил приглашение для входа:
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}

19
docs/ru/docs/advanced/security/index.md

@ -0,0 +1,19 @@
# Расширенная безопасность
## Дополнительные функции
Существуют дополнительные функции для обеспечения безопасности, кроме тех, которые рассмотрены в [Руководстве пользователя: Безопасность](../../tutorial/security/index.md){.internal-link target=_blank}.
/// tip | Совет
Следующие разделы **не обязательно "продвинутые"**.
И возможно, что для вашего случая решения находятся в одном из них.
///
## Сначала прочтите Руководство
Следующие разделы предполагают, что вы уже прочитали основное [Руководство пользователя: Безопасность](../../tutorial/security/index.md){.internal-link target=_blank}.
Все они основаны на тех же концепциях, но позволяют использовать некоторые дополнительные функции.

274
docs/ru/docs/advanced/security/oauth2-scopes.md

@ -0,0 +1,274 @@
# OAuth2 scope
Вы можете использовать OAuth2 scope напрямую с **FastAPI**, они интегрированы для работы без проблем.
Это позволит вам иметь более тонкую систему разрешений, следуя стандарту OAuth2, интегрированную в ваше OpenAPI приложение (и документы API).
OAuth2 с scope — это механизм, используемый многими крупными провайдерами аутентификации, такими как Facebook, Google, GitHub, Microsoft, Twitter и т.д. Они используют его для предоставления определенных разрешений пользователям и приложениям.
Каждый раз, когда вы "входите с помощью" Facebook, Google, GitHub, Microsoft, Twitter, это приложение использует OAuth2 с scope.
В этом разделе вы увидите, как управлять аутентификацией и авторизацией с использованием того же OAuth2 с scope в вашем приложении **FastAPI**.
/// warning | Предупреждение
Это более или менее сложный раздел. Если вы только начинаете, вы можете пропустить его.
Вы не обязательно нуждаетесь в OAuth2 scope, и можете обрабатывать аутентификацию и авторизацию, как вам угодно.
Но OAuth2 с scope может быть прекрасно интегрирован в ваш API (с OpenAPI) и в ваши документы API.
Тем не менее, вы все равно должны применять эти scope, или любые другие требования к безопасности/авторизации, так как вам нужно, в вашем коде.
В многих случаях, OAuth2 с scope может быть избыточными.
Но если вы знаете, что они вам нужны, или вы заинтересовались, продолжайте чтение.
///
## OAuth2 scope и OpenAPI
Спецификация OAuth2 определяет "scope" как список строк, разделенных пробелами.
Содержимое каждой из этих строк может иметь любой формат, но не должно содержать пробелов.
Эти scope представляют собой "разрешения".
В OpenAPI (например, в документах API) вы можете определить "схемы безопасности".
Когда одна из этих схем безопасности использует OAuth2, вы также можете объявлять и использовать scope.
Каждый "scope" — это просто строка (без пробелов).
Обычно они используются для объявления определенных разрешений безопасности, например:
* `users:read` или `users:write` — это распространенные примеры.
* `instagram_basic` используется Facebook/Instagram.
* `https://www.googleapis.com/auth/drive` используется Google.
/// info | Информация
В OAuth2 "scope" — это просто строка, указывающая на конкретные требуемые разрешения.
Не имеет значения, есть ли в нем другие символы, такие как `:`, или если это URL.
Эти детали специфичны для реализации.
Для OAuth2 они просто строки.
///
## Общее представление
Сначала давайте быстро посмотрим на части, которые изменяются по сравнению с примерами в основном **Учебнике - Руководстве пользователя** для [OAuth2 с Паролем (и хешированием), Bearer с JWT токенами](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Теперь используя OAuth2 scope:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:125,129:135,140,156] *}
Теперь давайте рассмотрим эти изменения шаг за шагом.
## Схема безопасности OAuth2
Первое изменение состоит в том, что мы теперь объявляем схему безопасности OAuth2 с двумя доступными scope, `me` и `items`.
Параметр `scopes` принимает `dict`, где каждый scope указывается в качестве ключа, а описание — в качестве значения:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Поскольку мы теперь объявляем эти scope, они будут отображаться в документации API, когда вы входите/авторизируетесь.
И вы сможете выбрать, к каким scope вы хотите дать доступ: `me` и `items`.
Это тот же механизм, который используется, когда вы предоставляете разрешения при входе в Facebook, Google, GitHub и т.д:
<img src="/img/tutorial/security/image11.png">
## JWT токен с scope
Теперь модифицируйте токен *операции пути*, чтобы вернуть запрашиваемые scope.
Мы по-прежнему используем тот же `OAuth2PasswordRequestForm`. Он включает свойство `scopes` с `list` из `str`, в котором содержатся все scope, полученные в запросе.
И мы возвращаем scope как часть JWT токена.
/// danger | Опасность
Для упрощения, здесь мы просто добавляем полученные scope непосредственно в токен.
Но в вашем приложении, с целью безопасности, вы должны удостовериться, что добавляете только те scope, которые пользователь действительно может иметь, или те, которые вы заранее определили.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[156] *}
## Объявление scope в *операциях пути* и зависимостях
Теперь мы объявляем, что *операция пути* для `/users/me/items/` требует scope `items`.
Для этого мы импортируем и используем `Security` из `fastapi`.
Вы можете использовать `Security` для объявления зависимостей (так же как `Depends`), но `Security` также принимает параметр `scopes` со списком scope (строк).
В данном случае, мы передаем функцию зависимости `get_current_active_user` в `Security` (так же как мы бы делали это с `Depends`).
Но мы также передаем `list` с scope, в этом случае только с одним scope: `items` (хотя их может быть больше).
И функция зависимости `get_current_active_user` также может объявлять подзависимости, не только с `Depends`, но и с `Security`. Объявляя свою подзависимость (`get_current_user`), а также требования к scope.
В этом случае требуется scope `me` (могут требоваться и другие scope).
/// note | Примечание
Вам не обязательно нужно добавлять разные scope в разные места.
Мы делаем это здесь, чтобы продемонстрировать, как **FastAPI** обрабатывает scope, объявленные на разных уровнях.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,140,171] *}
/// info | Технические детали
`Security` на самом деле является подклассом `Depends`, и у него есть всего один дополнительный параметр, который мы рассмотрим позже.
Но используя `Security` вместо `Depends`, **FastAPI** будет знать, что он может объявить scope безопасности, использовать их внутри и документировать API с помощью OpenAPI.
Но когда вы импортируете `Query`, `Path`, `Depends`, `Security` и другие из `fastapi`, это на самом деле функции, которые возвращают специальные классы.
///
## Используйте `SecurityScopes`
Теперь обновите зависимость `get_current_user`.
Это та зависимость, которая используется выше.
Вот где мы используем ту же схему OAuth2, которую создали ранее, объявляя ее как зависимость: `oauth2_scheme`.
Поскольку эта функция зависимости не имеет собственных требований к scope, мы можем использовать `Depends` с `oauth2_scheme`, нам не нужно использовать `Security`, когда нам не нужно указывать scope безопасности.
Также мы объявляем специальный параметр типа `SecurityScopes`, импортированный из `fastapi.security`.
Этот класс `SecurityScopes` похож на `Request` (`Request` использовался для получения объекта запроса напрямую).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Используйте `scopes`
Параметр `security_scopes` будет типа `SecurityScopes`.
У него есть свойство `scopes` со списком, содержащим все scope, требуемые как сама зависимость, так и все зависимости, которые используют это как подзависимость. Это значит, все "зависимые"... это может звучать запутанно, но это объясняется снова позже.
Объект `security_scopes` (класса `SecurityScopes`) также предоставляет атрибут `scope_str` с одной строкой, содержащей эти scope, разделенные пробелами (мы собираемся использовать его).
Мы создаем `HTTPException`, который мы можем использовать (`raise`) позже в нескольких повторяющихся моментах.
В этом исключении мы включаем требуемые scope (если они есть) в виде строки, разделенной пробелами (используя `scope_str`). Мы помещаем эту строку, содержащую scope, в HTTP-заголовке `WWW-Authenticate` (это часть спецификации).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Проверьте `username` и форму данных
Мы проверяем, что получили `username`, и извлекаем scope.
Затем мы валидируем эти данные с помощью Pydantic-модели (обрабатывая исключение `ValidationError`), и если мы получаем ошибку при чтении JWT токена или валидации данных с Pydantic, мы вызываем `HTTPException`, который создали ранее.
Для этого мы обновляем Pydantic-модель `TokenData` с новым свойством `scopes`.
Валидируя данные с помощью Pydantic, мы можем убедиться, что у нас, например, точно есть `list` из `str` с scope и `str` с `username`.
Вместо, например, `dict`, или чего-то еще, так как это может сломать приложение в какой-то момент позже, создавая угрозу безопасности.
Мы также проверяем, что у нас есть пользователь с этим именем пользователя, и если нет, вызываем то же самое исключение, созданное ранее.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:128] *}
## Проверьте `scopes`
Теперь мы проверяем, все ли требуемые scope, этой зависимостью и всеми зависимыми (включая *операции пути*), включены в предоставленные токеном scope, в противном случае вызываем `HTTPException`.
Для этого мы используем `security_scopes.scopes`, который содержит `list` со всеми этими scope как `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[129:135] *}
## Дерево зависимостей и scope
Давайте еще раз рассмотрим это дерево зависимостей и scope.
Поскольку зависимость `get_current_active_user` имеет подзависимость на `get_current_user`, scope `"me"`, объявленный в `get_current_active_user`, будет включен в список требуемых scope в `security_scopes.scopes`, переданном в `get_current_user`.
Сама *операция пути* также объявляет scope, `"items"`, так что он также будет в списке `security_scopes.scopes`, передаваемом в `get_current_user`.
Вот как выглядит иерархия зависимостей и scope:
* У *операции пути* `read_own_items`:
* Требуемые scope `["items"]` с зависимостью:
* `get_current_active_user`:
* Функция зависимости `get_current_active_user` имеет:
* Требуемые scope `["me"]` с зависимостью:
* `get_current_user`:
* У функции зависимости `get_current_user`:
* Нет требуемых scope.
* Зависимость, использующая `oauth2_scheme`.
* Параметр `security_scopes` типа `SecurityScopes`:
* У этого параметра `security_scopes` есть свойство `scopes` со `list`, содержащим все scope, объявленные выше, так что:
* `security_scopes.scopes` будет содержать `["me", "items"]` для *операции пути* `read_own_items`.
* `security_scopes.scopes` будет содержать `["me"]` для *операции пути* `read_users_me`, потому что он объявлен в зависимости `get_current_active_user`.
* `security_scopes.scopes` будет содержать `[]` (ничего) для *операции пути* `read_system_status`, потому что там не объявлено никакое `Security` с `scopes`, и его зависимость `get_current_user` также не объявляет никакие `scopes`.
/// tip | Совет
Самое важное и "магическое" здесь то, что `get_current_user` будет иметь разный список `scopes` для проверки для каждой *операции пути*.
Все зависит от `scopes`, объявленных в каждой *операции пути* и каждой зависимости в дереве зависимостей для данной конкретной *операции пути*.
///
## Подробности о `SecurityScopes`
Вы можете использовать `SecurityScopes` в любой точке и в нескольких местах, он не должен быть на уровне "корневой" зависимости.
Он будет всегда иметь scope безопасности, объявленные в текущих зависимостях `Security` и всех зависимых для **конкретно этой** *операции пути* и **конкретно этого** дерева зависимостей.
Поскольку `SecurityScopes` будут иметь все scope, заявленные зависимыми, вы можете использовать его для проверки того, что токен имеет требуемые scope в центральной функции зависимости, а затем объявлять разные требования к scope в разных *операциях пути*.
Они будут проверяться независимо для каждой *операции пути*.
## Проверьте это
Если вы откроете документацию API, вы можете аутентифицироваться и указать, какие scope вы хотите авторизовать.
<img src="/img/tutorial/security/image11.png">
Если вы не выберете ни один scope, вы будете "аутентифицированы", но при попытке доступа к `/users/me/` или `/users/me/items/` вы получите ошибку, сообщающую, что у вас недостаточно разрешений. Вы все равно сможете получить доступ к `/status/`.
И если вы выберете scope `me`, но не scope `items`, вы сможете получить доступ к `/users/me/`, но не к `/users/me/items/`.
Это именно то, что произойдет с сторонним приложением, которое пытается получить доступ к одной из этих *операций пути* с токеном, предоставленным пользователем, в зависимости от того, сколько разрешений пользователь дал приложению.
## О сторонних интеграциях
В этом примере мы используем OAuth2 "парольный" поток.
Это уместно, когда мы входим в наше собственное приложение, вероятно, с нашим собственным фронтендом.
Потому что мы можем доверять ему получение `username` и `password`, так как мы его контролируем.
Но если вы строите приложение OAuth2, к которому будут подключаться другие (т.е. если вы создаете провайдер аутентификации, эквивалентный Facebook, Google, GitHub и т.д.), вы должны использовать один из других потоков.
Наиболее распространенным является поток implicit.
Наиболее безопасным является поток с кодом, но его сложнее реализовать, так как он требует больше шагов. Поскольку он более сложен, многие провайдеры в конечном итоге предлагают поток implicit.
/// note | Примечание
Часто каждый провайдер аутентификации называет свои потоки по-разному, чтобы сделать это частью своего бренда.
Но в конечном итоге они реализуют тот же стандарт OAuth2.
///
**FastAPI** включает утилиты для всех этих потоков аутентификации OAuth2 в `fastapi.security.oauth2`.
## `Security` в `dependencies` декоратора
Таким же образом, как вы можете определять `list` из `Depends` в параметре `dependencies` декоратора (как объяснено в [Dependencies в декораторах операций пути](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), вы также можете использовать `Security` с `scopes` там.

346
docs/ru/docs/advanced/settings.md

@ -0,0 +1,346 @@
# Настройки и переменные окружения
Во многих случаях вашим приложениям могут понадобиться внешние настройки или конфигурации, например, секретные ключи, учетные данные базы данных, учетные данные для почтовых сервисов и т.д.
Большинство этих настроек являются изменяемыми (могут изменяться), как, например, URL-ы баз данных. Многие из них могут быть чувствительными, как, например, секреты.
По этой причине их обычно предоставляют в виде переменных окружения, которые считываются приложением.
/// tip | Совет
Чтобы понять, что такое переменные окружения, можете прочитать [Переменные окружения](../environment-variables.md){.internal-link target=_blank}.
///
## Типы и валидация
Эти переменные окружения могут обрабатывать только текстовые строки, так как они являются внешними для Python и должны быть совместимы с другими программами и остальной системой (и даже с разными операционными системами, такими как Linux, Windows, macOS).
Это означает, что любое значение, считанное в Python из переменной окружения, будет типа `str`, и любое преобразование в другой тип или валидация должны осуществляться в коде.
## Pydantic `Settings`
К счастью, Pydantic предоставляет отличную утилиту для работы с этими настройками, поступающими из переменных окружения, используя <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic: Управление настройками</a>.
### Установка `pydantic-settings`
Сначала убедитесь, что вы создали ваше [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его и затем установили пакет `pydantic-settings`:
<div class="termy">
```console
$ pip install pydantic-settings
---> 100%
```
</div>
Этот пакет также включен, если вы устанавливаете все зависимости с:
<div class="termy">
```console
$ pip install "fastapi[all]"
---> 100%
```
</div>
/// info | Информация
В Pydantic v1 он поставлялся с основным пакетом. Теперь он распространяется как отдельный пакет, чтобы вы могли выбрать, устанавливать его или нет, если вам не нужна эта функциональность.
///
### Создание объекта `Settings`
Импортируйте `BaseSettings` из Pydantic и создайте подкласс, как это делается с Pydantic-моделью.
Так же, как и с Pydantic-моделями, вы объявляете атрибуты класса с аннотациями типов и, возможно, значениями по умолчанию.
Вы можете использовать все те же функции валидации и инструменты, которые используете в Pydantic-моделях, например, разные типы данных и дополнительные валидации с `Field()`.
//// tab | Pydantic v2
{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
////
//// tab | Pydantic v1
/// info | Информация
В Pydantic v1 вы импортировали бы `BaseSettings` непосредственно из `pydantic` вместо `pydantic_settings`.
///
{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
////
/// tip | Совет
Если вам нужно что-то быстрое для копирования и вставки, не используйте этот пример, используйте последний ниже.
///
Когда вы создаете экземпляр этого класса `Settings` (в данном случае, в объекте `settings`), Pydantic будет считывать переменные окружения без учета регистра, так что переменная в верхнем регистре `APP_NAME` все еще будет считана для атрибута `app_name`.
Затем он выполнит преобразование и валидацию данных. Таким образом, когда вы используете этот объект `settings`, у вас будут данные тех типов, которые вы объявили (например, `items_per_user` будет типа `int`).
### Использование `settings`
Затем вы можете использовать новый объект `settings` в вашем приложении:
{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
### Запуск сервера
Далее вы можете запустить сервер, передавая конфигурации как переменные окружения, например, вы можете установить `ADMIN_EMAIL` и `APP_NAME` следующим образом:
<div class="termy">
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
/// tip | Совет
Чтобы установить несколько переменных окружения для одной команды, просто разделите их пробелами и укажите перед командой.
///
Тогда настройка `admin_email` будет установлена в `"deadpool@example.com"`.
Настройка `app_name` будет иметь значение `"ChimichangApp"`.
А `items_per_user` сохранит свое значение по умолчанию — `50`.
## Настройки в другом модуле
Вы можете поместить эти настройки в другой файл-модуль, как вы видели в [Более крупные приложения - Несколько файлов](../tutorial/bigger-applications.md){.internal-link target=_blank}.
Например, у вас может быть файл `config.py` с:
{* ../../docs_src/settings/app01/config.py *}
И затем использовать его в файле `main.py`:
{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
/// tip | Совет
Вам также нужен будет файл `__init__.py`, как вы видели в [Более крупные приложения - Несколько файлов](../tutorial/bigger-applications.md){.internal-link target=_blank}.
///
## Настройки в зависимости
В некоторых случаях может быть полезно предоставлять настройки из зависимости, вместо использования глобального объекта `settings`, который используется везде.
Это может быть особенно полезно во время тестирования, так как очень легко переопределить зависимость вашими собственными настройками.
### Файл конфигурации
Исходя из предыдущего примера, ваш файл `config.py` может выглядеть так:
{* ../../docs_src/settings/app02/config.py hl[10] *}
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`.
### Основной файл приложения
Теперь мы создаем зависимость, которая возвращает новый `config.Settings()`.
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
/// tip | Совет
Мы обсудим `@lru_cache` чуть позже.
На данный момент можно считать, что `get_settings()` — это обычная функция.
///
А затем мы можем потребовать его в *функции-обработчике пути* как зависимость и использовать ее в любом месте, где это необходимо.
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
### Настройки и тестирование
Затем вам будет очень легко предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `get_settings`:
{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
В переопределении зависимости мы устанавливаем новое значение для `admin_email`, создавая новый объект `Settings`, а затем возвращаем этот новый объект.
Затем мы можем протестировать, что он используется.
## Чтение файла `.env`
Если у вас есть множество настроек, которые могут часто меняться, возможно, в разных окружениях, может быть полезно поместить их в файл и затем считывать их из него, как если бы они были переменными окружения.
Эта практика достаточно распространена, чтобы у нее было название, эти переменные окружения обычно помещаются в файл `.env`, и этот файл называется "dotenv".
/// tip | Совет
Файл, начинающийся с точки (`.`), является скрытым файлом в системах, похожих на Unix, таких как Linux и macOS.
Но файл dotenv не обязательно должен иметь именно такое имя.
///
Pydantic поддерживает считывание из таких файлов с использованием внешней библиотеки. Вы можете узнать больше на <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Поддержка Dotenv (.env)</a>.
/// tip | Совет
Для этого необходимо выполнить `pip install python-dotenv`.
///
### Файл `.env`
Вы могли бы иметь файл `.env` с:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Чтение настроек из `.env`
А затем обновить ваш `config.py` следующим образом:
//// tab | Pydantic v2
{* ../../docs_src/settings/app03_an/config.py hl[9] *}
/// tip | Совет
Атрибут `model_config` используется только для настройки Pydantic. Вы можете прочитать больше на <a href="https://docs.pydantic.dev/latest/concepts/config/" class="external-link" target="_blank">Pydantic: Концепции: Конфигурация</a>.
///
////
//// tab | Pydantic v1
{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *}
/// tip | Совет
Класс `Config` используется только для настройки Pydantic. Вы можете прочитать больше на <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Конфигурация Pydantic модели</a>.
///
////
/// info | Информация
В версии Pydantic 1 конфигурация осуществлялась во внутреннем классе `Config`, в версии Pydantic 2 это делается в атрибуте `model_config`. Этот атрибут принимает `dict`, и чтобы получить автозавершение и встроенные ошибки, вы можете импортировать и использовать `SettingsConfigDict` для определения этого `dict`.
///
Здесь мы определяем конфигурацию `env_file` внутри вашего класса Pydantic `Settings` и устанавливаем значение для имени файла с файлом dotenv, который мы хотим использовать.
### Создание `Settings` только один раз с `lru_cache`
Чтение файла с диска обычно является дорогой (медленной) операцией, поэтому, вероятно, вы захотите делать это только один раз, а затем использовать тот же объект настроек, вместо того чтобы считывать его для каждого запроса.
Но каждый раз, когда мы выполняем:
```Python
Settings()
```
новый объект `Settings` будет создан, и при создании он снова считает файл `.env`.
Если бы функция зависимости выглядела так:
```Python
def get_settings():
return Settings()
```
мы бы создавали этот объект для каждого запроса, и мы бы считывали файл `.env` для каждого запроса. ⚠️
Но так как мы используем декоратор `@lru_cache` сверху, объект `Settings` будет создан только один раз, при первом вызове. ✔️
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
Затем для всех последующих вызовов `get_settings()` в зависимостях для следующих запросов, вместо выполнения внутреннего кода функции `get_settings()` и создания нового объекта `Settings`, будет возвращен тот же объект, который был возвращен при первом вызове, снова и снова.
#### Технические детали `lru_cache`
`@lru_cache` модифицирует функцию, которую он декорирует, чтобы возвращать то же самое значение, которое было возвращено в первый раз, вместо того чтобы вычислять его снова, выполняя код функции каждый раз.
Таким образом, функция ниже будет выполнена один раз для каждой комбинации аргументов. А затем возвращаемые значения для каждой из этих комбинаций аргументов будут использоваться снова и снова, когда функция вызывается с точно такой же комбинацией аргументов.
Например, если у вас есть функция:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
ваша программа может выполняться так:
```mermaid
sequenceDiagram
participant code as Код
participant function as say_hi()
participant execute as Выполнить код функции
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: выполнить код функции
execute ->> code: вернуть результат
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: вернуть сохраненный результат
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: выполнить код функции
execute ->> code: вернуть результат
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: выполнить код функции
execute ->> code: вернуть результат
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: вернуть сохраненный результат
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: вернуть сохраненный результат
end
```
В случае нашей зависимости `get_settings()`, функция даже не принимает никаких аргументов, поэтому всегда возвращает одно и то же значение.
Таким образом, она ведет себя почти как глобальная переменная. Но поскольку используется функция зависимости, мы можем легко переопределить ее для тестирования.
`@lru_cache` является частью `functools`, который является частью стандартной библиотеки Python, вы можете прочитать больше об этом в <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">документации Python по `@lru_cache`</a>.
## Резюме
Вы можете использовать Pydantic Settings для управления настройками или конфигурациями вашего приложения, используя все возможности Pydantic-моделей.
* Используя зависимость, вы можете упростить тестирование.
* Вы можете использовать `.env` файлы с ним.
* Использование `@lru_cache` позволяет избежать повторного чтения файла dotenv для каждого запроса, при этом позволяя его переопределять во время тестирования.

67
docs/ru/docs/advanced/sub-applications.md

@ -0,0 +1,67 @@
# Подприложения - Mounts
Если вам нужно иметь два независимых FastAPI приложения, каждое с собственным, независимым OpenAPI и собственными интерфейсами документации, вы можете создать основное приложение и "смонтировать" одно (или более) подприложение(я).
## Монтирование **FastAPI** приложения
"Монтирование" означает добавление полностью "независимого" приложения в определенный path, которое затем будет обрабатывать все под этим path, с _операциями пути_, объявленными в этом подприложении.
### Высокоуровневое приложение
Сначала создайте основное, высшего уровня, **FastAPI** приложение и его *операции пути*:
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
### Подприложение
Затем создайте ваше подприложение и его *операции пути*.
Это подприложение является стандартным FastAPI приложением, но именно оно будет "смонтировано":
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
### Монтирование подприложения
В вашем высшего уровня приложении, `app`, смонтируйте подприложение, `subapi`.
В этом случае оно будет смонтировано на path `/subapi`:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
### Проверьте автоматическую документацию API
Теперь запустите команду `fastapi` с вашим файлом:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
И откройте документацию по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Вы увидите автоматическую документацию API для основного приложения, включающую лишь его собственные _операции пути_:
<img src="/img/tutorial/sub-applications/image01.png">
Затем откройте документацию для подприложения по адресу <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>.
Вы увидите автоматическую документацию API для подприложения, включающую только его собственные _операции пути_, все с правильным префиксом под-path `/subapi`:
<img src="/img/tutorial/sub-applications/image02.png">
Если вы попробуете взаимодействовать с любым из двух пользовательских интерфейсов, они будут работать правильно, потому что браузер сможет обратиться к каждому конкретному приложению или подприложению.
### Технические детали: `root_path`
Когда вы монтируете подприложение, как описано выше, FastAPI позаботится о передаче пути монтирования для подприложения, используя механизм из спецификации ASGI, называемый `root_path`.
Таким образом, подприложение будет знать, что необходимо использовать этот префикс пути для UI документации.
И подприложение также может иметь свои собственные смонтированные подприложения, и всё будет работать корректно, потому что FastAPI обрабатывает все эти `root_path`s автоматически.
Вы узнаете больше о `root_path` и том, как его использовать явно в разделе про [Работу за прокси-сервером](behind-a-proxy.md){.internal-link target=_blank}.

126
docs/ru/docs/advanced/templates.md

@ -0,0 +1,126 @@
# Шаблоны
Вы можете использовать любой шаблонизатор, который захотите, с **FastAPI**.
Популярным выбором является Jinja2, который также используется Flask и другими инструментами.
Существуют утилиты для его простой настройки, которые вы можете использовать непосредственно в своем приложении **FastAPI** (предоставлено Starlette).
## Установка зависимостей
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его и установили `jinja2`:
<div class="termy">
```console
$ pip install jinja2
---> 100%
```
</div>
## Использование `Jinja2Templates`
* Импортируйте `Jinja2Templates`.
* Создайте объект `templates`, который вы сможете повторно использовать позднее.
* Объявите параметр `Request` в *операции пути*, которая будет возвращать шаблон.
* Используйте созданные вами `templates` для рендеринга и возврата `TemplateResponse`, передайте имя шаблона, объект запроса и словарь "context" с парами "ключ-значение", которые будут использоваться внутри шаблона Jinja2.
{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
/// note | Примечание
До FastAPI 0.108.0, Starlette 0.29.0, параметр `name` был первым.
Также, до этого, в предыдущих версиях объект `request` передавался как часть пар ключ-значение в контексте для Jinja2.
///
/// tip | Совет
Объявляя `response_class=HTMLResponse`, интерфейс документации сможет определить, что ответ будет в формате HTML.
///
/// note | Технические детали
Вы также можете использовать `from starlette.templating import Jinja2Templates`.
**FastAPI** предоставляет тот же `starlette.templating` как `fastapi.templating` просто для вашего удобства, разработчик. Но большинство доступных ответов поступают непосредственно от Starlette. То же самое касается `Request` и `StaticFiles`.
///
## Написание шаблонов
Затем вы можете написать шаблон в `templates/item.html`, например:
```jinja hl_lines="7"
{!../../docs_src/templates/templates/item.html!}
```
### Значения контекста шаблона
В HTML, который содержит:
{% raw %}
```jinja
Item ID: {{ id }}
```
{% endraw %}
...будет показан `id`, взятый из "context" `dict`, который вы передали:
```Python
{"id": id}
```
Например, с `id`, равным `42`, это будет рендериться как:
```html
Item ID: 42
```
### Аргументы `url_for` в шаблоне
Вы также можете использовать `url_for()` внутри шаблона; он принимает в качестве аргументов те же аргументы, которые использовались бы в вашей *функции-обработчике пути*.
Таким образом, раздел с:
{% raw %}
```jinja
<a href="{{ url_for('read_item', id=id) }}">
```
{% endraw %}
...сгенерирует ссылку на тот же URL, который был бы обработан *функцией-обработчиком пути* `read_item(id=id)`.
Например, с `id`, равным `42`, это будет рендериться как:
```html
<a href="/items/42">
```
## Шаблоны и статические файлы
Вы также можете использовать `url_for()` внутри шаблона и использовать его, например, с `StaticFiles`, которые вы смонтировали с `name="static"`.
```jinja hl_lines="4"
{!../../docs_src/templates/templates/item.html!}
```
В этом примере это будет ссылка на CSS-файл по пути `static/styles.css` с:
```CSS hl_lines="4"
{!../../docs_src/templates/static/styles.css!}
```
И поскольку вы используете `StaticFiles`, этот CSS-файл будет автоматически обслуживаться вашим приложением **FastAPI** по URL `/static/styles.css`.
## Дополнительные сведения
Для получения дополнительной информации, включая тестирование шаблонов, смотрите <a href="https://www.starlette.io/templates/" class="external-link" target="_blank">документацию Starlette по шаблонам</a>.

53
docs/ru/docs/advanced/testing-dependencies.md

@ -0,0 +1,53 @@
# Тестирование зависимостей с использованием переопределений
## Переопределение зависимостей в процессе тестирования
Существуют сценарии, когда вам может понадобиться переопределить зависимость во время тестирования.
Вы не хотите, чтобы оригинальная зависимость выполнялась (также как и любые её подзависимости).
Вместо этого, вы хотите предоставить другую зависимость, которая будет использоваться только во время тестов (возможно, только для некоторых конкретных тестов), и будет предоставлять значение, которое можно использовать там, где использовалось значение оригинальной зависимости.
### Примеры использования: внешний сервис
Примером может служить ситуация, когда у вас есть внешний провайдер аутентификации, которого вам необходимо вызывать.
Вы отправляете ему токен, и он возвращает аутентифицированного пользователя.
Этот провайдер может брать плату за каждый запрос, и вызов его может занять больше времени, чем если бы вы использовали фиксированного пользователя-мока для тестов.
Вероятно, вы захотите протестировать внешнего провайдера один раз, но не обязательно вызывать его для каждого теста, который вы запускаете.
В этом случае вы можете переопределить зависимость, которая вызывает этого провайдера, и использовать пользовательскую зависимость, которая возвращает мока-пользователя, только для ваших тестов.
### Использование атрибута `app.dependency_overrides`
В таких случаях ваше приложение **FastAPI** имеет атрибут `app.dependency_overrides`, который является простым `dict`.
Чтобы переопределить зависимость для тестирования, вы указываете оригинальную зависимость (функцию) в качестве ключа, и ваше переопределение зависимости (другую функцию) в качестве значения.
Затем **FastAPI** будет вызывать это переопределение вместо оригинальной зависимости.
{* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *}
/// tip | Совет
Вы можете установить переопределение зависимости для зависимости, используемой в любом месте вашего приложения **FastAPI**.
Оригинальная зависимость может использоваться в *функции-обработчике пути*, *декораторе операции пути* (если вы не используете возвращаемое значение), вызове `.include_router()`, и т. д.
FastAPI все равно сможет ее переопределить.
///
Затем вы можете сбросить ваши переопределения (удалить их), установив `app.dependency_overrides` в пустой `dict`:
```Python
app.dependency_overrides = {}
```
/// tip | Совет
Если вы хотите переопределить зависимость только в некоторых тестах, вы можете установить переопределение в начале теста (внутри тестовой функции) и сбросить его в конце (в конце тестовой функции).
///

5
docs/ru/docs/advanced/testing-events.md

@ -0,0 +1,5 @@
# Тестирование событий: startup - shutdown
Когда вам нужно, чтобы ваши обработчики событий (`startup` и `shutdown`) запускались в ваших тестах, вы можете использовать `TestClient` с оператором `with`:
{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}

13
docs/ru/docs/advanced/testing-websockets.md

@ -0,0 +1,13 @@
# Тестирование WebSockets
Вы можете использовать тот же `TestClient` для тестирования WebSockets.
Для этого вы используете `TestClient` в операторе `with`, подключаясь к WebSocket:
{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
/// note | Заметка
Для получения дополнительных сведений обратитесь к документации Starlette для <a href="https://www.starlette.io/testclient/#testing-websocket-sessions" class="external-link" target="_blank">тестирования WebSockets</a>.
///

56
docs/ru/docs/advanced/using-request-directly.md

@ -0,0 +1,56 @@
# Прямое использование объекта Request
До сих пор вы объявляли части HTTP-запроса, которые вам нужны, с указанием их типов.
Получение данных из:
* path как параметров.
* HTTP-заголовков.
* cookies.
* и т.д.
И благодаря этому, **FastAPI** валидирует эти данные, выполняет их преобразование и автоматически генерирует документацию для вашего API.
Но есть ситуации, когда может понадобиться доступ к объекту `Request` напрямую.
## Подробности об объекте `Request`
Так как **FastAPI** на самом деле построен на **Starlette**, с уровнем дополнительных инструментов сверху, вы можете использовать объект <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> из Starlette напрямую, когда это необходимо.
Это также означает, что если вы получаете данные из объекта `Request` напрямую (например, читаете тело запроса), они не будут валидироваться, преобразовываться или документироваться (с использованием OpenAPI для автоматического пользовательского интерфейса API) в FastAPI.
Хотя любой другой параметр, объявленный обычно (например, тело запроса с Pydantic моделью), все равно будет валидироваться, преобразовываться, аннотироваться и т.д.
Но есть определенные случаи, когда полезно получить объект `Request`.
## Использование объекта `Request` напрямую
Представьте, что вы хотите получить IP-адрес/хост клиента внутри вашей *функции-обработчика пути*.
Для этого вам нужно будет получить доступ к запросу напрямую.
{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
Объявляя параметр *функции-обработчика пути* с типом `Request`, **FastAPI** поймет, что необходимо передать объект `Request` в этот параметр.
/// tip | Подсказка
Обратите внимание, что в этом случае мы объявляем path параметр вместе с параметром запроса.
Таким образом, path параметр будет извлечен, провалидирован, преобразован в указанный тип и аннотирован с помощью OpenAPI.
Точно так же вы можете объявить любой другой параметр как обычно, и дополнительно получить также объект `Request`.
///
## Документация по `Request`
Вы можете прочитать больше деталей о <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">объекте `Request` на официальном сайте документации Starlette</a>.
/// note | Технические Подробности
Вы также можете использовать `from starlette.requests import Request`.
**FastAPI** предоставляет это напрямую просто для удобства для вас, разработчика. Но эта функция приходит прямо из Starlette.
///

35
docs/ru/docs/advanced/wsgi.md

@ -0,0 +1,35 @@
# Включение WSGI - Flask, Django и другие
Вы можете монтировать WSGI-приложения, как вы видели в разделе [Дополнительные приложения - Монтирования](sub-applications.md){.internal-link target=_blank}, [За прокси](behind-a-proxy.md){.internal-link target=_blank}.
Для этого вы можете использовать `WSGIMiddleware`, чтобы обернуть ваше WSGI-приложение, например, Flask, Django и т.д.
## Использование `WSGIMiddleware`
Вам нужно импортировать `WSGIMiddleware`.
Затем обернуть WSGI (например, Flask) приложение с помощью middleware (промежуточного слоя).
И затем смонтировать это под path.
{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
## Проверьте это
Теперь каждый HTTP-запрос по path `/v1/` будет обработан приложением Flask.
А остальные запросы будут обработаны **FastAPI**.
Если вы запустите его и перейдете по ссылке <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a>, вы увидите ответ от Flask:
```txt
Hello, World from Flask!
```
И если вы перейдете по ссылке <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a>, вы увидите ответ от FastAPI:
```JSON
{
"message": "Hello World"
}
```

17
docs/ru/docs/deployment/cloud.md

@ -0,0 +1,17 @@
# Развёртывание FastAPI на облачных провайдерах
Вы можете использовать практически **любого облачного провайдера** для развертывания вашего приложения FastAPI.
В большинстве случаев у основных облачных провайдеров есть руководства по развертыванию FastAPI с их помощью.
## Облачные провайдеры - спонсоры
Некоторые облачные провайдеры ✨ [**спонсируют FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, это обеспечивает продолжительное и здоровое **развитие** FastAPI и его **экосистемы**.
Это демонстрирует их настоящую приверженность FastAPI и его **сообществу** (вам), так как они не только хотят предоставить вам **хороший сервис**, но и хотят убедиться, что у вас есть **хороший и здоровый фреймворк**, FastAPI. 🙇
Возможно, вам захочется попробовать их услуги и следовать их руководствам:
* <a href="https://docs.platform.sh/languages/python.html?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" class="external-link" target="_blank">Platform.sh</a>
* <a href="https://docs.porter.run/language-specific-guides/fastapi" class="external-link" target="_blank">Porter</a>
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>

137
docs/ru/docs/deployment/server-workers.md

@ -0,0 +1,137 @@
# Воркеры сервера - Uvicorn с воркерами
Давайте снова рассмотрим концепции деплоя:
* Безопасность - HTTPS
* Запуск при старте
* Перезапуски
* **Репликация (количество рабочих процессов)**
* Память
* Подготовительные шаги перед запуском
До этого момента, с помощью всех туториалов в документации, вы, вероятно, запускали **серверную программу**, например, используя команду `fastapi`, которая запускает Uvicorn, выполняя **единственный процесс**.
При деплое приложений вы, вероятно, захотите иметь некоторую **репликацию процессов**, чтобы воспользоваться преимуществами **многих ядер** и иметь возможность обрабатывать больше запросов.
Как вы видели в предыдущей главе о [Концепции деплоя](concepts.md){.internal-link target=_blank}, существует множество стратегий, которые вы можете использовать.
Здесь я покажу вам, как использовать **Uvicorn** с **воркер-процессами**, используя команду `fastapi` или непосредственно команду `uvicorn`.
/// info | Информация
Если вы используете контейнеры, например, с Docker или Kubernetes, я расскажу вам больше об этом в следующей главе: [FastAPI в контейнерах - Docker](docker.md){.internal-link target=_blank}.
В частности, работая с **Kubernetes**, вы, вероятно, **не** захотите использовать воркеры, а вместо этого запускать **один процесс Uvicorn в контейнере**, но об этом я расскажу позже в той главе.
///
## Несколько воркеров
Вы можете запустить несколько воркеров, используя опцию командной строки `--workers`:
//// tab | `fastapi`
Если вы используете команду `fastapi`:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Запуск продакшн-сервера 🚀
Поиск структуры файлов пакета в директориях с
<font color="#3465A4">__init__.py</font>
Импорт из <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Импортирование объекта приложения FastAPI из модуля со следующим кодом:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Использование строки импорта: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Сервер стартовал на <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Документация по адресу <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Логи:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn работает на <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Нажмите CTRL+C для выхода<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Стартовал родительский процесс <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Стартовал серверный процесс <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Стартовал серверный процесс <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Стартовал серверный процесс <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Стартовал серверный процесс <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Ожидание запуска приложения.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Ожидание запуска приложения.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Ожидание запуска приложения.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Ожидание запуска приложения.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Запуск приложения завершён.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Запуск приложения завершён.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Запуск приложения завершён.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Запуск приложения завершён.
```
</div>
////
//// tab | `uvicorn`
Если вы предпочитаете использовать команду `uvicorn` напрямую:
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
<font color="#A6E22E">INFO</font>: Uvicorn работает на <b>http://0.0.0.0:8080</b> (Нажмите CTRL+C для выхода)
<font color="#A6E22E">INFO</font>: Стартовал родительский процесс [<font color="#A1EFE4"><b>27365</b></font>]
<font color="#A6E22E">INFO</font>: Стартовал серверный процесс [<font color="#A1EFE4">27368</font>]
<font color="#A6E22E">INFO</font>: Ожидание запуска приложения.
<font color="#A6E22E">INFO</font>: Запуск приложения завершён.
<font color="#A6E22E">INFO</font>: Стартовал серверный процесс [<font color="#A1EFE4">27369</font>]
<font color="#A6E22E">INFO</font>: Ожидание запуска приложения.
<font color="#A6E22E">INFO</font>: Запуск приложения завершён.
<font color="#A6E22E">INFO</font>: Стартовал серверный процесс [<font color="#A1EFE4">27370</font>]
<font color="#A6E22E">INFO</font>: Ожидание запуска приложения.
<font color="#A6E22E">INFO</font>: Запуск приложения завершён.
<font color="#A6E22E">INFO</font>: Стартовал серверный процесс [<font color="#A1EFE4">27367</font>]
<font color="#A6E22E">INFO</font>: Ожидание запуска приложения.
<font color="#A6E22E">INFO</font>: Запуск приложения завершён.
```
</div>
////
Единственная новая опция здесь - `--workers`, указывающая Uvicorn запустить 4 воркер-процесса.
Вы также можете увидеть, что отображается **PID** каждого процесса: `27365` для родительского процесса (это **менеджер процессов**) и один для каждого воркер-процесса: `27368`, `27369`, `27370` и `27367`.
## Концепции деплоя
Здесь вы увидели, как использовать несколько **воркеров** для **параллелизации** выполнения приложения, использовать преимущества **многих ядер** процессора и иметь возможность обрабатывать **больше запросов**.
Из списка концепций деплоя, приведённого выше, использование воркеров в основном поможет с **репликацией**, и немного - с **перезапусками**, но вам по-прежнему необходимо позаботиться о следующих вещах:
* **Безопасность - HTTPS**
* **Запуск при старте**
* ***Перезапуски***
* Репликация (количество рабочих процессов)
* **Память**
* **Подготовительные шаги перед запуском**
## Контейнеры и Docker
В следующей главе о [FastAPI в контейнерах - Docker](docker.md){.internal-link target=_blank} я объясню некоторые стратегии, которые вы можете использовать для работы с другими **концепциями деплоя**.
Я покажу вам, как **создать собственный образ с нуля** для запуска одного процесса Uvicorn. Это простой процесс и, вероятно, то, что вы захотите сделать при использовании системы управления распределёнными контейнерами, такой как **Kubernetes**.
## Итог
Вы можете использовать несколько воркер-процессов с опцией `--workers` в CLI с командами `fastapi` или `uvicorn`, чтобы воспользоваться преимуществами **многоядерных процессоров**, для выполнения **нескольких процессов параллельно**.
Вы можете использовать эти инструменты и идеи, если настраиваете **собственную систему деплоя**, одновременно заботясь о других концепциях деплоя самостоятельно.
Изучите следующую главу, чтобы узнать о **FastAPI** с контейнерами (например, Docker и Kubernetes). Вы увидите, что эти инструменты также имеют простые способы решения других **концепций деплоя**. ✨

56
docs/ru/docs/how-to/conditional-openapi.md

@ -0,0 +1,56 @@
# Условный OpenAPI
Если потребуется, вы можете использовать настройки и переменные окружения, чтобы условно настраивать OpenAPI в зависимости от окружения, и даже полностью отключать его.
## О безопасности, API и документации
Скрытие пользовательских интерфейсов вашей документации в продакшне *не должно* быть способом защиты вашего API.
Это не добавляет никакой дополнительной безопасности вашему API, *операции пути* по-прежнему будут доступны там, где они есть.
Если в вашем коде есть уязвимость, она всё равно будет существовать.
Скрытие документации лишь затрудняет понимание того, как взаимодействовать с вашим API, и может усложнить его отладку в продакшне. Это может рассматриваться просто как форма <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">безопасности через неясность</a>.
Если вы хотите защитить ваш API, есть несколько более эффективных подходов:
* Убедитесь, что у вас есть хорошо определённые Pydantic-модели для тел запросов и ответов.
* Настройте необходимые разрешения и роли с использованием зависимостей.
* Никогда не храните пароли в открытом виде, только хеши паролей.
* Реализуйте и используйте известные криптографические инструменты, такие как Passlib и JWT токены и т. д.
* Добавьте более детальный контроль разрешений с помощью OAuth2 областей, где это необходимо.
* ...и так далее.
Тем не менее, может быть очень специфический случай, когда вам действительно нужно отключить документацию API для какого-то окружения (например, для продакшна) или в зависимости от настроек из переменных окружения.
## Условный OpenAPI из настроек и переменных окружения
Вы можете легко использовать те же настройки Pydantic для конфигурации вашего сгенерированного OpenAPI и интерфейсов документации.
Например:
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
Здесь мы объявляем настройку `openapi_url` с тем же значением по умолчанию `"/openapi.json"`.
Затем мы используем его при создании приложения `FastAPI`.
Затем вы можете отключить OpenAPI (включая документацию UI) путем установки значения переменной окружения `OPENAPI_URL` в пустую строку, так:
<div class="termy">
```console
$ OPENAPI_URL= uvicorn main:app
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Затем, если вы перейдете по URL-адресам `/openapi.json`, `/docs` или `/redoc`, вы получите ошибку `404 Not Found`, например:
```JSON
{
"detail": "Not Found"
}
```

70
docs/ru/docs/how-to/configure-swagger-ui.md

@ -0,0 +1,70 @@
# Настройка Swagger UI
Вы можете настроить некоторые дополнительные <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">параметры Swagger UI</a>.
Чтобы их настроить, передайте аргумент `swagger_ui_parameters` при создании объекта приложения `FastAPI()` или в функцию `get_swagger_ui_html()`.
`swagger_ui_parameters` принимает словарь с конфигурациями, передаваемыми непосредственно в Swagger UI.
FastAPI преобразует конфигурации в **JSON**, чтобы они были совместимы с JavaScript, так как это нужно для Swagger UI.
## Отключение подсветки синтаксиса
Например, вы можете отключить подсветку синтаксиса в Swagger UI.
Без изменения настроек, подсветка синтаксиса включена по умолчанию:
<img src="/img/tutorial/extending-openapi/image02.png">
Но вы можете отключить её, установив `syntaxHighlight` в `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
...и тогда Swagger UI больше не будет отображать подсветку синтаксиса:
<img src="/img/tutorial/extending-openapi/image03.png">
## Изменение темы
Таким же образом вы можете установить тему подсветки синтаксиса с помощью ключа `"syntaxHighlight.theme"` (обратите внимание, что в нем есть точка):
{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
Эта конфигурация изменит цветовую тему подсветки синтаксиса:
<img src="/img/tutorial/extending-openapi/image04.png">
## Изменение параметров Swagger UI по умолчанию
FastAPI включает некоторые параметры конфигурации по умолчанию, подходящие для большинства случаев использования.
Эти конфигурации по умолчанию включают:
{* ../../fastapi/openapi/docs.py ln[8:23] hl[17:23] *}
Вы можете переопределить любой из них, установив другое значение в аргументе `swagger_ui_parameters`.
Например, чтобы отключить `deepLinking`, вы можете передать эти настройки в `swagger_ui_parameters`:
{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
## Другие параметры Swagger UI
Чтобы узнать о всех возможных конфигурациях, которые вы можете использовать, прочитайте официальную <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">документацию по параметрам Swagger UI</a>.
## Настройки только для JavaScript
Swagger UI также позволяет использовать другие конфигурации, представляющие собой **только для JavaScript** объекты (например, функции JavaScript).
FastAPI также включает эти настройки `presets`, предназначенные только для JavaScript:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Это объекты **JavaScript**, а не строки, поэтому вы не можете передавать их напрямую из кода Python.
Если вам нужно использовать такие настройки, предназначенные только для JavaScript, вы можете использовать один из методов выше. Переопределите все *операции пути* Swagger UI и вручную напишите любой нужный JavaScript.

185
docs/ru/docs/how-to/custom-docs-ui-assets.md

@ -0,0 +1,185 @@
# Статические ресурсы пользовательского интерфейса для документации (самостоятельный хостинг)
Документация API использует **Swagger UI** и **ReDoc**, и для каждой из них нужны определенные JavaScript и CSS файлы.
По умолчанию эти файлы обслуживаются с <abbr title="Content Delivery Network: сервис, обычно состоящий из нескольких серверов, который предоставляет статические файлы, такие как JavaScript и CSS. Обычно используется для обслуживания этих файлов с сервера, находящегося ближе к клиенту, улучшая производительность.">CDN</abbr>.
Но можно настроить их по-своему, задать определенный CDN или обслуживать файлы самостоятельно.
## Пользовательский CDN для JavaScript и CSS
Допустим, вы хотите использовать другой <abbr title="Content Delivery Network">CDN</abbr>, например, `https://unpkg.com/`.
Это может быть полезно, если, например, вы живете в стране, где ограничивают некоторые URL-адреса.
### Отключите автоматическую документацию
Первый шаг — отключить автоматическую документацию, так как по умолчанию она использует стандартный CDN.
Чтобы отключить их, установите для их URL-адресов значение `None` при создании приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
### Включите пользовательскую документацию
Теперь вы можете создать *операции пути* для пользовательской документации.
Вы можете повторно использовать внутренние функции FastAPI для создания HTML-страниц для документации и передать им необходимые аргументы:
* `openapi_url`: URL-адрес, где HTML-страница документации может получить OpenAPI-схему для вашего API. Вы можете использовать здесь атрибут `app.openapi_url`.
* `title`: заголовок вашего API.
* `oauth2_redirect_url`: здесь вы можете использовать `app.swagger_ui_oauth2_redirect_url`, чтобы воспользоваться значением по умолчанию.
* `swagger_js_url`: URL-адрес, по которому HTML для ваших документов Swagger UI может получить **JavaScript** файл. Это пользовательский URL-адрес CDN.
* `swagger_css_url`: URL-адрес, по которому HTML для ваших документов Swagger UI может получить **CSS** файл. Это пользовательский URL-адрес CDN.
И аналогично для ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Совет
*Операция пути* для `swagger_ui_redirect` — это вспомогательная функция, когда вы используете OAuth2.
Если вы интегрируете свой API с провайдером OAuth2, вы сможете пройти аутентификацию и вернуться к документации API с полученными учетными данными. И взаимодействовать с ней, используя настоящую аутентификацию OAuth2.
Swagger UI справится с этим закулисно для вас, но ему нужен этот вспомогательный "редирект".
///
### Создайте *операцию пути* для тестирования
Теперь, чтобы убедиться, что всё работает, создайте *операцию пути*:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
### Проверьте это
Теперь вы должны иметь возможность перейти к вашей документации по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> и перезагрузить страницу. Она будет загружать эти ресурсы с нового CDN.
## Самостоятельный хостинг JavaScript и CSS для документации
Самостоятельный хостинг JavaScript и CSS может быть полезен, если, например, вам нужно, чтобы ваше приложение продолжало работать даже в офлайн-режиме, без открытого доступа к Интернету, или в локальной сети.
Здесь вы увидите, как можно обслуживать эти файлы самостоятельно, в том же приложении FastAPI, и настроить документацию для их использования.
### Структура файлов проекта
Допустим, структура файлов вашего проекта выглядит так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Теперь создайте каталог для хранения этих статических файлов.
Теперь ваша новая структура файлов может выглядеть так:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static/
```
### Загрузите файлы
Загрузите необходимые статические файлы для документации и разместите их в каталоге `static/`.
Вы можете щелкнуть правой кнопкой мыши по каждой ссылке и выбрать опцию, аналогичную `Сохранить ссылку как...`.
**Swagger UI** использует файлы:
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a>
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a>
А **ReDoc** использует файл:
* <a href="https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a>
После этого ваша структура файлов может выглядеть следующим образом:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Обслуживание статических файлов
* Импортируйте `StaticFiles`.
* "Монтируйте" экземпляр `StaticFiles()` на конкретном пути.
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
### Тестирование статических файлов
Запустите ваше приложение и перейдите на <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>.
Вы должны увидеть очень длинный JavaScript файл для **ReDoc**.
Он может начинаться с чего-то вроде:
```JavaScript
/*! Для информации о лицензии смотрите redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Это подтверждает, что вы можете обслуживать статические файлы из вашего приложения, и что вы разместили статические файлы для документации в правильном месте.
Теперь мы можем настроить приложение для использования этих статических файлов для документации.
### Отключите автоматическую документацию для статических файлов
Также как при использовании пользовательского CDN, первый шаг — отключить автоматическую документацию, так как по умолчанию они используют CDN.
Чтобы отключить их, установите для их URL-адресов значение `None` при создании приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
### Включите пользовательскую документацию для статических файлов
И аналогично предыдущим шагам с пользовательским CDN, теперь вы можете создать *операции пути* для пользовательской документации.
Опять же, вы можете повторно использовать внутренние функции FastAPI для создания HTML-страниц для документации и передать им необходимые аргументы:
* `openapi_url`: URL-адрес, где HTML-страница документации может получить OpenAPI-схему для вашего API. Вы можете использовать здесь атрибут `app.openapi_url`.
* `title`: заголовок вашего API.
* `oauth2_redirect_url`: здесь вы можете использовать `app.swagger_ui_oauth2_redirect_url`, чтобы воспользоваться значением по умолчанию.
* `swagger_js_url`: URL-адрес, по которому HTML для ваших документов Swagger UI может получить **JavaScript** файл. **Это тот файл, который теперь обслуживает ваше приложение**.
* `swagger_css_url`: URL-адрес, по которому HTML для ваших документов Swagger UI может получить **CSS** файл. **Это тот файл, который теперь обслуживает ваше приложение**.
И аналогично для ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Совет
*Операция пути* для `swagger_ui_redirect` — это вспомогательная функция, когда вы используете OAuth2.
Если вы интегрируете свой API с провайдером OAuth2, вы сможете пройти аутентификацию и вернуться к документации API с полученными учетными данными. И взаимодействовать с ней, используя настоящую аутентификацию OAuth2.
Swagger UI справится с этим закулисно для вас, но ему нужен этот вспомогательный "редирект".
///
### Создайте *операцию пути* для тестирования статических файлов
Теперь, чтобы убедиться, что всё работает, создайте *операцию пути*:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
### Проверьте пользовательский интерфейс статических файлов
Теперь вы должны иметь возможность отключить WiFi, перейти к вашей документации по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> и перезагрузить страницу.
И даже без Интернета вы сможете увидеть документацию для вашего API и взаимодействовать с ней.

109
docs/ru/docs/how-to/custom-request-and-route.md

@ -0,0 +1,109 @@
# Пользовательские классы Request и APIRoute
В некоторых случаях вам может понадобиться переопределить логику, используемую в классах `Request` и `APIRoute`.
В частности, это может быть хорошей альтернативой логике в middleware (Промежуточный слой).
Например, если вы хотите прочитать или изменить тело запроса до того, как оно будет обработано вашим приложением.
/// danger
Это "продвинутая" функция.
Если вы только начинаете работать с **FastAPI**, вы можете пропустить этот раздел.
///
## Сценарии использования
Некоторые сценарии использования включают:
* Конвертация тел запросов, не являющихся JSON, в JSON (например, <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>).
* Разжатие тел запросов, сжатых методом gzip.
* Автоматическое логирование всех тел запросов.
## Обработка пользовательских кодировок тела запроса
Давайте посмотрим, как использовать пользовательский подкласс `Request` для разжатия gzip-запросов.
И подкласс `APIRoute`, чтобы использовать этот пользовательский класс запроса.
### Создание пользовательского класса `GzipRequest`
/// tip | Совет
Это учебный пример, чтобы показать как это работает. Если вам нужна поддержка gzip, вы можете использовать предоставленный [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}.
///
Сначала мы создадим класс `GzipRequest`, который переопределит метод `Request.body()`, чтобы разжать тело запроса, если присутствует соответствующий заголовок.
Если в заголовке нет `gzip`, он не будет пытаться разжать тело запроса.
Таким образом, один и тот же класс маршрута может обрабатывать как gzip-сжатые, так и несжатые запросы.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
### Создание пользовательского класса `GzipRoute`
Затем мы создаем пользовательский подкласс `fastapi.routing.APIRoute`, который будет использовать `GzipRequest`.
На этот раз он переопределит метод `APIRoute.get_route_handler()`.
Этот метод возвращает функцию. И эта функция принимает запрос и возвращает ответ.
Здесь мы используем его для создания `GzipRequest` из исходного запроса.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
/// note | Технические детали
`Request` имеет атрибут `request.scope`, который является просто Python-словарем, содержащим метаданные, относящиеся к запросу.
`Request` также имеет `request.receive`, это функция для "получения" тела запроса.
Словарь `scope` и функция `receive` являются частью спецификации ASGI.
И именно эти две вещи, `scope` и `receive`, нужны для создания нового экземпляра `Request`.
Чтобы узнать больше о `Request`, ознакомьтесь с <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">документацией Starlette о запросах</a>.
///
Единственное, что функция, возвращаемая `GzipRequest.get_route_handler`, делает иначе, это преобразует `Request` в `GzipRequest`.
Таким образом, наш `GzipRequest` позаботится о разжатии данных (если это необходимо) перед передачей их нашим *операциям пути*.
После этого вся логика обработки такая же.
Но благодаря изменениям в `GzipRequest.body`, тело запроса будет автоматически разжиматься, когда **FastAPI** нужно будет его загрузить.
## Доступ к телу запроса в обработчике исключений
/// tip | Подсказка
Чтобы решить эту же проблему, вероятно, гораздо проще использовать `body` в пользовательском обработчике для `RequestValidationError` ([Обработка ошибок](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
Но этот пример по-прежнему актуален и показывает, как взаимодействовать с внутренними компонентами.
///
Мы также можем использовать этот же подход, чтобы получить доступ к телу запроса в обработчике исключений.
Все, что нам нужно сделать, — это обработать запрос внутри блока `try`/`except`:
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
Если возникает исключение, экземпляр `Request` все равно будет доступен, поэтому мы можем прочитать и использовать тело запроса при обработке ошибки:
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
## Пользовательский класс `APIRoute` в роутере
Вы также можете установить параметр `route_class` для `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
В этом примере *операции путей* под `router` будут использовать пользовательский класс `TimedRoute`, и в ответе будет добавлен дополнительный HTTP-заголовок `X-Response-Time` с временем, затраченным на генерацию ответа:
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}

80
docs/ru/docs/how-to/extending-openapi.md

@ -0,0 +1,80 @@
# Расширение OpenAPI
Бывают случаи, когда вам может понадобиться изменить сгенерированную схему OpenAPI.
В этом разделе вы узнаете, как это сделать.
## Обычный процесс
Обычный (по умолчанию) процесс выглядит следующим образом.
Приложение `FastAPI` (экземпляр) имеет метод `.openapi()`, который ожидается вернуть схему OpenAPI.
В рамках создания объекта приложения для `/openapi.json` (или для того, что вы указали в `openapi_url`) регистрируется *операция пути*.
Она просто возвращает JSON ответ с результатом метода `.openapi()` приложения.
По умолчанию метод `.openapi()` проверяет свойство `.openapi_schema`, чтобы увидеть, есть ли в нем содержимое, и возвращает их.
Если его нет, он генерирует его с использованием вспомогательной функции в `fastapi.openapi.utils.get_openapi`.
И эта функция `get_openapi()` принимает в качестве параметров:
* `title`: Заголовок OpenAPI, отображаемый в документации.
* `version`: Версия вашего API, например `2.5.0`.
* `openapi_version`: Версия спецификации OpenAPI, используемой. По умолчанию последняя: `3.1.0`.
* `summary`: Краткое резюме API.
* `description`: Описание вашего API, оно может включать markdown и будет показано в документации.
* `routes`: Список маршрутов, это каждая из зарегистрированных *операций пути*. Они берутся из `app.routes`.
/// info | Информация
Параметр `summary` доступен в OpenAPI 3.1.0 и выше, поддерживается FastAPI 0.99.0 и выше.
///
## Переопределение значений по умолчанию
Используя информацию выше, вы можете использовать ту же вспомогательную функцию для генерации схемы OpenAPI и переопределения каждой части, которая вам нужна.
Например, давайте добавим <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">OpenAPI расширение ReDoc для включения настраиваемого логотипа</a>.
### Обычный **FastAPI**
Сначала напишите все ваше приложение на **FastAPI** как обычно:
{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
### Генерация схемы OpenAPI
Затем используйте ту же вспомогательную функцию для генерации схемы OpenAPI внутри функции `custom_openapi()`:
{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
### Изменение схемы OpenAPI
Теперь вы можете добавить расширение ReDoc, добавив настраиваемый `x-logo` в "объект" `info` в схеме OpenAPI:
{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
### Кэширование схемы OpenAPI
Вы можете использовать свойство `.openapi_schema` как "кэш", чтобы сохранить вашу сгенерированную схему.
Таким образом, ваше приложение не будет генерировать схему каждый раз, когда пользователь открывает вашу документацию API.
Она будет сгенерирована только один раз, и затем та же самая кэшированная схема будет использована для следующих запросов.
{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
### Переопределение метода
Теперь вы можете заменить метод `.openapi()` на вашу новую функцию.
{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
### Проверьте это
Как только вы перейдете по адресу <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>, вы увидите, что вы используете ваш настраиваемый логотип (в этом примере используется логотип **FastAPI**):
<img src="/img/tutorial/extending-openapi/image01.png">

39
docs/ru/docs/how-to/general.md

@ -0,0 +1,39 @@
# Общие сведения - How To - Рецепты
Вот несколько путеводителей на другие части документации для общих или часто задаваемых вопросов.
## Фильтрация данных - Безопасность
Чтобы убедиться, что вы не возвращаете больше данных, чем следует, прочитайте документацию по [Учебнику - Модель ответа - Возвращаемый тип](../tutorial/response-model.md){.internal-link target=_blank}.
## Теги документации - OpenAPI
Чтобы добавить теги к вашим *операциям пути* и сгруппировать их в интерфейсе документации, прочитайте документацию по [Учебнику - Конфигурации операций пути - Теги](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
## Краткое описание и описание документации - OpenAPI
Чтобы добавить краткое описание и описание к вашим *операциям пути* и отобразить их в интерфейсе документации, прочитайте документацию по [Учебнику - Конфигурации операций пути - Краткое описание и описание](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
## Описание ответа в документации - OpenAPI
Чтобы определить описание ответа, отображаемого в интерфейсе документации, прочитайте документацию по [Учебнику - Конфигурации операций пути - Описание ответа](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
## Устаревание *операции пути* в документации - OpenAPI
Чтобы пометить *операцию пути* как устаревшую и отобразить это в интерфейсе документации, прочитайте документацию по [Учебнику - Конфигурации операций пути - Устаревание](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
## Преобразование любых данных в совместимые с JSON
Чтобы преобразовать любые данные в совместимые с JSON, прочитайте документацию по [Учебнику - Совместимый кодировщик JSON](../tutorial/encoder.md){.internal-link target=_blank}.
## Метаданные OpenAPI - Документация
Чтобы добавить метаданные в вашу схему OpenAPI, включая лицензию, версию, контактную информацию и т.д., прочитайте документацию по [Учебнику - Метаданные и URL-адреса документации](../tutorial/metadata.md){.internal-link target=_blank}.
## Пользовательский URL OpenAPI
Чтобы кастомизировать (или удалить) URL OpenAPI, прочитайте документацию по [Учебнику - Метаданные и URL-адреса документации](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
## URL-адреса документации OpenAPI
Чтобы обновить URL-адреса, используемые для автоматически сгенерированных интерфейсов документации, прочитайте документацию по [Учебнику - Метаданные и URL-адреса документации](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.

60
docs/ru/docs/how-to/graphql.md

@ -0,0 +1,60 @@
# GraphQL
Так как **FastAPI** основан на стандарте **ASGI**, очень легко интегрировать любую библиотеку **GraphQL**, также совместимую с ASGI.
Вы можете комбинировать обычные *операции пути* FastAPI с GraphQL в одном и том же приложении.
/// tip | Совет
**GraphQL** решает некоторые очень специфические случаи использования.
У него есть **преимущества** и **недостатки** по сравнению с обычными **веб API**.
Убедитесь, что вы оценили, компенсируют ли **выгоды** для вашего случая использования **недостатки**. 🤓
///
## Библиотеки GraphQL
Вот некоторые из библиотек **GraphQL**, которые поддерживают **ASGI**. Вы можете использовать их с **FastAPI**:
* <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a> 🍓
* С <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">документацией для FastAPI</a>
* <a href="https://ariadnegraphql.org/" class="external-link" target="_blank">Ariadne</a>
* С <a href="https://ariadnegraphql.org/docs/fastapi-integration" class="external-link" target="_blank">документацией для FastAPI</a>
* <a href="https://tartiflette.io/" class="external-link" target="_blank">Tartiflette</a>
* С <a href="https://tartiflette.github.io/tartiflette-asgi/" class="external-link" target="_blank">Tartiflette ASGI</a> для обеспечения интеграции с ASGI
* <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>
* С <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>
## GraphQL с использованием Strawberry
Если вам нужно или вы хотите работать с **GraphQL**, библиотека <a href="https://strawberry.rocks/" class="external-link" target="_blank">**Strawberry**</a> является **рекомендуемой**, так как ее дизайн наиболее близок к дизайну **FastAPI**, и всё основано на **аннотациях типов**.
В зависимости от вашего случая использования, вы можете предпочесть другую библиотеку, но если бы вы спросили меня, я бы, вероятно, предложил попробовать **Strawberry**.
Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
Вы можете узнать больше о Strawberry в <a href="https://strawberry.rocks/" class="external-link" target="_blank">документации Strawberry</a>.
А также документацию о <a href="https://strawberry.rocks/docs/integrations/fastapi" class="external-link" target="_blank">Strawberry с FastAPI</a>.
## Устаревший `GraphQLApp` от Starlette
В предыдущих версиях Starlette включалась класс `GraphQLApp` для интеграции с <a href="https://graphene-python.org/" class="external-link" target="_blank">Graphene</a>.
Он был исключен из Starlette, но если у вас есть код, который использует его, вы можете легко **мигрировать** на <a href="https://github.com/ciscorn/starlette-graphene3" class="external-link" target="_blank">starlette-graphene3</a>, который покрывает тот же случай использования и имеет **почти идентичный интерфейс**.
/// tip | Совет
Если вам нужен GraphQL, я всё же рекомендую обратить внимание на <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry</a>, так как он основан на аннотациях типов, а не на кастомных классах и типах.
///
## Узнать больше
Вы можете узнать больше о **GraphQL** в <a href="https://graphql.org/" class="external-link" target="_blank">официальной документации GraphQL</a>.
Также вы можете прочитать больше о каждой из описанных выше библиотек по предоставленным ссылкам.

13
docs/ru/docs/how-to/index.md

@ -0,0 +1,13 @@
# Как сделать - Рецепты
Здесь вы найдете различные рецепты или руководства "как сделать" для **различных тем**.
Большинство этих идей будут более или менее **независимыми**, и в большинстве случаев вам следует изучать их только в том случае, если они напрямую применимы к **вашему проекту**.
Если что-то кажется интересным и полезным для вашего проекта, смело изучайте это, в противном случае вы можете просто пропустить их.
/// tip | Совет
Если вы хотите **изучить FastAPI** структурированным образом (рекомендуется), прочитайте [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} главу за главой.
///

104
docs/ru/docs/how-to/separate-openapi-schemas.md

@ -0,0 +1,104 @@
# Отдельные схемы OpenAPI для ввода и вывода или нет
При использовании **Pydantic v2** сгенерированный OpenAPI более точный и **правильный**, чем раньше. 😎
Фактически, в некоторых случаях, будет даже **две схемы JSON** в OpenAPI для одной и той же модели Pydantic: для ввода и вывода, в зависимости от наличия **значений по умолчанию**.
Давайте посмотрим, как это работает и как изменить это, если необходимо.
## Модели Pydantic для ввода и вывода
Предположим, у вас есть модель Pydantic со значениями по умолчанию, как эта:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Модель для ввода
Если использовать эту модель для ввода, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
...то поле `description` **не будет обязательным**. Потому что у него есть значение по умолчанию `None`.
### Модель ввода в документации
Вы можете убедиться в этом, посмотрев документацию: поле `description` не помечено как обязательное **красной звездочкой**:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image01.png">
</div>
### Модель для вывода
Но если использовать ту же модель для вывода, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
...тогда, поскольку `description` имеет значение по умолчанию, если вы **ничего не вернете** для этого поля, оно по-прежнему будет иметь это **значение по умолчанию**.
### Модель для данных ответа
Если вы взаимодействуете с документацией и проверяете ответ, даже если в коде ничего не добавлялось в одно из полей `description`, JSON-ответ содержит значение по умолчанию (`null`):
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image02.png">
</div>
Это означает, что у него **всегда будет значение**, просто иногда это значение может быть `None` (или `null` в JSON).
Это значит, что клиенты, использующие ваш API, не должны проверять, существует ли значение, они могут **предполагать, что поле всегда будет**, но в некоторых случаях оно будет иметь значение по умолчанию `None`.
Способ описать это в OpenAPI — отметить это поле как **обязательное**, потому что оно всегда будет присутствовать.
Из-за этого, схема JSON для модели может отличаться в зависимости от того, используется она для **ввода или вывода**:
* для **ввода** `description` **не будет обязательным**
* для **вывода** оно будет **обязательным** (и возможно `None`, или в терминах JSON, `null`)
### Модель для вывода в документации
Вы также можете проверить модель вывода в документации, **оба** поля `name` и `description` отмечены как **обязательные** с **красной звездочкой**:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image03.png">
</div>
### Модель для ввода и вывода в документации
И если вы проверите все доступные схемы (JSON схемы) в OpenAPI, вы увидите, что их две: `Item-Input` и `Item-Output`.
Для `Item-Input` `description` **не обязателен**, у него нет красной звездочки.
Но для `Item-Output` `description` **обязателен**, у него есть красная звездочка.
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image04.png">
</div>
С этой функцией из **Pydantic v2**, ваша документация API более **точная**, и если у вас есть автоматически сгенерированные клиенты и SDK, они будут более точными тоже, с более лучшим **опытом разработчика** и консистентностью. 🎉
## Не разделять схемы
Теперь, есть случаи, когда вы можете захотеть иметь **одну и ту же схему для ввода и вывода**.
Возможно, основным случаем использования будет, если у вас уже есть какой-то автоматически сгенерированный клиентский код/SDK, и вы не хотите обновлять весь автоматически сгенерированный клиентский код/SDK еще. Вы, вероятно, захотите сделать это в какой-то момент, но, возможно, не прямо сейчас.
В этом случае вы можете отключить эту функцию в **FastAPI** с параметром `separate_input_output_schemas=False`.
/// info | Информация
Поддержка `separate_input_output_schemas` была добавлена в FastAPI в версии `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Одинаковая схема для вводных и выводных моделей в документации
И теперь будет одна единственная схема для ввода и вывода для модели, только `Item`, и `description` будет **необязательным**:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
</div>
Это такое же поведение, как в Pydantic v1. 🤓

7
docs/ru/docs/how-to/testing-database.md

@ -0,0 +1,7 @@
# Тестирование базы данных
Вы можете изучить базы данных, SQL и SQLModel в <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">документации SQLModel</a>. 🤓
Есть мини <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">учебное пособие по использованию SQLModel с FastAPI</a>. ✨
Этот учебник включает в себя раздел о <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">тестировании SQL баз данных</a>. 😎

3
docs/ru/docs/resources/index.md

@ -0,0 +1,3 @@
# Ресурсы
Дополнительные ресурсы, внешние ссылки, статьи и другое. ✈️
Loading…
Cancel
Save