Browse Source

🌐 Add Russian translations for missing pages (LLM-generated) (#14135)

* Copy En pages

* Translate pages (reviewed and corrected)

* Apply changes from latest PRs: 13917 and 14099
pull/14136/head
Motov Yurii 4 days ago
committed by GitHub
parent
commit
04d5e659bc
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 503
      docs/ru/docs/_llm-test.md
  2. 247
      docs/ru/docs/advanced/additional-responses.md
  3. 153
      docs/ru/docs/advanced/advanced-dependencies.md
  4. 458
      docs/ru/docs/advanced/behind-a-proxy.md
  5. 312
      docs/ru/docs/advanced/custom-response.md
  6. 95
      docs/ru/docs/advanced/dataclasses.md
  7. 165
      docs/ru/docs/advanced/events.md
  8. 208
      docs/ru/docs/advanced/generate-clients.md
  9. 97
      docs/ru/docs/advanced/middleware.md
  10. 186
      docs/ru/docs/advanced/openapi-callbacks.md
  11. 55
      docs/ru/docs/advanced/openapi-webhooks.md
  12. 204
      docs/ru/docs/advanced/path-operation-advanced-configuration.md
  13. 41
      docs/ru/docs/advanced/response-headers.md
  14. 107
      docs/ru/docs/advanced/security/http-basic-auth.md
  15. 19
      docs/ru/docs/advanced/security/index.md
  16. 274
      docs/ru/docs/advanced/security/oauth2-scopes.md
  17. 346
      docs/ru/docs/advanced/settings.md
  18. 67
      docs/ru/docs/advanced/sub-applications.md
  19. 126
      docs/ru/docs/advanced/templates.md
  20. 53
      docs/ru/docs/advanced/testing-dependencies.md
  21. 12
      docs/ru/docs/advanced/testing-events.md
  22. 13
      docs/ru/docs/advanced/testing-websockets.md
  23. 56
      docs/ru/docs/advanced/using-request-directly.md
  24. 35
      docs/ru/docs/advanced/wsgi.md
  25. 16
      docs/ru/docs/deployment/cloud.md
  26. 139
      docs/ru/docs/deployment/server-workers.md
  27. 56
      docs/ru/docs/how-to/conditional-openapi.md
  28. 70
      docs/ru/docs/how-to/configure-swagger-ui.md
  29. 185
      docs/ru/docs/how-to/custom-docs-ui-assets.md
  30. 109
      docs/ru/docs/how-to/custom-request-and-route.md
  31. 80
      docs/ru/docs/how-to/extending-openapi.md
  32. 39
      docs/ru/docs/how-to/general.md
  33. 60
      docs/ru/docs/how-to/graphql.md
  34. 13
      docs/ru/docs/how-to/index.md
  35. 104
      docs/ru/docs/how-to/separate-openapi-schemas.md
  36. 7
      docs/ru/docs/how-to/testing-database.md
  37. 3
      docs/ru/docs/resources/index.md

503
docs/ru/docs/_llm-test.md

@ -0,0 +1,503 @@
# Тестовый файл LLM { #llm-test-file }
Этот документ проверяет, понимает ли <abbr title="Large Language Model – Большая языковая модель">LLM</abbr>, переводящая документацию, `general_prompt` в `scripts/translate.py` и языковой специфичный промпт в `docs/{language code}/llm-prompt.md`. Языковой специфичный промпт добавляется к `general_prompt`.
Тесты, добавленные здесь, увидят все создатели языковых промптов.
Использование:
* Подготовьте языковой специфичный промпт — `docs/{language code}/llm-prompt.md`.
* Выполните новый перевод этого документа на нужный целевой язык (см., например, команду `translate-page` в `translate.py`). Это создаст перевод в `docs/{language code}/docs/_llm-test.md`.
* Проверьте, всё ли в порядке в переводе.
* При необходимости улучшите ваш языковой специфичный промпт, общий промпт или английский документ.
* Затем вручную исправьте оставшиеся проблемы в переводе, чтобы он был хорошим.
* Переведите заново, имея хороший перевод на месте. Идеальным результатом будет ситуация, когда LLM больше не вносит изменений в перевод. Это означает, что общий промпт и ваш языковой специфичный промпт максимально хороши (иногда он будет делать несколько, казалось бы, случайных изменений, причина в том, что <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">LLM — недетерминированные алгоритмы</a>).
Тесты:
## Фрагменты кода { #code-snippets}
//// tab | Тест
Это фрагмент кода: `foo`. А это ещё один фрагмент кода: `bar`. И ещё один: `baz quux`.
////
//// tab | Информация
Содержимое фрагментов кода должно оставаться как есть.
См. раздел `### Content of code snippets` в общем промпте в `scripts/translate.py`.
////
## Кавычки { #quotes }
//// tab | Тест
Вчера мой друг написал: "Если вы написали incorrectly правильно, значит вы написали это неправильно". На что я ответил: "Верно, но 'incorrectly' — это неправильно, а не '"incorrectly"'".
/// note | Примечание
LLM, вероятно, переведёт это неправильно. Интересно лишь то, сохранит ли она фиксированный перевод при повторном переводе.
///
////
//// tab | Информация
Автор промпта может выбрать, хочет ли он преобразовывать нейтральные кавычки в типографские. Допускается оставить их как есть.
См., например, раздел `### Quotes` в `docs/de/llm-prompt.md`.
////
## Кавычки во фрагментах кода { #quotes-in-code-snippets}
//// tab | Тест
`pip install "foo[bar]"`
Примеры строковых литералов во фрагментах кода: `"this"`, `'that'`.
Сложный пример строковых литералов во фрагментах кода: `f"I like {'oranges' if orange else "apples"}"`
Хардкор: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Информация
... Однако кавычки внутри фрагментов кода должны оставаться как есть.
////
## Блоки кода { #code-blocks }
//// tab | Тест
Пример кода Bash...
```bash
# Вывести приветствие вселенной
echo "Hello universe"
```
...и пример вывода в консоли...
```console
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
Searching for package file structure
```
...и ещё один пример вывода в консоли...
```console
// Создать директорию "Code"
$ mkdir code
// Перейти в эту директорию
$ cd code
```
...и пример кода на Python...
```Python
wont_work() # Это не сработает 😱
works(foo="bar") # Это работает 🎉
```
...и на этом всё.
////
//// tab | Информация
Код в блоках кода не должен изменяться, за исключением комментариев.
См. раздел `### Content of code blocks` в общем промпте в `scripts/translate.py`.
////
## Вкладки и цветные блоки { #tabs-and-colored-boxes }
//// tab | Тест
/// info | Информация
Некоторый текст
///
/// note | Примечание
Некоторый текст
///
/// note | Технические подробности
Некоторый текст
///
/// check | Проверка
Некоторый текст
///
/// tip | Совет
Некоторый текст
///
/// warning | Предупреждение
Некоторый текст
///
/// danger | Опасность
Некоторый текст
///
////
//// tab | Информация
Для вкладок и блоков `Info`/`Note`/`Warning`/и т.п. нужно добавить перевод их заголовка после вертикальной черты (`|`).
См. разделы `### Special blocks` и `### Tab blocks` в общем промпте в `scripts/translate.py`.
////
## Веб- и внутренние ссылки { #web-and-internal-links }
//// tab | Тест
Текст ссылок должен переводиться, адрес ссылки не должен изменяться:
* [Ссылка на заголовок выше](#code-snippets)
* [Внутренняя ссылка](index.md#installation){.internal-link target=_blank}
* <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">Внешняя ссылка</a>
* <a href="https://fastapi.tiangolo.com/css/styles.css" class="external-link" target="_blank">Ссылка на стиль</a>
* <a href="https://fastapi.tiangolo.com/js/logic.js" class="external-link" target="_blank">Ссылка на скрипт</a>
* <a href="https://fastapi.tiangolo.com/img/foo.jpg" class="external-link" target="_blank">Ссылка на изображение</a>
Текст ссылок должен переводиться, адрес ссылки должен указывать на перевод:
* <a href="https://fastapi.tiangolo.com/ru/" class="external-link" target="_blank">Ссылка на FastAPI</a>
////
//// tab | Информация
Ссылки должны переводиться, но их адреса не должны изменяться. Исключение — абсолютные ссылки на страницы документации FastAPI. В этом случае ссылка должна вести на перевод.
См. раздел `### Links` в общем промпте в `scripts/translate.py`.
////
## HTML-элементы "abbr" { #html-abbr-elements }
//// tab | Тест
Вот некоторые элементы, обёрнутые в HTML-элементы "abbr" (часть выдумана):
### abbr даёт полную расшифровку { #the-abbr-gives-a-full-phrase }
* <abbr title="Getting Things Done – Как привести дела в порядок">GTD</abbr>
* <abbr title="less than – меньше чем"><code>lt</code></abbr>
* <abbr title="XML Web Token – XML веб‑токен">XWT</abbr>
* <abbr title="Parallel Server Gateway Interface – Параллельный серверный интерфейс шлюза">PSGI</abbr>
### abbr даёт объяснение { #the-abbr-gives-an-explanation }
* <abbr title="Группа машин, которые настроены на соединение и совместную работу определённым образом.">кластер</abbr>
* <abbr title="Метод машинного обучения, который использует искусственные нейронные сети с многочисленными скрытыми слоями между входным и выходным слоями, тем самым формируя сложную внутреннюю структуру">Глубокое обучение</abbr>
### abbr даёт полную расшифровку и объяснение { #the-abbr-gives-a-full-phrase-and-an-explanation }
* <abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla: документация для разработчиков, созданная командой Firefox">MDN</abbr>
* <abbr title="Input/Output – Ввод/Вывод: чтение или запись на диск, сетевое взаимодействие.">I/O</abbr>.
////
//// tab | Информация
Атрибуты "title" элементов "abbr" переводятся по определённым правилам.
Переводы могут добавлять свои собственные элементы "abbr", которые LLM не должна удалять. Например, чтобы объяснить английские слова.
См. раздел `### HTML abbr elements` в общем промпте в `scripts/translate.py`.
////
## Заголовки { #headings }
//// tab | Тест
### Разработка веб‑приложения — руководство { #develop-a-webapp-a-tutorial }
Привет.
### Аннотации типов и -аннотации { #type-hints-and-annotations }
Снова привет.
### Супер- и подклассы { #super-and-subclasses }
Снова привет.
////
//// tab | Информация
Единственное жёсткое правило для заголовков — LLM должна оставить часть хеша в фигурных скобках без изменений, чтобы ссылки не ломались.
См. раздел `### Headings` в общем промпте в `scripts/translate.py`.
Для некоторых языковых инструкций см., например, раздел `### Headings` в `docs/de/llm-prompt.md`.
////
## Термины, используемые в документации { #terms-used-in-the-docs }
//// tab | Тест
* вы
* ваш
* например
* и т.д.
* `foo` как `int`
* `bar` как `str`
* `baz` как `list`
* Учебник — Руководство пользователя
* Расширенное руководство пользователя
* Документация по SQLModel
* Документация API
* Автоматическая документация
* Наука о данных
* Глубокое обучение
* Машинное обучение
* Внедрение зависимостей
* Аутентификация HTTP Basic
* HTTP Digest
* формат ISO
* стандарт JSON Schema
* JSON-схема
* определение схемы
* password flow
* Мобильный
* устаревший
* спроектированный
* некорректный
* на лету
* стандарт
* по умолчанию
* чувствительный к регистру
* нечувствительный к регистру
* обслуживать приложение
* отдавать страницу
* приложение
* приложение
* HTTP-запрос
* HTTP-ответ
* ответ с ошибкой
* операция пути
* декоратор операции пути
* функция-обработчик пути
* тело
* тело запроса
* тело ответа
* JSON-тело
* тело формы
* тело файла
* тело функции
* параметр
* body-параметр
* path-параметр
* query-параметр
* cookie-параметр
* параметр заголовка
* параметр формы
* параметр функции
* событие
* событие запуска
* запуск сервера
* событие остановки
* событие lifespan
* обработчик
* обработчик события
* обработчик исключений
* обрабатывать
* модель
* Pydantic-модель
* модель данных
* модель базы данных
* модель формы
* объект модели
* класс
* базовый класс
* родительский класс
* подкласс
* дочерний класс
* родственный класс
* метод класса
* заголовок
* HTTP-заголовки
* заголовок авторизации
* заголовок `Authorization`
* заголовок `Forwarded`
* система внедрения зависимостей
* зависимость
* зависимый объект
* зависимый
* ограниченный вводом/выводом
* ограниченный процессором
* конкурентность
* параллелизм
* многопроцессность
* переменная окружения
* переменная окружения
* `PATH`
* переменная `PATH`
* аутентификация
* провайдер аутентификации
* авторизация
* форма авторизации
* провайдер авторизации
* пользователь аутентифицируется
* система аутентифицирует пользователя
* CLI
* интерфейс командной строки
* сервер
* клиент
* облачный провайдер
* облачный сервис
* разработка
* этапы разработки
* dict
* словарь
* перечисление
* enum
* член перечисления
* кодировщик
* декодировщик
* кодировать
* декодировать
* исключение
* вызвать
* выражение
* оператор
* фронтенд
* бэкенд
* обсуждение на GitHub
* Issue на GitHub (тикет/обращение)
* производительность
* оптимизация производительности
* тип возвращаемого значения
* возвращаемое значение
* безопасность
* схема безопасности
* задача
* фоновая задача
* функция задачи
* шаблон
* шаблонизатор
* аннотация типов
* аннотация типов
* воркер сервера
* воркер Uvicorn
* воркер Gunicorn
* воркер-процесс
* класс воркера
* рабочая нагрузка
* деплой
* развернуть
* SDK
* набор средств разработки ПО
* `APIRouter`
* `requirements.txt`
* токен Bearer
* несовместимое изменение
* баг
* кнопка
* вызываемый объект
* код
* коммит
* менеджер контекста
* корутина
* сессия базы данных
* диск
* домен
* движок
* фиктивный X
* метод HTTP GET
* элемент
* библиотека
* lifespan
* блокировка
* middleware (Промежуточный слой)
* мобильное приложение
* модуль
* монтирование
* сеть
* origin (источник)
* переопределение
* полезная нагрузка
* процессор
* свойство
* прокси
* пулл-реквест (запрос на изменение)
* запрос
* ОЗУ
* удалённая машина
* статус-код
* строка
* тег
* веб‑фреймворк
* подстановочный знак
* вернуть
* валидировать
////
//// tab | Информация
Это неполный и ненормативный список (в основном) технических терминов, встречающихся в документации. Он может помочь автору промпта понять, по каким терминам LLM нужна подсказка. Например, когда она продолжает возвращать действительно хороший перевод к неоптимальному. Или когда у неё возникают проблемы со склонением/спряжением термина на вашем языке.
См., например, раздел `### List of English terms and their preferred German translations` в `docs/de/llm-prompt.md`.
////

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

@ -0,0 +1,247 @@
# Дополнительные ответы в OpenAPI { #additional-responses-in-openapi }
/// warning | Предупреждение
Это довольно продвинутая тема.
Если вы только начинаете работать с **FastAPI**, возможно, вам это пока не нужно.
///
Вы можете объявлять дополнительные ответы с дополнительными статус-кодами, типами содержимого, описаниями и т.д.
Эти дополнительные ответы будут включены в схему OpenAPI, и поэтому появятся в документации API.
Но для таких дополнительных ответов убедитесь, что вы возвращаете `Response`, например `JSONResponse`, напрямую, со своим статус-кодом и содержимым.
## Дополнительный ответ с `model` { #additional-response-with-model }
Вы можете передать вашим декораторам операции пути параметр `responses`.
Он принимает `dict`: ключи — это статус-коды для каждого ответа (например, `200`), а значения — другие `dict` с информацией для каждого из них.
Каждый из этих `dict` для ответа может иметь ключ `model`, содержащий Pydantic-модель, аналогично `response_model`.
**FastAPI** возьмёт эту модель, сгенерирует для неё JSON‑схему и включит её в нужное место в OpenAPI.
Например, чтобы объявить ещё один ответ со статус-кодом `404` и Pydantic-моделью `Message`, можно написать:
{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
/// note | Примечание
Имейте в виду, что необходимо возвращать `JSONResponse` напрямую.
///
/// info | Информация
Ключ `model` не является частью OpenAPI.
**FastAPI** возьмёт Pydantic-модель оттуда, сгенерирует JSON‑схему и поместит её в нужное место.
Нужное место:
* В ключе `content`, значением которого является другой JSON‑объект (`dict`), содержащий:
* Ключ с типом содержимого, например `application/json`, значением которого является другой JSON‑объект, содержащий:
* Ключ `schema`, значением которого является JSON‑схема из модели — вот нужное место.
* **FastAPI** добавляет здесь ссылку на глобальные JSON‑схемы в другом месте вашего OpenAPI вместо того, чтобы включать схему напрямую. Так другие приложения и клиенты смогут использовать эти JSON‑схемы напрямую, предоставлять лучшие инструменты генерации кода и т.д.
///
Сгенерированные в OpenAPI ответы для этой операции пути будут такими:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Схемы даны как ссылки на другое место внутри схемы OpenAPI:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Дополнительные типы содержимого для основного ответа { #additional-media-types-for-the-main-response }
Вы можете использовать этот же параметр `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` для любого дополнительного ответа, у которого есть связанная модель.
///
## Комбинирование информации { #combining-information }
Вы также можете комбинировать информацию об ответах из нескольких мест, включая параметры `response_model`, `status_code` и `responses`.
Вы можете объявить `response_model`, используя статус-код по умолчанию `200` (или свой, если нужно), а затем объявить дополнительную информацию для этого же ответа в `responses`, напрямую в схеме OpenAPI.
**FastAPI** сохранит дополнительную информацию из `responses` и объединит её с JSON‑схемой из вашей модели.
Например, вы можете объявить ответ со статус-кодом `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">
## Комбинирование предопределённых и пользовательских ответов { #combine-predefined-responses-and-custom-ones }
Возможно, вы хотите иметь некоторые предопределённые ответы, применимые ко многим операциям пути, но при этом комбинировать их с пользовательскими ответами, необходимыми для каждой конкретной операции пути.
В таких случаях вы можете использовать приём 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 { #more-information-about-openapi-responses }
Чтобы увидеть, что именно можно включать в ответы, посмотрите эти разделы спецификации OpenAPI:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">Объект Responses OpenAPI</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">Объект Response OpenAPI</a>, вы можете включить всё из этого объекта напрямую в каждый ответ внутри вашего параметра `responses`. Включая `description`, `headers`, `content` (внутри него вы объявляете разные типы содержимого и JSON‑схемы) и `links`.

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

@ -0,0 +1,153 @@
# Продвинутые зависимости { #advanced-dependencies }
## Параметризованные зависимости { #parameterized-dependencies }
Все зависимости, которые мы видели, — это конкретная функция или класс.
Но бывают случаи, когда нужно задавать параметры зависимости, не объявляя много разных функций или классов.
Представим, что нам нужна зависимость, которая проверяет, содержит ли query-параметр `q` некоторое фиксированное содержимое.
Но при этом мы хотим иметь возможность параметризовать это фиксированное содержимое.
## «Вызываемый» экземпляр { #a-callable-instance }
В Python есть способ сделать экземпляр класса «вызываемым» объектом.
Не сам класс (он уже является вызываемым), а экземпляр этого класса.
Для этого объявляем метод `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
В этом случае именно `__call__` **FastAPI** использует для проверки дополнительных параметров и подзависимостей, и именно он будет вызван, чтобы позже передать значение параметру в вашей *функции-обработчике пути*.
## Параметризуем экземпляр { #parameterize-the-instance }
Теперь мы можем использовать `__init__`, чтобы объявить параметры экземпляра, с помощью которых будем «параметризовать» зависимость:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
В этом случае **FastAPI** вовсе не трогает `__init__` и не зависит от него — мы используем его напрямую в нашем коде.
## Создаём экземпляр { #create-an-instance }
Мы можем создать экземпляр этого класса так:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
Так мы «параметризуем» нашу зависимость: теперь внутри неё хранится "bar" в атрибуте `checker.fixed_content`.
## Используем экземпляр как зависимость { #use-the-instance-as-a-dependency }
Затем мы можем использовать этот `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 | Совет
Все это может показаться притянутым за уши. И пока может быть не совсем понятно, чем это полезно.
Эти примеры намеренно простые, но они показывают, как всё устроено.
В главах про безопасность есть вспомогательные функции, реализованные тем же способом.
Если вы поняли всё выше, вы уже знаете, как «под капотом» работают эти утилиты для безопасности.
///
## Зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Предупреждение
Скорее всего, вам не понадобятся эти технические детали.
Они полезны главным образом, если у вас было приложение FastAPI версии ниже 0.118.0 и вы столкнулись с проблемами зависимостей с `yield`.
///
Зависимости с `yield` со временем изменялись, чтобы учитывать разные случаи применения и исправлять проблемы. Ниже — краткое резюме изменений.
### Зависимости с `yield` и `StreamingResponse`, технические детали { #dependencies-with-yield-and-streamingresponse-technical-details }
До FastAPI 0.118.0, если вы использовали зависимость с `yield`, код после `yield` выполнялся после возврата из *функции-обработчика пути*, но прямо перед отправкой ответа.
Идея состояла в том, чтобы не удерживать ресурсы дольше необходимого, пока ответ «путешествует» по сети.
Это изменение также означало, что если вы возвращали `StreamingResponse`, код после `yield` в зависимости уже успевал выполниться.
Например, если у вас была сессия базы данных в зависимости с `yield`, `StreamingResponse` не смог бы использовать эту сессию во время стриминга данных, потому что сессия уже была закрыта в коде после `yield`.
В версии 0.118.0 это поведение было возвращено к тому, что код после `yield` выполняется после отправки ответа.
/// info | Информация
Как вы увидите ниже, это очень похоже на поведение до версии 0.106.0, но с несколькими улучшениями и исправлениями краевых случаев.
///
#### Сценарии с ранним выполнением кода после `yield` { #use-cases-with-early-exit-code }
Есть некоторые сценарии со специфическими условиями, которым могло бы помочь старое поведение — выполнение кода после `yield` перед отправкой ответа.
Например, представьте, что вы используете сессию базы данных в зависимости с `yield` только для проверки пользователя, а в самой *функции-обработчике пути* эта сессия больше не используется, и при этом ответ отправляется долго, например, это `StreamingResponse`, который медленно отправляет данные и по какой-то причине не использует базу данных.
В таком случае сессия базы данных будет удерживаться до завершения отправки ответа, хотя если вы её не используете, удерживать её не требуется.
Это могло бы выглядеть так:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
Код после `yield`, автоматическое закрытие `Session` в:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
…будет выполнен после того, как ответ закончит отправку медленных данных:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Но поскольку `generate_stream()` не использует сессию базы данных, нет реальной необходимости держать сессию открытой во время отправки ответа.
Если у вас именно такой сценарий с SQLModel (или SQLAlchemy), вы можете явно закрыть сессию, когда она больше не нужна:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Так сессия освободит подключение к базе данных, и другие запросы смогут его использовать.
Если у вас есть другой сценарий, где нужно раннее завершение зависимости с `yield`, пожалуйста, создайте <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">вопрос в GitHub Discussions</a> с описанием конкретного кейса и почему вам было бы полезно иметь раннее закрытие для зависимостей с `yield`.
Если появятся веские причины для раннего закрытия в зависимостях с `yield`, я рассмотрю добавление нового способа опционально включать раннее закрытие.
### Зависимости с `yield` и `except`, технические детали { #dependencies-with-yield-and-except-technical-details }
До FastAPI 0.110.0, если вы использовали зависимость с `yield`, затем перехватывали исключение с `except` в этой зависимости и не пробрасывали исключение снова, исключение автоматически пробрасывалось дальше к обработчикам исключений или к обработчику внутренней ошибки сервера.
В версии 0.110.0 это было изменено, чтобы исправить неконтролируемое потребление памяти из‑за проброшенных исключений без обработчика (внутренние ошибки сервера) и привести поведение в соответствие с обычным поведением Python-кода.
### Фоновые задачи и зависимости с `yield`, технические детали { #background-tasks-and-dependencies-with-yield-technical-details }
До FastAPI 0.106.0 вызывать исключения после `yield` было невозможно: код после `yield` в зависимостях выполнялся уже после отправки ответа, поэтому [Обработчики исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} к тому моменту уже отработали.
Так было сделано в основном для того, чтобы можно было использовать те же объекты, «отданные» зависимостями через `yield`, внутри фоновых задач, потому что код после `yield` выполнялся после завершения фоновых задач.
В FastAPI 0.106.0 это изменили, чтобы не удерживать ресурсы, пока ответ передаётся по сети.
/// tip | Совет
Кроме того, фоновая задача обычно — это самостоятельный фрагмент логики, который следует обрабатывать отдельно, со своими ресурсами (например, со своим подключением к базе данных).
Так код, скорее всего, будет чище.
///
Если вы полагались на прежнее поведение, теперь ресурсы для фоновых задач следует создавать внутри самой фоновой задачи и использовать внутри неё только данные, которые не зависят от ресурсов зависимостей с `yield`.
Например, вместо использования той же сессии базы данных, создайте новую сессию в фоновой задаче и получите объекты из базы данных с помощью этой новой сессии. И затем, вместо передачи объекта из базы данных параметром в функцию фоновой задачи, передавайте идентификатор этого объекта и заново получайте объект внутри функции фоновой задачи.

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

@ -0,0 +1,458 @@
# За прокси‑сервером { #behind-a-proxy }
Во многих случаях перед приложением FastAPI используется прокси‑сервер, например Traefik или Nginx.
Такие прокси могут обрабатывать HTTPS‑сертификаты и многое другое.
## Пересылаемые заголовки прокси { #proxy-forwarded-headers }
Прокси перед вашим приложением обычно на лету добавляет некоторые HTTP‑заголовки перед отправкой запроса на ваш сервер, чтобы сообщить ему, что запрос был переслан прокси, а также передать исходный (публичный) URL (включая домен), информацию об использовании HTTPS и т.д.
Программа сервера (например, Uvicorn, запущенный через FastAPI CLI) умеет интерпретировать эти заголовки и передавать соответствующую информацию вашему приложению.
Но из соображений безопасности, пока сервер не уверен, что находится за доверенным прокси, он не будет интерпретировать эти заголовки.
/// note | Технические детали
Заголовки прокси:
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
///
### Включить пересылаемые заголовки прокси { #enable-proxy-forwarded-headers }
Вы можете запустить FastAPI CLI с опцией командной строки `--forwarded-allow-ips` и передать IP‑адреса, которым следует доверять при чтении этих пересылаемых заголовков.
Если указать `--forwarded-allow-ips="*"`, приложение будет доверять всем входящим IP.
Если ваш сервер находится за доверенным прокси и только прокси обращается к нему, этого достаточно, чтобы он принимал IP этого прокси.
<div class="termy">
```console
$ fastapi run --forwarded-allow-ips="*"
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Редиректы с HTTPS { #redirects-with-https }
Например, вы объявили операцию пути `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01.py hl[6] *}
Если клиент обратится к `/items`, по умолчанию произойдёт редирект на `/items/`.
Но до установки опции `--forwarded-allow-ips` редирект может вести на `http://localhost:8000/items/`.
Однако приложение может быть доступно по `https://mysuperapp.com`, и редирект должен вести на `https://mysuperapp.com/items/`.
Указав `--proxy-headers`, FastAPI сможет редиректить на корректный адрес. 😎
```
https://mysuperapp.com/items/
```
/// tip | Совет
Если хотите узнать больше об HTTPS, смотрите руководство [О HTTPS](../deployment/https.md){.internal-link target=_blank}.
///
### Как работают пересылаемые заголовки прокси
Ниже показано, как прокси добавляет пересылаемые заголовки между клиентом и сервером приложения:
```mermaid
sequenceDiagram
participant Client as Клиент
participant Proxy as Прокси/Балансировщик нагрузки
participant Server as FastAPI-сервер
Client->>Proxy: HTTPS-запрос<br/>Host: mysuperapp.com<br/>Path: /items
Note over Proxy: Прокси-сервер добавляет пересылаемые заголовки
Proxy->>Server: HTTP-запрос<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
Note over Server: Server интерпретирует HTTP-заголовки<br/>(если --forwarded-allow-ips установлен)
Server->>Proxy: HTTP-ответ<br/>с верными HTTPS URLs
Proxy->>Client: HTTPS-ответ
```
Прокси перехватывает исходный клиентский запрос и добавляет специальные пересылаемые заголовки (`X-Forwarded-*`) перед передачей запроса на сервер приложения.
Эти заголовки сохраняют информацию об исходном запросе, которая иначе была бы потеряна:
* X-Forwarded-For: исходный IP‑адрес клиента
* X-Forwarded-Proto: исходный протокол (`https`)
* X-Forwarded-Host: исходный хост (`mysuperapp.com`)
Когда FastAPI CLI сконфигурирован с `--forwarded-allow-ips`, он доверяет этим заголовкам и использует их, например, чтобы формировать корректные URL в редиректах.
## Прокси с функцией удаления префикса пути { #proxy-with-a-stripped-path-prefix }
Прокси может добавлять к вашему приложению префикс пути (размещать приложение по пути с дополнительным префиксом).
В таких случаях вы можете использовать `root_path` для настройки приложения.
Механизм `root_path` определён спецификацией ASGI (на которой построен FastAPI, через Starlette).
`root_path` используется для обработки таких специфических случаев.
Он также используется внутри при монтировании вложенных приложений.
Прокси с функцией удаления префикса пути в этом случае означает, что вы объявляете путь `/app` в коде, а затем добавляете сверху слой (прокси), который размещает ваше приложение FastAPI под путём вида `/api/v1`.
Тогда исходный путь `/app` фактически будет обслуживаться по адресу `/api/v1/app`.
Хотя весь ваш код написан с расчётом, что путь один — `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
Прокси будет «обрезать» префикс пути на лету перед передачей запроса на сервер приложения (скорее всего Uvicorn, запущенный через FastAPI CLI), поддерживая у вашего приложения иллюзию, что его обслуживают по `/app`, чтобы вам не пришлось менять весь код и добавлять префикс `/api/v1`.
До этого момента всё будет работать как обычно.
Но когда вы откроете встроенный интерфейс документации (фронтенд), он будет ожидать получить схему OpenAPI по адресу `/openapi.json`, а не `/api/v1/openapi.json`.
Поэтому фронтенд (который работает в браузере) попытается обратиться к `/openapi.json` и не сможет получить схему OpenAPI.
Так как для нашего приложения используется прокси с префиксом пути `/api/v1`, фронтенду нужно забирать схему OpenAPI по `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Совет
IP `0.0.0.0` обычно означает, что программа слушает на всех IP‑адресах, доступных на этой машине/сервере.
///
Интерфейсу документации также нужна схема OpenAPI, в которой будет указано, что этот API `server` находится по пути `/api/v1` (за прокси). Например:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Здесь ещё что-то
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Здесь ещё что-то
}
}
```
В этом примере «Proxy» может быть, например, Traefik. А сервером будет что‑то вроде FastAPI CLI с Uvicorn, на котором запущено ваше приложение FastAPI.
### Указание `root_path` { #providing-the-root-path }
Для этого используйте опцию командной строки `--root-path`, например так:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Если вы используете Hypercorn, у него тоже есть опция `--root-path`.
/// note | Технические детали
Спецификация ASGI определяет `root_path` для такого случая.
А опция командной строки `--root-path` передаёт этот `root_path`.
///
### Проверка текущего `root_path` { #checking-the-current-root-path }
Вы можете получить текущий `root_path`, используемый вашим приложением для каждого запроса, — он входит в словарь `scope` (часть спецификации ASGI).
Здесь мы добавляем его в сообщение лишь для демонстрации.
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
Затем, если вы запустите Uvicorn так:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Ответ будет примерно таким:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Установка `root_path` в приложении FastAPI { #setting-the-root-path-in-the-fastapi-app }
Если нет возможности передать опцию командной строки `--root-path` (или аналог), вы можете указать параметр `root_path` при создании приложения FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
Передача `root_path` в `FastAPI` эквивалентна опции командной строки `--root-path` для Uvicorn или Hypercorn.
### О `root_path` { #about-root-path }
Учтите, что сервер (Uvicorn) не использует `root_path` ни для чего, кроме как передать его в приложение.
Если вы откроете в браузере <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/api/v1/app`.
Uvicorn ожидает, что прокси обратится к нему по `http://127.0.0.1:8000/app`, а уже задача прокси — добавить сверху префикс `/api/v1`.
## О прокси с урезанным префиксом пути { #about-proxies-with-a-stripped-path-prefix }
Помните, что прокси с урезанным префиксом пути — лишь один из вариантов настройки.
Во многих случаях по умолчанию прокси будет без урезанного префикса пути.
В таком случае (без урезанного префикса) прокси слушает, например, по адресу `https://myawesomeapp.com`, и если браузер идёт на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (например, Uvicorn) слушает на `http://127.0.0.1:8000`, то прокси (без урезанного префикса) обратится к Uvicorn по тому же пути: `http://127.0.0.1:8000/api/v1/app`.
## Локальное тестирование с Traefik { #testing-locally-with-traefik }
Вы можете легко поэкспериментировать локально с урезанным префиксом пути, используя <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Скачайте Traefik</a> — это один бинарный файл; распакуйте архив и запустите его прямо из терминала.
Затем создайте файл `traefik.toml` со следующим содержимым:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Это говорит Traefik слушать порт 9999 и использовать другой файл `routes.toml`.
/// tip | Совет
Мы используем порт 9999 вместо стандартного HTTP‑порта 80, чтобы не нужно было запускать с правами администратора (`sudo`).
///
Теперь создайте второй файл `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Этот файл настраивает Traefik на использование префикса пути `/api/v1`.
Далее Traefik будет проксировать запросы на ваш Uvicorn, работающий на `http://127.0.0.1:8000`.
Теперь запустите Traefik:
<div class="termy">
```console
$ ./traefik --configFile=traefik.toml
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
```
</div>
И запустите приложение с опцией `--root-path`:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Проверьте ответы { #check-the-responses }
Теперь, если вы перейдёте на 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"
}
```
/// tip | Совет
Обратите внимание, что хотя вы обращаетесь по `http://127.0.0.1:8000/app`, в ответе указан `root_path` равный `/api/v1`, взятый из опции `--root-path`.
///
А теперь откройте URL с портом Traefik и префиксом пути: <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>.
Мы получим тот же ответ:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
но уже по URL с префиксом, который добавляет прокси: `/api/v1`.
Разумеется, задумывается, что все будут обращаться к приложению через прокси, поэтому вариант с префиксом пути `/api/v1` является «правильным».
А вариант без префикса (`http://127.0.0.1:8000/app`), выдаваемый напрямую Uvicorn, предназначен исключительно для того, чтобы прокси (Traefik) мог к нему обращаться.
Это демонстрирует, как прокси (Traefik) использует префикс пути и как сервер (Uvicorn) использует `root_path`, переданный через опцию `--root-path`.
### Проверьте интерфейс документации { #check-the-docs-ui }
А вот самое интересное. ✨
«Официальный» способ доступа к приложению — через прокси с заданным префиксом пути. Поэтому, как и ожидается, если открыть интерфейс документации, отдаваемый напрямую Uvicorn, без префикса пути в 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`, чтобы создать в OpenAPI сервер по умолчанию с URL из `root_path`.
## Дополнительные серверы { #additional-servers }
/// warning | Предупреждение
Это более продвинутый сценарий. Можно пропустить.
///
По умолчанию FastAPI создаёт в схеме OpenAPI `server` с URL из `root_path`.
Но вы также можете указать дополнительные `servers`, например, если хотите, чтобы один и тот же интерфейс документации работал и со <abbr title="«промежуточное» или «предпродакшн» окружение">стейджингом</abbr>, и с продакшн.
Если вы передадите свой список `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": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Здесь ещё что-то
}
}
```
/// tip | Совет
Обратите внимание на автоматически добавленный сервер с `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">
/// tip | Совет
Интерфейс документации будет взаимодействовать с сервером, который вы выберете.
///
### Отключить автоматическое добавление сервера из `root_path` { #disable-automatic-server-from-root-path }
Если вы не хотите, чтобы FastAPI добавлял автоматический сервер, используя `root_path`, укажите параметр `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
и тогда этот сервер не будет добавлен в схему OpenAPI.
## Монтирование вложенного приложения { #mounting-a-sub-application }
Если вам нужно смонтировать вложенное приложение (как описано в [Вложенные приложения — монтирование](sub-applications.md){.internal-link target=_blank}), и при этом вы используете прокси с `root_path`, делайте это обычным образом — всё будет работать, как ожидается.
FastAPI умно использует `root_path` внутри, так что всё просто работает. ✨

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

@ -0,0 +1,312 @@
# Кастомные ответы — HTML, поток, файл и другие { #custom-response-html-stream-file-others }
По умолчанию **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` { #use-orjsonresponse }
Например, если вы выжимаете максимум производительности, вы можете установить и использовать <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> и задать ответ как `ORJSONResponse`.
Импортируйте класс (подкласс) `Response`, который вы хотите использовать, и объявите его в декораторе операции пути.
Для больших ответов возвращать `Response` напрямую значительно быстрее, чем возвращать словарь.
Это потому, что по умолчанию FastAPI проверяет каждый элемент внутри и убеждается, что он сериализуем в JSON, используя тот же [JSON Compatible Encoder](../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-response }
Чтобы вернуть ответ с 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` { #return-a-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` { #document-in-openapi-and-override-response }
Если вы хотите переопределить ответ внутри функции, но при этом задокументировать «тип содержимого» в OpenAPI, вы можете использовать параметр `response_class` И вернуть объект `Response`.
Тогда `response_class` будет использоваться только для документирования *операции пути* в OpenAPI, а ваш `Response` будет использован как есть.
#### Вернуть `HTMLResponse` напрямую { #return-an-htmlresponse-directly }
Например, это может быть что-то вроде:
{* ../../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">
## Доступные ответы { #available-responses }
Ниже перечислены некоторые доступные классы ответов.
Учтите, что вы можете использовать `Response`, чтобы вернуть что угодно ещё, или даже создать собственный подкласс.
/// note | Технические детали
Вы также могли бы использовать `from starlette.responses import HTMLResponse`.
**FastAPI** предоставляет те же `starlette.responses` как `fastapi.responses` для вашего удобства как разработчика. Но большинство доступных классов ответов приходят непосредственно из Starlette.
///
### `Response` { #response }
Базовый класс `Response`, от него наследуются все остальные ответы.
Его можно возвращать напрямую.
Он принимает следующие параметры:
- `content``str` или `bytes`.
- `status_code` — целое число, HTTP статус-код.
- `headers` — словарь строк.
- `media_type` — строка, задающая тип содержимого. Например, `"text/html"`.
FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Также будет добавлен заголовок Content-Type, основанный на `media_type` и с добавлением charset для текстовых типов.
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Принимает текст или байты и возвращает HTML-ответ, как описано выше.
### `PlainTextResponse` { #plaintextresponse }
Принимает текст или байты и возвращает ответ в виде простого текста.
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Принимает данные и возвращает ответ, кодированный как `application/json`.
Это ответ по умолчанию, используемый в **FastAPI**, как было сказано выше.
### `ORJSONResponse` { #orjsonresponse }
Быстрая альтернативная реализация JSON-ответа с использованием <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, как было сказано выше.
/// info | Информация
Требуется установка `orjson`, например командой `pip install orjson`.
///
### `UJSONResponse` { #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` { #redirectresponse }
Возвращает HTTP-редирект. По умолчанию использует статус-код 307 (Temporary Redirect — временное перенаправление).
Вы можете вернуть `RedirectResponse` напрямую:
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
---
Или можно использовать его в параметре `response_class`:
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
Если вы сделаете так, то сможете возвращать URL напрямую из своей функции-обработчика пути.
В этом случае будет использован статус-код по умолчанию для `RedirectResponse`, то есть `307`.
---
Также вы можете использовать параметр `status_code` в сочетании с параметром `response_class`:
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа потоково.
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
#### Использование `StreamingResponse` с файлоподобными объектами { #using-streamingresponse-with-file-like-objects }
Если у вас есть <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">файлоподобный</a> объект (например, объект, возвращаемый `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` { #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] *}
В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.
## Пользовательский класс ответа { #custom-response-class }
Вы можете создать собственный класс ответа, унаследовавшись от `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. 😉
## Класс ответа по умолчанию { #default-response-class }
При создании экземпляра класса **FastAPI** или `APIRouter` вы можете указать, какой класс ответа использовать по умолчанию.
Параметр, который это определяет, — `default_response_class`.
В примере ниже **FastAPI** будет использовать `ORJSONResponse` по умолчанию во всех операциях пути вместо `JSONResponse`.
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
/// tip | Совет
Вы по-прежнему можете переопределять `response_class` в операциях пути, как и раньше.
///
## Дополнительная документация { #additional-documentation }
Вы также можете объявить тип содержимого и многие другие детали в OpenAPI с помощью `responses`: [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.

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

@ -0,0 +1,95 @@
# Использование dataclasses { #using-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-in-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-in-nested-data-structures }
Вы также можете комбинировать `dataclasses` с другими аннотациями типов, чтобы создавать вложенные структуры данных.
В некоторых случаях вам всё же может понадобиться использовать версию `dataclasses` из Pydantic. Например, если у вас возникают ошибки с автоматически генерируемой документацией API.
В таком случае вы можете просто заменить стандартные `dataclasses` на `pydantic.dataclasses`, которая является полностью совместимой заменой (drop-in replacement):
{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
1. Мы по-прежнему импортируем `field` из стандартных `dataclasses`.
2. `pydantic.dataclasses` — полностью совместимая замена (drop-in replacement) для `dataclasses`.
3. Dataclass `Author` содержит список dataclass `Item`.
4. Dataclass `Author` используется в параметре `response_model`.
5. Вы можете использовать и другие стандартные аннотации типов вместе с dataclasses в качестве тела запроса.
В этом случае это список dataclass `Item`.
6. Здесь мы возвращаем словарь, содержащий `items`, который является списком dataclass.
FastAPI по-прежнему способен <abbr title="преобразование данных в формат, который можно передавать">сериализовать</abbr> данные в JSON.
7. Здесь `response_model` использует аннотацию типа — список dataclass `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), чтобы преобразовать HTTP-ответ.
Вы можете комбинировать `dataclasses` с другими аннотациями типов множеством способов, чтобы формировать сложные структуры данных.
Смотрите подсказки в коде выше, чтобы увидеть более конкретные детали.
## Узнать больше { #learn-more }
Вы также можете комбинировать `dataclasses` с другими Pydantic-моделями, наследоваться от них, включать их в свои модели и т.д.
Чтобы узнать больше, посмотрите <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">документацию Pydantic о dataclasses</a>.
## Версия { #version }
Доступно начиная с версии FastAPI `0.67.0`. 🔖

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

@ -0,0 +1,165 @@
# События lifespan { #lifespan-events }
Вы можете определить логику (код), которую нужно выполнить перед тем, как приложение начнет запускаться. Это означает, что этот код будет выполнен один раз, перед тем как приложение начнет получать HTTP-запросы.
Аналогично, вы можете определить логику (код), которую нужно выполнить, когда приложение завершает работу. В этом случае код будет выполнен один раз, после обработки, возможно, многих запросов.
Поскольку этот код выполняется до того, как приложение начинает принимать запросы, и сразу после того, как оно заканчивает их обрабатывать, он охватывает весь lifespan (жизненный цикл) приложения (слово «lifespan» станет важным через секунду 😉).
Это может быть очень полезно для настройки ресурсов, которые нужны для всего приложения, которые разделяются между запросами и/или которые нужно затем очистить. Например, пул подключений к базе данных или загрузка общей модели Машинного обучения.
## Вариант использования { #use-case }
Начнем с примера варианта использования, а затем посмотрим, как это решить.
Представим, что у вас есть несколько моделей Машинного обучения, которые вы хотите использовать для обработки запросов. 🤖
Эти же модели разделяются между запросами, то есть это не одна модель на запрос, не одна на пользователя и т.п.
Представим, что загрузка модели может занимать довольно много времени, потому что ей нужно прочитать много данных с диска. Поэтому вы не хотите делать это для каждого запроса.
Вы могли бы загрузить её на верхнем уровне модуля/файла, но это означало бы, что модель загружается даже если вы просто запускаете простой автоматический тест; тогда этот тест будет медленным, так как ему придется ждать загрузки модели перед запуском независимой части кода.
Именно это мы и решим: давайте загружать модель перед тем, как начнётся обработка запросов, но только непосредственно перед тем, как приложение начнет принимать запросы, а не во время загрузки кода.
## Lifespan { #lifespan }
Вы можете определить логику для startup и shutdown, используя параметр `lifespan` приложения `FastAPI` и «менеджер контекста» (через секунду покажу что это).
Начнем с примера, а затем разберём его подробнее.
Мы создаём асинхронную функцию `lifespan()` с `yield` примерно так:
{* ../../docs_src/events/tutorial003.py hl[16,19] *}
Здесь мы симулируем дорогую операцию startup по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен до того, как приложение начнет принимать запросы, во время startup.
А затем сразу после `yield` мы выгружаем модель. Этот код будет выполнен после того, как приложение закончит обрабатывать запросы, непосредственно перед shutdown. Это может, например, освободить ресурсы, такие как память или GPU.
/// tip | Совет
`shutdown` произойдёт, когда вы останавливаете приложение.
Возможно, вам нужно запустить новую версию, или вы просто устали от него. 🤷
///
### Функция lifespan { #lifespan-function }
Первое, на что стоит обратить внимание, — мы определяем асинхронную функцию с `yield`. Это очень похоже на Зависимости с `yield`.
{* ../../docs_src/events/tutorial003.py hl[14:19] *}
Первая часть функции, до `yield`, будет выполнена до запуска приложения.
А часть после `yield` будет выполнена после завершения работы приложения.
### Асинхронный менеджер контекста { #async-context-manager }
Если присмотреться, функция декорирована `@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] *}
## Альтернативные события (устаревшие) { #alternative-events-deprecated }
/// warning | Предупреждение
Рекомендуемый способ обрабатывать startup и shutdown — использовать параметр `lifespan` приложения `FastAPI`, как описано выше. Если вы укажете параметр `lifespan`, обработчики событий `startup` и `shutdown` больше вызываться не будут. Либо всё через `lifespan`, либо всё через события — не одновременно.
Эту часть, скорее всего, можно пропустить.
///
Есть альтернативный способ определить логику, которую нужно выполнить во время startup и во время shutdown.
Вы можете определить обработчики событий (функции), которые нужно выполнить до старта приложения или при его завершении.
Эти функции можно объявить с `async def` или обычным `def`.
### Событие `startup` { #startup-event }
Чтобы добавить функцию, которую нужно запустить до старта приложения, объявите её как обработчик события `"startup"`:
{* ../../docs_src/events/tutorial001.py hl[8] *}
В этом случае функция-обработчик события `startup` инициализирует «базу данных» items (это просто `dict`) некоторыми значениями.
Вы можете добавить более одного обработчика события.
И ваше приложение не начнет принимать запросы, пока все обработчики события `startup` не завершатся.
### Событие `shutdown` { #shutdown-event }
Чтобы добавить функцию, которую нужно запустить при завершении работы приложения, объявите её как обработчик события `"shutdown"`:
{* ../../docs_src/events/tutorial002.py hl[6] *}
Здесь функция-обработчик события `shutdown` запишет строку текста `"Application shutdown"` в файл `log.txt`.
/// info | Информация
В функции `open()` параметр `mode="a"` означает «добавление» (append), то есть строка будет добавлена в конец файла, без перезаписи предыдущего содержимого.
///
/// tip | Совет
Обратите внимание, что в этом случае мы используем стандартную Python-функцию `open()`, которая взаимодействует с файлом.
То есть это I/O (ввод/вывод), требующий «ожидания» записи на диск.
Но `open()` не использует `async` и `await`.
Поэтому мы объявляем обработчик события обычным `def` вместо `async def`.
///
### `startup` и `shutdown` вместе { #startup-and-shutdown-together }
С высокой вероятностью логика для вашего startup и shutdown связана: вы можете хотеть что-то запустить, а затем завершить, получить ресурс, а затем освободить его и т.д.
Делать это в отдельных функциях, которые не разделяют общую логику или переменные, сложнее, так как придётся хранить значения в глобальных переменных или использовать похожие приёмы.
Поэтому теперь рекомендуется использовать `lifespan`, как описано выше.
## Технические детали { #technical-details }
Немного технических подробностей для любопытных умников. 🤓
Под капотом, в 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">документации Starlette по Lifespan</a>.
Включая то, как работать с состоянием lifespan, которое можно использовать в других частях вашего кода.
///
## Подприложения { #sub-applications }
🚨 Имейте в виду, что эти события lifespan (startup и shutdown) будут выполнены только для основного приложения, а не для [Подприложения — Mounts](sub-applications.md){.internal-link target=_blank}.

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

@ -0,0 +1,208 @@
# Генерация SDK { #generating-sdks }
Поскольку **FastAPI** основан на спецификации **OpenAPI**, его API можно описать в стандартном формате, понятном множеству инструментов.
Это упрощает генерацию актуальной **документации**, клиентских библиотек (<abbr title="Software Development Kits – Наборы средств разработки">**SDKs**</abbr>) на разных языках, а также **тестирования** или **воркфлоу автоматизации**, которые остаются синхронизированными с вашим кодом.
В этом руководстве вы узнаете, как сгенерировать **TypeScript SDK** для вашего бэкенда на FastAPI.
## Генераторы SDK с открытым исходным кодом { #open-source-sdk-generators }
Гибкий вариант — <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>, который поддерживает **многие языки программирования** и умеет генерировать SDK из вашей спецификации OpenAPI.
Для **TypeScript‑клиентов** <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> — специализированное решение, обеспечивающее оптимальный опыт для экосистемы TypeScript.
Больше генераторов SDK можно найти на <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>.
/// tip | Совет
FastAPI автоматически генерирует спецификации **OpenAPI 3.1**, поэтому любой используемый инструмент должен поддерживать эту версию.
///
## Генераторы SDK от спонсоров FastAPI { #sdk-generators-from-fastapi-sponsors }
В этом разделе представлены решения с **венчурной поддержкой** и **поддержкой компаний** от компаний, которые спонсируют FastAPI. Эти продукты предоставляют **дополнительные возможности** и **интеграции** сверх высококачественно генерируемых SDK.
Благодаря ✨ [**спонсорству FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨ эти компании помогают обеспечивать, чтобы фреймворк и его **экосистема** оставались здоровыми и **устойчивыми**.
Их спонсорство также демонстрирует серьёзную приверженность **сообществу** 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.stainless.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>
Некоторые из этих решений также могут быть open source или иметь бесплатные тарифы, так что вы сможете попробовать их без финансовых затрат. Другие коммерческие генераторы SDK доступны и их можно найти онлайн. 🤓
## Создать TypeScript SDK { #create-a-typescript-sdk }
Начнём с простого приложения FastAPI:
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
Обратите внимание, что *операции пути (обработчики пути)* определяют модели, которые они используют для полезной нагрузки запроса и полезной нагрузки ответа, с помощью моделей `Item` и `ResponseMessage`.
### Документация API { #api-docs }
Если перейти на `/docs`, вы увидите **схемы** данных, отправляемых в запросах и принимаемых в ответах:
<img src="/img/tutorial/generate-clients/image01.png">
Вы видите эти схемы, потому что они были объявлены с моделями в приложении.
Эта информация доступна в **схеме OpenAPI** приложения и затем отображается в документации API.
Та же информация из моделей, включённая в OpenAPI, может использоваться для **генерации клиентского кода**.
### Hey API { #hey-api }
Как только у нас есть приложение FastAPI с моделями, мы можем использовать Hey API для генерации TypeScript‑клиента. Самый быстрый способ сделать это — через npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Это сгенерирует TypeScript SDK в `./src/client`.
Вы можете узнать, как <a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">установить `@hey-api/openapi-ts`</a> и почитать о <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">сгенерированном результате</a> на их сайте.
### Использование SDK { #using-the-sdk }
Теперь вы можете импортировать и использовать клиентский код. Это может выглядеть так, обратите внимание, что вы получаете автозавершение для методoв:
<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-app-with-tags }
Во многих случаях ваше приложение FastAPI будет больше, и вы, вероятно, будете использовать теги, чтобы разделять разные группы *операций пути*.
Например, у вас может быть раздел для **items** и другой раздел для **users**, и они могут быть разделены тегами:
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
### Генерация TypeScript‑клиента с тегами { #generate-a-typescript-client-with-tags }
Если вы генерируете клиент для приложения FastAPI с использованием тегов, обычно клиентский код также будет разделён по тегам.
Таким образом вы сможете иметь всё правильно упорядоченным и сгруппированным в клиентском коде:
<img src="/img/tutorial/generate-clients/image06.png">
В этом случае у вас есть:
* `ItemsService`
* `UsersService`
### Имена методов клиента { #client-method-names }
Сейчас сгенерированные имена методов вроде `createItemItemsPost` выглядят не очень аккуратно:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...это потому, что генератор клиента использует внутренний **ID операции** OpenAPI для каждой *операции пути*.
OpenAPI требует, чтобы каждый ID операции был уникален среди всех *операций пути*, поэтому FastAPI использует **имя функции**, **путь** и **HTTP‑метод/операцию** для генерации этого ID операции, так как таким образом можно гарантировать уникальность ID операций.
Но далее я покажу, как это улучшить. 🤓
## Пользовательские ID операций и лучшие имена методов { #custom-operation-ids-and-better-method-names }
Вы можете **изменить** способ **генерации** этих ID операций, чтобы сделать их проще, а имена методов в клиентах — **более простыми**.
В этом случае вам нужно будет обеспечить, чтобы каждый ID операции был **уникальным** другим способом.
Например, вы можете гарантировать, что у каждой *операции пути* есть тег, и затем генерировать ID операции на основе **тега** и **имени** *операции пути* (имени функции).
### Пользовательская функция генерации уникального ID { #custom-generate-unique-id-function }
FastAPI использует **уникальный ID** для каждой *операции пути*, который применяется для **ID операции**, а также для имён любых необходимых пользовательских моделей запросов или ответов.
Вы можете кастомизировать эту функцию. Она принимает `APIRoute` и возвращает строку.
Например, здесь берётся первый тег (скорее всего у вас один тег) и имя *операции пути* (имя функции).
Затем вы можете передать эту пользовательскую функцию в **FastAPI** через параметр `generate_unique_id_function`:
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
### Генерация TypeScript‑клиента с пользовательскими ID операций { #generate-a-typescript-client-with-custom-operation-ids }
Теперь, если снова сгенерировать клиент, вы увидите, что имена методов улучшились:
<img src="/img/tutorial/generate-clients/image07.png">
Как видите, теперь имена методов содержат тег, а затем имя функции; больше они не включают информацию из URL‑пути и HTTP‑операции.
### Предобработка спецификации OpenAPI для генератора клиента { #preprocess-the-openapi-specification-for-the-client-generator }
Сгенерированном коде всё ещё есть **дублирующаяся информация**.
Мы уже знаем, что этот метод относится к **items**, потому что это слово есть в `ItemsService` (взято из тега), но при этом имя тега всё ещё добавлено префиксом к имени метода. 😕
Скорее всего мы захотим оставить это в OpenAPI в целом, так как это гарантирует, что ID операций будут **уникальны**.
Но для сгенерированного клиента мы можем **модифицировать** ID операций OpenAPI непосредственно перед генерацией клиентов, чтобы сделать имена методов более приятными и **чистыми**.
Мы можем скачать OpenAPI JSON в файл `openapi.json`, а затем **убрать этот префикс‑тег** таким скриптом:
{* ../../docs_src/generate_clients/tutorial004.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
После этого ID операций будут переименованы с чего‑то вроде `items-get_items` просто в `get_items`, и генератор клиента сможет создавать более простые имена методов.
### Генерация TypeScript‑клиента с предобработанным OpenAPI { #generate-a-typescript-client-with-the-preprocessed-openapi }
Так как конечный результат теперь в файле `openapi.json`, нужно обновить входное расположение:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
После генерации нового клиента у вас будут **чистые имена методов**, со всем **автозавершением**, **ошибками прямо в редакторе** и т.д.:
<img src="/img/tutorial/generate-clients/image08.png">
## Преимущества { #benefits }
При использовании автоматически сгенерированных клиентов вы получите **автозавершение** для:
* Методов.
* Данных запроса — в теле запроса, query‑параметрах и т.д.
* Данных ответа.
У вас также будут **ошибки прямо в редакторе** для всего.
И каждый раз, когда вы обновляете код бэкенда и **перегенерируете** фронтенд, в нём появятся новые *операции пути* как методы, старые будут удалены, а любые другие изменения отразятся в сгенерированном коде. 🤓
Это также означает, что если что‑то изменилось, это будет **отражено** в клиентском коде автоматически. И если вы **соберёте** клиент, он завершится с ошибкой, если где‑то есть **несоответствие** в используемых данных.
Таким образом, вы **обнаружите многие ошибки** очень рано в цикле разработки, вместо того чтобы ждать, когда ошибки проявятся у конечных пользователей в продакшн, и затем пытаться отладить, в чём проблема. ✨

97
docs/ru/docs/advanced/middleware.md

@ -0,0 +1,97 @@
# Расширенное использование middleware { #advanced-middleware }
В основном руководстве вы читали, как добавить [пользовательское middleware](../tutorial/middleware.md){.internal-link target=_blank} в ваше приложение.
А затем — как работать с [CORS с помощью `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
В этом разделе посмотрим, как использовать другие middleware.
## Добавление ASGI middleware { #adding-asgi-middlewares }
Так как **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 { #integrated-middlewares }
**FastAPI** включает несколько middleware для распространённых сценариев. Ниже рассмотрим, как их использовать.
/// note | Технические детали
В следующих примерах вы также можете использовать `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** предоставляет несколько middleware в `fastapi.middleware` для удобства разработчика. Но большинство доступных middleware приходит напрямую из Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Гарантирует, что все входящие запросы должны использовать либо `https`, либо `wss`.
Любой входящий запрос по `http` или `ws` будет перенаправлен на безопасную схему.
{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Гарантирует, что во всех входящих запросах корректно установлен `Host`‑заголовок, чтобы защититься от атак на HTTP‑заголовок Host.
{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
Поддерживаются следующие аргументы:
- `allowed_hosts` — список доменных имён, которые следует разрешить как имена хостов. Подстановки вида `*.example.com` поддерживаются для сопоставления поддоменов. Чтобы разрешить любой хост, используйте либо `allowed_hosts=["*"]`, либо не добавляйте это middleware.
- `www_redirect` — если установлено в True, запросы к не‑www версиям разрешённых хостов будут перенаправляться на их www‑аналоги. По умолчанию — `True`.
Если входящий запрос не проходит валидацию, будет отправлен ответ `400`.
## `GZipMiddleware` { #gzipmiddleware }
Обрабатывает GZip‑ответы для любых запросов, которые включают `"gzip"` в заголовке `Accept-Encoding`.
Это middleware обрабатывает как обычные, так и потоковые ответы.
{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
Поддерживаются следующие аргументы:
- `minimum_size` — не сжимать GZip‑ом ответы, размер которых меньше этого минимального значения в байтах. По умолчанию — `500`.
- `compresslevel` — уровень GZip‑сжатия. Целое число от 1 до 9. По умолчанию — `9`. Более низкое значение — быстреее сжатие, но больший размер файла; более высокое значение — более медленное сжатие, но меньший размер файла.
## Другие middleware { #other-middlewares }
Существует много других 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">документацию по middleware в Starlette</a> и <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">список ASGI Awesome</a>.

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

@ -0,0 +1,186 @@
# Обратные вызовы в OpenAPI { #openapi-callbacks }
Вы можете создать API с *операцией пути* (обработчиком пути), которая будет инициировать HTTP-запрос к *внешнему API*, созданному кем-то другим (скорее всего тем же разработчиком, который будет использовать ваш API).
Процесс, происходящий, когда ваше приложение API обращается к *внешнему API*, называется «callback» (обратный вызов). Программное обеспечение, написанное внешним разработчиком, отправляет HTTP-запрос вашему API, а затем ваш API выполняет обратный вызов, отправляя HTTP-запрос во *внешний API* (который, вероятно, тоже создал тот же разработчик).
В этом случае вам может понадобиться задокументировать, как должно выглядеть это внешнее API: какую *операцию пути* оно должно иметь, какое тело запроса ожидать, какой ответ возвращать и т.д.
## Приложение с обратными вызовами { #an-app-with-callbacks }
Давайте рассмотрим это на примере.
Представьте, что вы разрабатываете приложение, позволяющее создавать счета.
Эти счета будут иметь `id`, `title` (необязательный), `customer` и `total`.
Пользователь вашего API (внешний разработчик) создаст счет в вашем API с помощью POST-запроса.
Затем ваш API (предположим) сделает следующее:
* Отправит счет клиенту внешнего разработчика.
* Получит оплату.
* Отправит уведомление обратно пользователю API (внешнему разработчику).
* Это будет сделано отправкой POST-запроса (из *вашего API*) в *внешний API*, предоставленный этим внешним разработчиком (это и есть «callback»).
## Обычное приложение **FastAPI** { #the-normal-fastapi-app }
Сначала посмотрим, как будет выглядеть обычное приложение API до добавления обратного вызова.
В нём будет *операция пути*, которая получит тело запроса `Invoice`, и query-параметр `callback_url`, содержащий URL для обратного вызова.
Эта часть вполне обычна, большая часть кода вам уже знакома:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
/// tip | Совет
Query-параметр `callback_url` использует тип Pydantic <a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a>.
///
Единственное новое — это `callbacks=invoices_callback_router.routes` в качестве аргумента *декоратора операции пути*. Далее разберёмся, что это такое.
## Документирование обратного вызова { #documenting-the-callback }
Реальный код обратного вызова будет сильно зависеть от вашего приложения API.
И, вероятно, он будет заметно отличаться от одного приложения к другому.
Это могут быть буквально одна-две строки кода, например:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Но, возможно, самая важная часть обратного вызова — это убедиться, что пользователь вашего API (внешний разработчик) правильно реализует *внешний API* в соответствии с данными, которые *ваш API* будет отправлять в теле запроса обратного вызова и т.п.
Поэтому далее мы добавим код, документирующий, как должен выглядеть этот *внешний API*, чтобы получать обратный вызов от *вашего API*.
Эта документация отобразится в Swagger UI по адресу `/docs` в вашем API и позволит внешним разработчикам понять, как построить *внешний API*.
В этом примере сам обратный вызов не реализуется (это может быть всего одна строка кода), реализуется только часть с документацией.
/// tip | Совет
Сам обратный вызов — это всего лишь HTTP-запрос.
Реализуя обратный вызов, вы можете использовать, например, <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>.
///
## Напишите код документации обратного вызова { #write-the-callback-documentation-code }
Этот код не будет выполняться в вашем приложении, он нужен только для *документирования* того, как должен выглядеть *внешний API*.
Но вы уже знаете, как легко получить автоматическую документацию для API с **FastAPI**.
Мы используем те же знания, чтобы задокументировать, как должен выглядеть *внешний API*... создав *операции пути*, которые внешний API должен реализовать (те, которые ваш API будет вызывать).
/// tip | Совет
Когда вы пишете код для документирования обратного вызова, полезно представить, что вы — тот самый *внешний разработчик*. И что вы сейчас реализуете *внешний API*, а не *свой API*.
Временное принятие этой точки зрения (внешнего разработчика) поможет интуитивно понять, куда поместить параметры, какую Pydantic-модель использовать для тела запроса, для ответа и т.д. во *внешнем API*.
///
### Создайте `APIRouter` для обратного вызова { #create-a-callback-apirouter }
Сначала создайте новый `APIRouter`, который будет содержать один или несколько обратных вызовов.
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
### Создайте *операцию пути* для обратного вызова { #create-the-callback-path-operation }
Чтобы создать *операцию пути* для обратного вызова, используйте тот же `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> (подробнее ниже), где можно использовать переменные с параметрами и части исходного HTTP-запроса, отправленного *вашему API*.
### Выражение пути для обратного вызова { #the-callback-path-expression }
*Путь* обратного вызова может содержать <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 (внешний разработчик) отправляет HTTP-запрос вашему API по адресу:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
с телом JSON:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
то *ваш API* обработает счёт и, в какой-то момент позже, отправит запрос обратного вызова на `callback_url` (*внешний API*):
```
https://www.external.org/events/invoices/2expen51ve
```
с телом JSON примерно такого вида:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
и будет ожидать от *внешнего API* ответ с телом JSON вида:
```JSON
{
"ok": true
}
```
/// tip | Совет
Обратите внимание, что используемый URL обратного вызова содержит URL, полученный как query-параметр в `callback_url` (`https://www.external.org/events`), а также `id` счёта из тела JSON (`2expen51ve`).
///
### Подключите маршрутизатор обратного вызова { #add-the-callback-router }
К этому моменту у вас есть необходимые *операции пути* обратного вызова (те, которые *внешний разработчик* должен реализовать во *внешнем API*) в созданном выше маршрутизаторе обратных вызовов.
Теперь используйте параметр `callbacks` в *декораторе операции пути вашего API*, чтобы передать атрибут `.routes` (это, по сути, просто `list` маршрутов/*операций пути*) из этого маршрутизатора обратных вызовов:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
/// tip | Совет
Обратите внимание, что вы передаёте не сам маршрутизатор (`invoices_callback_router`) в `callback=`, а его атрибут `.routes`, то есть `invoices_callback_router.routes`.
///
### Проверьте документацию { #check-the-docs }
Теперь вы можете запустить приложение и перейти по адресу <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 { #openapi-webhooks }
Бывают случаи, когда вы хотите сообщить пользователям вашего API, что ваше приложение может вызвать их приложение (отправив HTTP-запрос) с некоторыми данными, обычно чтобы уведомить о каком-то событии.
Это означает, что вместо обычного процесса, когда пользователи отправляют запросы вашему API, ваш API (или ваше приложение) может отправлять запросы в их систему (в их API, их приложение).
Обычно это называется вебхуком.
## Шаги вебхуков { #webhooks-steps }
Обычно процесс таков: вы определяете в своем коде, какое сообщение вы будете отправлять, то есть тело запроса.
Вы также определяете, в какие моменты (при каких событиях) ваше приложение будет отправлять эти запросы.
А ваши пользователи каким-то образом (например, в веб‑панели) указывают URL-адрес, на который ваше приложение должно отправлять эти запросы.
Вся логика регистрации URL-адресов для вебхуков и код, который реально отправляет эти запросы, целиком на вашей стороне. Вы пишете это так, как вам нужно, в своем собственном коде.
## Документирование вебхуков с помощью FastAPI и OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
С FastAPI, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP-операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), а также тела запросов, которые ваше приложение будет отправлять.
Это значительно упростит вашим пользователям реализацию их API для приема ваших вебхук-запросов; возможно, они даже смогут автоматически сгенерировать часть кода своего API.
/// info | Информация
Вебхуки доступны в OpenAPI 3.1.0 и выше, поддерживаются в FastAPI `0.99.0` и новее.
///
## Приложение с вебхуками { #an-app-with-webhooks }
При создании приложения на **FastAPI** есть атрибут `webhooks`, с помощью которого можно объявлять вебхуки так же, как вы объявляете операции пути (обработчики пути), например с `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
Определенные вами вебхуки попадут в схему **OpenAPI** и в автоматический **интерфейс документации**.
/// info | Информация
Объект `app.webhooks` на самом деле — это обычный `APIRouter`, тот же тип, который вы используете при структурировании приложения по нескольким файлам.
///
Обратите внимание: в случае с вебхуками вы на самом деле не объявляете путь (например, `/items/`), передаваемый туда текст — это лишь идентификатор вебхука (имя события). Например, в `@app.webhooks.post("new-subscription")` имя вебхука — `new-subscription`.
Это связано с тем, что предполагается: фактический URL‑путь, по которому они хотят получать запрос вебхука, ваши пользователи укажут каким-то другим образом (например, в веб‑панели).
### Посмотрите документацию { #check-the-docs }
Теперь вы можете запустить приложение и перейти по ссылке <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 @@
# Расширенная конфигурация операций пути { #path-operation-advanced-configuration }
## OpenAPI operationId { #openapi-operationid }
/// warning | Предупреждение
Если вы не «эксперт» по OpenAPI, скорее всего, это вам не нужно.
///
Вы можете задать OpenAPI `operationId`, который будет использоваться в вашей *операции пути*, с помощью параметра `operation_id`.
Нужно убедиться, что он уникален для каждой операции.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
### Использование имени функции-обработчика пути как operationId { #using-the-path-operation-function-name-as-the-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 { #exclude-from-openapi }
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматической документации), используйте параметр `include_in_schema` и установите его в `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
## Расширенное описание из docstring { #advanced-description-from-docstring }
Вы можете ограничить количество строк из docstring *функции-обработчика пути*, используемых для OpenAPI.
Добавление `\f` (экранированного символа «form feed») заставит **FastAPI** обрезать текст, используемый для OpenAPI, в этой точке.
Эта часть не попадёт в документацию, но другие инструменты (например, Sphinx) смогут использовать остальное.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
## Дополнительные ответы { #additional-responses }
Вы, вероятно, уже видели, как объявлять `response_model` и `status_code` для *операции пути*.
Это определяет метаданные об основном ответе *операции пути*.
Также можно объявлять дополнительные ответы с их моделями, статус-кодами и т.д.
В документации есть целая глава об этом — [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
## Дополнительные данные OpenAPI { #openapi-extra }
Когда вы объявляете *операцию пути* в своём приложении, **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-extensions }
`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 для операции пути { #custom-openapi-path-operation-schema }
Словарь в `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 { #custom-openapi-content-type }
Используя тот же приём, вы можете воспользоваться Pydantic-моделью, чтобы определить JSON Schema, которая затем будет включена в пользовательский раздел схемы OpenAPI для *операции пути*.
И вы можете сделать это, даже если тип данных в запросе — не JSON.
Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей 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 Schema модели назывался `Item.schema()`, в Pydantic версии 2 метод называется `Item.model_json_schema()`.
///
Тем не менее, хотя мы не используем встроенную функциональность по умолчанию, мы всё равно используем Pydantic-модель, чтобы вручную сгенерировать JSON Schema для данных, которые мы хотим получить в 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-headers }
## Использовать параметр `Response` { #use-a-response-parameter }
Вы можете объявить параметр типа `Response` в вашей функции-обработчике пути (как можно сделать и для cookie).
А затем вы можете устанавливать HTTP-заголовки в этом *временном* объекте ответа.
{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
После этого вы можете вернуть любой нужный объект, как обычно (например, `dict`, модель из базы данных и т.д.).
И, если вы объявили `response_model`, он всё равно будет использован для фильтрации и преобразования возвращённого объекта.
**FastAPI** использует этот *временный* ответ, чтобы извлечь HTTP-заголовки (а также cookie и статус-код) и поместит их в финальный HTTP-ответ, который содержит возвращённое вами значение, отфильтрованное согласно `response_model`.
Вы также можете объявлять параметр `Response` в зависимостях и устанавливать в них заголовки (и cookie).
## Вернуть `Response` напрямую { #return-a-response-directly }
Вы также можете добавить HTTP-заголовки, когда возвращаете `Response` напрямую.
Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}, и передайте заголовки как дополнительный параметр:
{* ../../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` часто используется для установки заголовков и cookie, **FastAPI** также предоставляет его как `fastapi.Response`.
///
## Пользовательские HTTP-заголовки { #custom-headers }
Помните, что собственные проприетарные заголовки можно добавлять, <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 Basic Auth приложение ожидает HTTP-заголовок, который содержит имя пользователя и пароль.
Если его нет, возвращается ошибка HTTP 401 «Unauthorized».
Также возвращается заголовок `WWW-Authenticate` со значением `Basic` и необязательным параметром `realm`.
Это говорит браузеру показать встроенное окно запроса имени пользователя и пароля.
Затем, когда вы вводите эти данные, браузер автоматически отправляет их в заголовке.
## Простой HTTP Basic Auth { #simple-http-basic-auth }
* Импортируйте `HTTPBasic` и `HTTPBasicCredentials`.
* Создайте «схему» `security` с помощью `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">
## Проверка имени пользователя { #check-the-username }
Вот более полный пример.
Используйте зависимость, чтобы проверить, корректны ли имя пользователя и пароль.
Для этого используйте стандартный модуль 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"):
# Вернуть ошибку
...
```
Но используя `secrets.compare_digest()`, вы защитите код от атак типа «тайминговая атака» (атака по времени).
### Тайминговые атаки { #timing-attacks }
Что такое «тайминговая атака»?
Представим, что злоумышленники пытаются угадать имя пользователя и пароль.
И они отправляют запрос с именем пользователя `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`, прежде чем понять, что строки отличаются. Поэтому на ответ «Неверное имя пользователя или пароль» уйдёт на несколько микросекунд больше.
#### Время ответа помогает злоумышленникам { #the-time-to-answer-helps-the-attackers }
Замечая, что сервер прислал «Неверное имя пользователя или пароль» на несколько микросекунд позже, злоумышленники поймут, что какая-то часть была угадана — начальные буквы верны.
Тогда они могут попробовать снова, зная, что правильнее что-то ближе к `stanleyjobsox`, чем к `johndoe`.
#### «Профессиональная» атака { #a-professional-attack }
Конечно, злоумышленники не будут делать всё это вручную — они напишут программу, возможно, с тысячами или миллионами попыток в секунду. И будут подбирать по одной дополнительной верной букве за раз.
Так за минуты или часы они смогут угадать правильные имя пользователя и пароль — с «помощью» нашего приложения — используя лишь время, затраченное на ответ.
#### Исправление с помощью `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Но в нашем коде мы используем `secrets.compare_digest()`.
Вкратце: сравнение `stanleyjobsox` с `stanleyjobson` займёт столько же времени, сколько и сравнение `johndoe` с `stanleyjobson`. То же относится и к паролю.
Таким образом, используя `secrets.compare_digest()` в коде приложения, вы защитите его от всего этого класса атак на безопасность.
### Возврат ошибки { #return-the-error }
После того как обнаружено, что учётные данные некорректны, верните `HTTPException` со статус-кодом ответа 401 (тем же, что и при отсутствии учётных данных) и добавьте HTTP-заголовок `WWW-Authenticate`, чтобы браузер снова показал окно входа:
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}

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

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

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

@ -0,0 +1,274 @@
# OAuth2 scopes { #oauth2-scopes }
Вы можете использовать OAuth2 scopes (scope - область, рамки) напрямую с **FastAPI** — они интегрированы и работают бесшовно.
Это позволит вам иметь более детальную систему разрешений по стандарту OAuth2, интегрированную в ваше OpenAPI‑приложение (и документацию API).
OAuth2 со scopes — это механизм, который используют многие крупные провайдеры аутентификации: Facebook, Google, GitHub, Microsoft, X (Twitter) и т.д. Они применяют его, чтобы предоставлять конкретные разрешения пользователям и приложениям.
Каждый раз, когда вы «входите через» Facebook, Google, GitHub, Microsoft, X (Twitter), это приложение использует OAuth2 со scopes.
В этом разделе вы увидите, как управлять аутентификацией и авторизацией с теми же OAuth2 scopes в вашем приложении на **FastAPI**.
/// warning | Предупреждение
Это более-менее продвинутый раздел. Если вы только начинаете, можете пропустить его.
Вам не обязательно нужны OAuth2 scopes — аутентификацию и авторизацию можно реализовать любым нужным вам способом.
Но OAuth2 со scopes можно красиво интегрировать в ваш API (через OpenAPI) и документацию API.
Так или иначе, вы все равно будете применять эти scopes или какие-то другие требования безопасности/авторизации, как вам нужно, в вашем коде.
Во многих случаях OAuth2 со scopes может быть избыточным.
Но если вы знаете, что это нужно, или вам просто интересно — продолжайте чтение.
///
## OAuth2 scopes и OpenAPI { #oauth2-scopes-and-openapi }
Спецификация OAuth2 определяет «scopes» как список строк, разделённых пробелами.
Содержимое каждой такой строки может иметь любой формат, но не должно содержать пробелов.
Эти scopes представляют «разрешения».
В OpenAPI (например, в документации API) можно определить «схемы безопасности» (security schemes).
Когда одна из таких схем безопасности использует OAuth2, вы также можете объявлять и использовать scopes.
Каждый «scope» — это просто строка (без пробелов).
Обычно они используются для объявления конкретных разрешений безопасности, например:
- `users:read` или `users:write` — распространённые примеры.
- `instagram_basic` используется Facebook / Instagram.
- `https://www.googleapis.com/auth/drive` используется Google.
/// info | Информация
В OAuth2 «scope» — это просто строка, объявляющая требуемое конкретное разрешение.
Неважно, есть ли там другие символы, такие как `:`, или это URL.
Эти детали зависят от реализации.
Для OAuth2 это просто строки.
///
## Взгляд издалека { #global-view }
Сначала быстро посмотрим, что изменилось по сравнению с примерами из основного раздела **Учебник - Руководство пользователя** — [OAuth2 с паролем (и хешированием), Bearer с JWT-токенами](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Теперь — с использованием OAuth2 scopes:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Теперь рассмотрим эти изменения шаг за шагом.
## OAuth2 схема безопасности { #oauth2-security-scheme }
Первое изменение — мы объявляем схему безопасности OAuth2 с двумя доступными scopes: `me` и `items`.
Параметр `scopes` получает `dict`, где каждый scope — это ключ, а описание — значение:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Так как теперь мы объявляем эти scopes, они появятся в документации API при входе/авторизации.
И вы сможете выбрать, какие scopes вы хотите выдать доступ: `me` и `items`.
Это тот же механизм, когда вы даёте разрешения при входе через Facebook, Google, GitHub и т.д.:
<img src="/img/tutorial/security/image11.png">
## JWT-токены со scopes { #jwt-token-with-scopes }
Теперь измените операцию пути, выдающую токен, чтобы возвращать запрошенные scopes.
Мы всё ещё используем тот же `OAuth2PasswordRequestForm`. Он включает свойство `scopes` с `list` из `str` — каждый scope, полученный в запросе.
И мы возвращаем scopes как часть JWT‑токена.
/// danger | Опасность
Для простоты здесь мы просто добавляем полученные scopes прямо в токен.
Но в вашем приложении, в целях безопасности, следует убедиться, что вы добавляете только те scopes, которые пользователь действительно может иметь, или те, которые вы заранее определили.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Объявление scopes в *обработчиках путей* и зависимостях { #declare-scopes-in-path-operations-and-dependencies }
Теперь объявим, что операция пути для `/users/me/items/` требует scope `items`.
Для этого импортируем и используем `Security` из `fastapi`.
Вы можете использовать `Security` для объявления зависимостей (как `Depends`), но `Security` также принимает параметр `scopes` со списком scopes (строк).
В этом случае мы передаём функцию‑зависимость `get_current_active_user` в `Security` (точно так же, как сделали бы с `Depends`).
Но мы также передаём `list` scopes — в данном случае только один scope: `items` (их могло быть больше).
И функция‑зависимость `get_current_active_user` тоже может объявлять подзависимости не только через `Depends`, но и через `Security`, объявляя свою подзависимость (`get_current_user`) и дополнительные требования по scopes.
В данном случае требуется scope `me` (их также могло быть больше одного).
/// note | Примечание
Вам не обязательно добавлять разные scopes в разных местах.
Мы делаем это здесь, чтобы показать, как **FastAPI** обрабатывает scopes, объявленные на разных уровнях.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Технические детали
`Security` на самом деле является подклассом `Depends` и имеет всего один дополнительный параметр, который мы рассмотрим позже.
Но используя `Security` вместо `Depends`, **FastAPI** будет знать, что можно объявлять security scopes, использовать их внутри и документировать API в OpenAPI.
Однако когда вы импортируете `Query`, `Path`, `Depends`, `Security` и другие из `fastapi`, это на самом деле функции, возвращающие специальные классы.
///
## Использование `SecurityScopes` { #use-securityscopes }
Теперь обновим зависимость `get_current_user`.
Именно её используют зависимости выше.
Здесь мы используем ту же схему OAuth2, созданную ранее, объявляя её как зависимость: `oauth2_scheme`.
Поскольку у этой функции‑зависимости нет собственных требований по scopes, мы можем использовать `Depends` с `oauth2_scheme` — нам не нужно использовать `Security`, если не требуется указывать security scopes.
Мы также объявляем специальный параметр типа `SecurityScopes`, импортированный из `fastapi.security`.
Класс `SecurityScopes` похож на `Request` (через `Request` мы получали сам объект запроса).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Использование `scopes` { #use-the-scopes }
Параметр `security_scopes` будет типа `SecurityScopes`.
У него есть свойство `scopes` со списком, содержащим все scopes, требуемые им самим и всеми зависимостями, использующими его как подзависимость. То есть всеми «зависящими»… это может звучать запутанно, ниже есть дополнительное объяснение.
Объект `security_scopes` (класс `SecurityScopes`) также предоставляет атрибут `scope_str` — это одна строка с этими scopes, разделёнными пробелами (мы будем её использовать).
Мы создаём `HTTPException`, который можем переиспользовать (`raise`) в нескольких местах.
В этом исключении мы включаем требуемые scopes (если есть) в виде строки, разделённой пробелами (используя `scope_str`). Эту строку со scopes мы помещаем в HTTP‑заголовок `WWW-Authenticate` (это часть спецификации).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Проверка `username` и формата данных { #verify-the-username-and-data-shape }
Мы проверяем, что получили `username`, и извлекаем scopes.
Затем валидируем эти данные с помощью Pydantic‑модели (перехватывая исключение `ValidationError`), и если возникает ошибка при чтении JWT‑токена или при валидации данных с Pydantic, мы вызываем `HTTPException`, созданное ранее.
Для этого мы обновляем Pydantic‑модель `TokenData`, добавляя новое свойство `scopes`.
Валидируя данные с помощью Pydantic, мы можем удостовериться, что у нас, например, именно `list` из `str` со scopes и `str` с `username`.
А не, скажем, `dict` или что‑то ещё — ведь это могло бы где‑то позже сломать приложение и создать риск для безопасности.
Мы также проверяем, что существует пользователь с таким именем, и если нет — вызываем то же исключение, созданное ранее.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Проверка `scopes` { #verify-the-scopes }
Теперь проверяем, что все требуемые scopes — этой зависимостью и всеми зависящими (включая операции пути) — присутствуют среди scopes, предоставленных в полученном токене, иначе вызываем `HTTPException`.
Для этого используем `security_scopes.scopes`, содержащий `list` со всеми этими scopes как `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Дерево зависимостей и scopes { #dependency-tree-and-scopes }
Ещё раз рассмотрим дерево зависимостей и scopes.
Так как у зависимости `get_current_active_user` есть подзависимость `get_current_user`, scope `"me"`, объявленный в `get_current_active_user`, будет включён в список требуемых scopes в `security_scopes.scopes`, передаваемый в `get_current_user`.
Сама операция пути тоже объявляет scope — `"items"`, поэтому он также будет в списке `security_scopes.scopes`, передаваемом в `get_current_user`.
Иерархия зависимостей и scopes выглядит так:
- Операция пути `read_own_items`:
- Запрашивает scopes `["items"]` с зависимостью:
- `get_current_active_user`:
- Функция‑зависимость `get_current_active_user`:
- Запрашивает scopes `["me"]` с зависимостью:
- `get_current_user`:
- Функция‑зависимость `get_current_user`:
- Собственных scopes не запрашивает.
- Имеет зависимость, использующую `oauth2_scheme`.
- Имеет параметр `security_scopes` типа `SecurityScopes`:
- Этот параметр `security_scopes` имеет свойство `scopes` с `list`, содержащим все объявленные выше scopes, то есть:
- `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` { #more-details-about-securityscopes }
Вы можете использовать `SecurityScopes` в любой точке и в нескольких местах — необязательно в «корневой» зависимости.
Он всегда будет содержать security scopes, объявленные в текущих зависимостях `Security`, и всеми зависящими — для этой конкретной операции пути и этого конкретного дерева зависимостей.
Поскольку `SecurityScopes` будет содержать все scopes, объявленные зависящими, вы можете использовать его, чтобы централизованно проверять наличие требуемых scopes в токене в одной функции‑зависимости, а затем объявлять разные требования по scopes в разных операциях пути.
Они будут проверяться независимо для каждой операции пути.
## Проверим это { #check-it }
Откройте документацию API — вы сможете аутентифицироваться и указать, какие scopes вы хотите авторизовать.
<img src="/img/tutorial/security/image11.png">
Если вы не выберете ни один scope, вы будете «аутентифицированы», но при попытке доступа к `/users/me/` или `/users/me/items/` получите ошибку о недостаточных разрешениях. При этом доступ к `/status/` будет возможен.
Если вы выберете scope `me`, но не `items`, вы сможете получить доступ к `/users/me/`, но не к `/users/me/items/`.
Так и будет происходить со сторонним приложением, которое попытается обратиться к одной из этих операций пути с токеном, предоставленным пользователем, — в зависимости от того, сколько разрешений пользователь дал приложению.
## О сторонних интеграциях { #about-third-party-integrations }
В этом примере мы используем OAuth2 «password flow» (аутентификация по паролю).
Это уместно, когда мы входим в наше собственное приложение, вероятно, с нашим собственным фронтендом.
Мы можем ему доверять при получении `username` и `password`, потому что он под нашим контролем.
Но если вы создаёте OAuth2‑приложение, к которому будут подключаться другие (т.е. вы строите провайдера аутентификации наподобие Facebook, Google, GitHub и т.п.), вам следует использовать один из других «flows».
Самый распространённый — «implicit flow».
Самый безопасный — «code flow», но он сложнее в реализации, так как требует больше шагов. Из‑за сложности многие провайдеры в итоге рекомендуют «implicit flow».
/// note | Примечание
Часто каждый провайдер аутентификации называет свои «flows» по‑разному — как часть бренда.
Но в итоге они реализуют один и тот же стандарт OAuth2.
///
FastAPI включает утилиты для всех этих OAuth2‑flows в `fastapi.security.oauth2`.
## `Security` в параметре `dependencies` декоратора { #security-in-decorator-dependencies }
Точно так же, как вы можете определить `list` из `Depends` в параметре `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 @@
# Настройки и переменные окружения { #settings-and-environment-variables }
Во многих случаях вашему приложению могут понадобиться внешние настройки или конфигурации, например секретные ключи, учетные данные для базы данных, учетные данные для email‑сервисов и т.д.
Большинство таких настроек являются изменяемыми (могут меняться), например URL базы данных. И многие из них могут быть «чувствительными», например секреты.
По этой причине обычно их передают через переменные окружения, которые считываются приложением.
/// tip | Совет
Чтобы понять, что такое переменные окружения, вы можете прочитать [Переменные окружения](../environment-variables.md){.internal-link target=_blank}.
///
## Типы и валидация { #types-and-validation }
Переменные окружения могут содержать только текстовые строки, так как они внешние по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с разными операционными системами, такими как Linux, Windows, macOS).
Это означает, что любое значение, прочитанное в Python из переменной окружения, будет `str`, а любые преобразования к другим типам или любая валидация должны выполняться в коде.
## Pydantic `Settings` { #pydantic-settings }
К счастью, Pydantic предоставляет отличную утилиту для работы с этими настройками, поступающими из переменных окружения, — <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic: управление настройками</a>.
### Установка `pydantic-settings` { #install-pydantic-settings }
Сначала убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет `pydantic-settings`:
<div class="termy">
```console
$ pip install pydantic-settings
---> 100%
```
</div>
Он также включен при установке набора `all` с:
<div class="termy">
```console
$ pip install "fastapi[all]"
---> 100%
```
</div>
/// info | Информация
В Pydantic v1 он входил в основной пакет. Теперь он распространяется как отдельный пакет, чтобы вы могли установить его только при необходимости.
///
### Создание объекта `Settings` { #create-the-settings-object }
Импортируйте `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` { #use-the-settings }
Затем вы можете использовать новый объект `settings` в вашем приложении:
{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
### Запуск сервера { #run-the-server }
Далее вы можете запустить сервер, передав конфигурации через переменные окружения. Например, можно задать `ADMIN_EMAIL` и `APP_NAME` так:
<div class="termy">
```console
$ ADMIN_EMAIL="[email protected]" 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` будет установлен в `"[email protected]"`.
`app_name` будет `"ChimichangApp"`.
А `items_per_user` сохранит значение по умолчанию `50`.
## Настройки в другом модуле { #settings-in-another-module }
Вы можете вынести эти настройки в другой модуль, как показано в разделе [Большие приложения — несколько файлов](../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-in-a-dependency }
Иногда может быть полезно предоставлять настройки через зависимость, вместо глобального объекта `settings`, используемого повсюду.
Это особенно удобно при тестировании, так как очень легко переопределить зависимость своими настройками.
### Файл конфигурации { #the-config-file }
Продолжая предыдущий пример, ваш файл `config.py` может выглядеть так:
{* ../../docs_src/settings/app02/config.py hl[10] *}
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`.
### Основной файл приложения { #the-main-app-file }
Теперь мы создаем зависимость, которая возвращает новый `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] *}
### Настройки и тестирование { #settings-and-testing }
Далее будет очень просто предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `get_settings`:
{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
В переопределении зависимости мы задаем новое значение `admin_email` при создании нового объекта `Settings`, а затем возвращаем этот новый объект.
После этого можно протестировать, что он используется.
## Чтение файла `.env` { #reading-a-env-file }
Если у вас много настроек, которые могут часто меняться, возможно в разных окружениях, может быть удобно поместить их в файл и читать оттуда как переменные окружения.
Эта практика достаточно распространена и имеет название: такие переменные окружения обычно размещают в файле `.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` { #the-env-file }
У вас может быть файл `.env` со следующим содержимым:
```bash
ADMIN_EMAIL="[email protected]"
APP_NAME="ChimichangApp"
```
### Чтение настроек из `.env` { #read-settings-from-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: Concepts: Configuration</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 Model Config</a>.
///
////
/// info | Информация
В Pydantic версии 1 конфигурация задавалась во внутреннем классе `Config`, в Pydantic версии 2 — в атрибуте `model_config`. Этот атрибут принимает `dict`, и чтобы получить автозавершение и ошибки «на лету», вы можете импортировать и использовать `SettingsConfigDict` для описания этого `dict`.
///
Здесь мы задаем параметр конфигурации `env_file` внутри вашего класса Pydantic `Settings` и устанавливаем значение равным имени файла dotenv, который хотим использовать.
### Создание `Settings` только один раз с помощью `lru_cache` { #creating-the-settings-only-once-with-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-technical-details }
`@lru_cache` модифицирует декорируемую функцию так, что она возвращает то же значение, что и в первый раз, вместо повторного вычисления, то есть вместо выполнения кода функции каждый раз.
Таким образом, функция под декоратором будет выполнена один раз для каждой комбинации аргументов. Затем значения, возвращенные для каждой из этих комбинаций, будут использоваться снова и снова при вызове функции с точно такой же комбинацией аргументов.
Например, если у вас есть функция:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
ваша программа может выполняться так:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
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>.
## Итоги { #recap }
Вы можете использовать Pydantic Settings для управления настройками и конфигурациями вашего приложения с полной мощью Pydantic‑моделей.
* Используя зависимость, вы упрощаете тестирование.
* Можно использовать файлы `.env`.
* `@lru_cache` позволяет не читать файл dotenv снова и снова для каждого запроса, при этом давая возможность переопределять его во время тестирования.

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

@ -0,0 +1,67 @@
# Подприложения — Mounts (монтирование) { #sub-applications-mounts }
Если вам нужны два независимых приложения FastAPI, каждое со своим собственным OpenAPI и собственными интерфейсами документации, вы можете иметь основное приложение и «смонтировать» одно (или несколько) подприложений.
## Монтирование приложения **FastAPI** { #mounting-a-fastapi-application }
«Монтирование» означает добавление полностью независимого приложения по конкретному пути; далее оно будет обрабатывать всё под этим путём, используя объявленные в подприложении _операции пути_.
### Приложение верхнего уровня { #top-level-application }
Сначала создайте основное, верхнего уровня, приложение **FastAPI** и его *операции пути*:
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
### Подприложение { #sub-application }
Затем создайте подприложение и его *операции пути*.
Это подприложение — обычное стандартное приложение FastAPI, но именно оно будет «смонтировано»:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
### Смонтируйте подприложение { #mount-the-sub-application }
В вашем приложении верхнего уровня, `app`, смонтируйте подприложение `subapi`.
В этом случае оно будет смонтировано по пути `/subapi`:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
### Проверьте автоматическую документацию API { #check-the-automatic-api-docs }
Теперь запустите команду `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 для подприложения, включающую только его собственные _операции пути_, все под корректным префиксом подпути `/subapi`:
<img src="/img/tutorial/sub-applications/image02.png">
Если вы попробуете взаимодействовать с любым из двух интерфейсов, всё будет работать корректно, потому что браузер сможет обращаться к каждому конкретному приложению и подприложению.
### Технические подробности: `root_path` { #technical-details-root-path }
Когда вы монтируете подприложение, как описано выше, FastAPI позаботится о передаче пути монтирования для подприложения, используя механизм из спецификации ASGI под названием `root_path`.
Таким образом подприложение будет знать, что для интерфейса документации нужно использовать этот префикс пути.
У подприложения также могут быть свои собственные смонтированные подприложения, и всё будет работать корректно, потому что FastAPI автоматически обрабатывает все эти `root_path`.
Вы узнаете больше о `root_path` и о том, как использовать его явно, в разделе [За прокси](behind-a-proxy.md){.internal-link target=_blank}.

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

@ -0,0 +1,126 @@
# Шаблоны { #templates }
Вы можете использовать любой шаблонизатор вместе с **FastAPI**.
Часто выбирают Jinja2 — тот же, что используется во Flask и других инструментах.
Есть утилиты для простой настройки, которые вы можете использовать прямо в своем приложении **FastAPI** (предоставляются Starlette).
## Установка зависимостей { #install-dependencies }
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его и установили `jinja2`:
<div class="termy">
```console
$ pip install jinja2
---> 100%
```
</div>
## Использование `Jinja2Templates` { #using-jinja2templates }
- Импортируйте `Jinja2Templates`.
- Создайте объект `templates`, который сможете переиспользовать позже.
- Объявите параметр `Request` в *операции пути*, которая будет возвращать шаблон.
- Используйте созданный `templates`, чтобы отрендерить и вернуть `TemplateResponse`; передайте имя шаблона, объект `request` и словарь «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`.
///
## Написание шаблонов { #writing-templates }
Затем вы можете создать шаблон в `templates/item.html`, например:
```jinja hl_lines="7"
{!../../docs_src/templates/templates/item.html!}
```
### Значения контекста шаблона { #template-context-values }
В HTML, который содержит:
{% raw %}
```jinja
Item ID: {{ id }}
```
{% endraw %}
...будет показан `id`, взятый из переданного вами «context» `dict`:
```Python
{"id": id}
```
Например, для ID `42` это отрендерится как:
```html
Item ID: 42
```
### Аргументы `url_for` в шаблоне { #template-url-for-arguments }
Вы также можете использовать `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">
```
## Шаблоны и статические файлы { #templates-and-static-files }
Вы также можете использовать `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`.
## Подробнее { #more-details }
Больше подробностей, включая то, как тестировать шаблоны, смотрите в <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 @@
# Тестирование зависимостей с переопределениями { #testing-dependencies-with-overrides }
## Переопределение зависимостей во время тестирования { #overriding-dependencies-during-testing }
Есть сценарии, когда может понадобиться переопределить зависимость во время тестирования.
Вы не хотите, чтобы исходная зависимость выполнялась (и любые её подзависимости тоже).
Вместо этого вы хотите предоставить другую зависимость, которая будет использоваться только во время тестов (возможно, только в некоторых конкретных тестах) и будет возвращать значение, которое можно использовать везде, где использовалось значение исходной зависимости.
### Варианты использования: внешний сервис { #use-cases-external-service }
Пример: у вас есть внешний провайдер аутентификации, к которому нужно обращаться.
Вы отправляете ему токен, а он возвращает аутентифицированного пользователя.
Такой провайдер может брать плату за каждый запрос, и его вызов может занимать больше времени, чем использование фиксированного мок-пользователя для тестов.
Вероятно, вы захотите протестировать внешний провайдер один раз, но не обязательно вызывать его для каждого запускаемого теста.
В таком случае вы можете переопределить зависимость, которая обращается к этому провайдеру, и использовать собственную зависимость, возвращающую мок-пользователя, только для ваших тестов.
### Используйте атрибут `app.dependency_overrides` { #use-the-app-dependency-overrides-attribute }
Для таких случаев у вашего приложения **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 | Совет
Если вы хотите переопределять зависимость только во время некоторых тестов, задайте переопределение в начале теста (внутри функции теста) и сбросьте его в конце (в конце функции теста).
///

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

@ -0,0 +1,12 @@
# Тестирование событий: lifespan и startup - shutdown { #testing-events-lifespan-and-startup-shutdown }
Если вам нужно, чтобы `lifespan` выполнялся в ваших тестах, вы можете использовать `TestClient` вместе с оператором `with`:
{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
Вы можете узнать больше подробностей в статье [Запуск lifespan в тестах на официальном сайте документации Starlette.](https://www.starlette.io/lifespan/#running-lifespan-in-tests)
Для устаревших событий `startup` и `shutdown` вы можете использовать `TestClient` следующим образом:
{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}

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

@ -0,0 +1,13 @@
# Тестирование WebSocket { #testing-websockets }
Вы можете использовать тот же `TestClient` для тестирования WebSocket.
Для этого используйте `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">тестированию WebSocket</a>.
///

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

@ -0,0 +1,56 @@
# Прямое использование Request { #using-the-request-directly }
До этого вы объявляли нужные части HTTP-запроса вместе с их типами.
Извлекая данные из:
* пути (как параметров),
* HTTP-заголовков,
* Cookie,
* и т.д.
Тем самым **FastAPI** валидирует эти данные, преобразует их и автоматически генерирует документацию для вашего API.
Но бывают ситуации, когда нужно обратиться к объекту `Request` напрямую.
## Подробности об объекте `Request` { #details-about-the-request-object }
Так как под капотом **FastAPI** — это **Starlette** с дополнительным слоем инструментов, вы можете при необходимости напрямую использовать объект <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> из Starlette.
Это также означает, что если вы получаете данные напрямую из объекта `Request` (например, читаете тело запроса), то они не будут валидироваться, конвертироваться или документироваться (с OpenAPI, для автоматического пользовательского интерфейса API) средствами FastAPI.
При этом любой другой параметр, объявленный обычным образом (например, тело запроса с Pydantic-моделью), по-прежнему будет валидироваться, конвертироваться, аннотироваться и т.д.
Но есть конкретные случаи, когда полезно получить объект `Request`.
## Используйте объект `Request` напрямую { #use-the-request-object-directly }
Представим, что вы хотите получить IP-адрес/хост клиента внутри вашей *функции-обработчика пути*.
Для этого нужно обратиться к запросу напрямую.
{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
Если объявить параметр *функции-обработчика пути* с типом `Request`, **FastAPI** поймёт, что нужно передать объект `Request` в этот параметр.
/// tip | Совет
Обратите внимание, что в этом примере мы объявляем path-параметр вместе с параметром `Request`.
Таким образом, path-параметр будет извлечён, валидирован, преобразован к указанному типу и задокументирован в OpenAPI.
Точно так же вы можете объявлять любые другие параметры как обычно и, дополнительно, получать `Request`.
///
## Документация по `Request` { #request-documentation }
Подробнее об <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 и другие { #including-wsgi-flask-django-others }
Вы можете монтировать WSGI‑приложения, как вы видели в [Подприложения — Mounts](sub-applications.md){.internal-link target=_blank}, [За прокси‑сервером](behind-a-proxy.md){.internal-link target=_blank}.
Для этого вы можете использовать `WSGIMiddleware` и обернуть им ваше WSGI‑приложение, например Flask, Django и т.д.
## Использование `WSGIMiddleware` { #using-wsgimiddleware }
Нужно импортировать `WSGIMiddleware`.
Затем оберните WSGI‑приложение (например, Flask) в middleware (Промежуточный слой).
После этого смонтируйте его на путь.
{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
## Проверьте { #check-it }
Теперь каждый HTTP‑запрос по пути `/v1/` будет обрабатываться приложением Flask.
А всё остальное будет обрабатываться **FastAPI**.
Если вы запустите это и перейдёте по <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a>, вы увидите HTTP‑ответ от Flask:
```txt
Hello, World from Flask!
```
А если вы перейдёте по <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a>, вы увидите HTTP‑ответ от FastAPI:
```JSON
{
"message": "Hello World"
}
```

16
docs/ru/docs/deployment/cloud.md

@ -0,0 +1,16 @@
# Развертывание FastAPI у облачных провайдеров { #deploy-fastapi-on-cloud-providers }
Вы можете использовать практически любого облачного провайдера, чтобы развернуть свое приложение на FastAPI.
В большинстве случаев у основных облачных провайдеров есть руководства по развертыванию FastAPI на их платформе.
## Облачные провайдеры — спонсоры { #cloud-providers-sponsors }
Некоторые облачные провайдеры ✨ [**спонсируют FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨ — это обеспечивает непрерывное и здоровое развитие FastAPI и его экосистемы.
И это показывает их искреннюю приверженность FastAPI и его сообществу (вам): они не только хотят предоставить вам хороший сервис, но и стремятся гарантировать, что у вас будет хороший и стабильный фреймворк — FastAPI. 🙇
Возможно, вы захотите попробовать их сервисы и воспользоваться их руководствами:
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
* <a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" class="external-link" target="_blank">Railway</a>

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

@ -0,0 +1,139 @@
# Серверные воркеры — Uvicorn с воркерами { #server-workers-uvicorn-with-workers }
Давайте снова вспомним те концепции деплоя, о которых говорили ранее:
* Безопасность — 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 на контейнер**, но об этом подробнее далее в той главе.
///
## Несколько воркеров { #multiple-workers }
Можно запустить несколько воркеров с помощью опции командной строки `--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> Starting production server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <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> Importing the FastAPI app object from the module with the
following code:
<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> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <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> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</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 running on <b>http://0.0.0.0:8080</b> (Press CTRL+C to quit)
<font color="#A6E22E">INFO</font>: Started parent process [<font color="#A1EFE4"><b>27365</b></font>]
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27368</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27369</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27370</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27367</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
```
</div>
////
Единственная новая опция здесь — `--workers`, она говорит Uvicorn запустить 4 воркер-процесса.
Также видно, что выводится **PID** каждого процесса: `27365` — для родительского процесса (это **менеджер процессов**) и по одному для каждого воркер-процесса: `27368`, `27369`, `27370` и `27367`.
## Концепции деплоя { #deployment-concepts }
Здесь вы увидели, как использовать несколько **воркеров**, чтобы **распараллелить** выполнение приложения, задействовать **несколько ядер** CPU и обслуживать **больше запросов**.
Из списка концепций деплоя выше использование воркеров в основном помогает с **репликацией**, и немного — с **перезапусками**, но об остальных по-прежнему нужно позаботиться:
* **Безопасность — HTTPS**
* **Запуск при старте**
* ***Перезапуски***
* Репликация (количество запущенных процессов)
* **Память**
* **Предварительные шаги перед запуском**
## Контейнеры и Docker { #containers-and-docker }
В следующей главе о [FastAPI в контейнерах — Docker](docker.md){.internal-link target=_blank} я объясню стратегии, которые можно использовать для решения остальных **концепций деплоя**.
Я покажу, как **собрать свой образ с нуля**, чтобы запускать один процесс Uvicorn. Это простой подход и, вероятно, именно то, что вам нужно при использовании распределённой системы управления контейнерами, такой как **Kubernetes**.
## Резюме { #recap }
Вы можете использовать несколько воркер-процессов с опцией командной строки `--workers` в командах `fastapi` или `uvicorn`, чтобы задействовать **многоядерные CPU**, запуская **несколько процессов параллельно**.
Вы можете использовать эти инструменты и идеи, если настраиваете **собственную систему деплоя** и самостоятельно закрываете остальные концепции деплоя.
Перейдите к следующей главе, чтобы узнать о **FastAPI** в контейнерах (например, Docker и Kubernetes). Вы увидите, что эти инструменты тоже предлагают простые способы решить другие **концепции деплоя**. ✨

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

@ -0,0 +1,56 @@
# Условный OpenAPI { #conditional-openapi }
При необходимости вы можете использовать настройки и переменные окружения, чтобы условно настраивать OpenAPI в зависимости от окружения и даже полностью его отключать.
## О безопасности, API и документации { #about-security-apis-and-docs }
Скрытие пользовательских интерфейсов документации в продакшн *не должно* быть способом защиты вашего API.
Это не добавляет дополнительной безопасности вашему API, *операции пути* (обработчики пути) всё равно будут доступны по своим путям.
Если в вашем коде есть уязвимость, она всё равно останется.
Сокрытие документации лишь усложняет понимание того, как взаимодействовать с вашим API, и может усложнить его отладку в продакшн. Это можно считать просто разновидностью <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" class="external-link" target="_blank">безопасности через сокрытие</a>.
Если вы хотите обезопасить свой API, есть несколько более эффективных вещей, которые можно сделать, например:
* Убедитесь, что у вас чётко определены Pydantic-модели для тел запросов и ответов.
* Настройте необходимые разрешения и роли с помощью зависимостей.
* Никогда не храните пароли в открытом виде, только хэши паролей.
* Реализуйте и используйте известные криптографические инструменты, например pwdlib и JWT-токены, и т.д.
* Добавьте более тонкое управление доступом с помощью OAuth2 scopes (областей) там, где это необходимо.
* ...и т.п.
Тем не менее, у вас может быть очень специфичный случай использования, когда действительно нужно отключить документацию API для некоторых окружений (например, в продакшн) или в зависимости от настроек из переменных окружения.
## Условный OpenAPI из настроек и переменных окружения { #conditional-openapi-from-settings-and-env-vars }
Вы можете легко использовать те же настройки Pydantic, чтобы настроить сгенерированный OpenAPI и интерфейсы документации.
Например:
{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
Здесь мы объявляем настройку `openapi_url` с тем же значением по умолчанию — `"/openapi.json"`.
Затем используем её при создании приложения FastAPI.
Далее вы можете отключить OpenAPI (включая интерфейсы документации), установив переменную окружения `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>
После этого, если перейти по адресам `/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 { #configure-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.
## Отключить подсветку синтаксиса { #disable-syntax-highlighting }
Например, вы можете отключить подсветку синтаксиса в 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">
## Изменить тему { #change-the-theme }
Аналогично вы можете задать тему подсветки синтаксиса с ключом "syntaxHighlight.theme" (обратите внимание, что посередине стоит точка):
{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
Эта настройка изменит цветовую тему подсветки синтаксиса:
<img src="/img/tutorial/extending-openapi/image04.png">
## Изменить параметры Swagger UI по умолчанию { #change-default-swagger-ui-parameters }
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 { #other-swagger-ui-parameters }
Чтобы увидеть все остальные возможные настройки, прочитайте официальную <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" class="external-link" target="_blank">документацию по параметрам Swagger UI</a>.
## Настройки только для JavaScript { #javascript-only-settings }
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 @@
# Свои статические ресурсы UI документации (самостоятельный хостинг) { #custom-docs-ui-static-assets-self-hosting }
Документация API использует **Swagger UI** и **ReDoc**, и для каждого из них нужны некоторые файлы JavaScript и CSS.
По умолчанию эти файлы отдаются с <abbr title="Content Delivery Network – Сеть доставки контента: Сервис, обычно состоящий из нескольких серверов, который предоставляет статические файлы, такие как JavaScript и CSS. Обычно используется, чтобы отдавать эти файлы с сервера, расположенного ближе к клиенту, что улучшает производительность.">CDN</abbr>.
Но это можно настроить: вы можете указать конкретный CDN или отдавать файлы самостоятельно.
## Пользовательский CDN для JavaScript и CSS { #custom-cdn-for-javascript-and-css }
Допустим, вы хотите использовать другой <abbr title="Content Delivery Network – Сеть доставки контента">CDN</abbr>, например `https://unpkg.com/`.
Это может быть полезно, если, например, вы живёте в стране, где некоторые URL ограничены.
### Отключить автоматическую документацию { #disable-the-automatic-docs }
Первый шаг — отключить автоматическую документацию, так как по умолчанию она использует стандартный CDN.
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
### Подключить пользовательскую документацию { #include-the-custom-docs }
Теперь вы можете создать *операции пути* для пользовательской документации.
Вы можете переиспользовать внутренние функции 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 сделает это за вас «за кулисами», но для этого ему нужен этот вспомогательный «redirect» эндпоинт.
///
### Создайте *операцию пути*, чтобы проверить { #create-a-path-operation-to-test-it }
Чтобы убедиться, что всё работает, создайте *операцию пути*:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
### Тестирование { #test-it }
Теперь вы должны иметь возможность открыть свою документацию по адресу <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 для документации { #self-hosting-javascript-and-css-for-docs }
Самостоятельный хостинг JavaScript и CSS может быть полезен, если, например, вам нужно, чтобы приложение продолжало работать в офлайне, без доступа к открытому Интернету, или в локальной сети.
Здесь вы увидите, как отдавать эти файлы самостоятельно, в том же приложении FastAPI, и настроить документацию на их использование.
### Структура файлов проекта { #project-file-structure }
Допустим, структура файлов вашего проекта выглядит так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Теперь создайте директорию для хранения этих статических файлов.
Новая структура файлов может выглядеть так:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static/
```
### Скачайте файлы { #download-the-files }
Скачайте статические файлы, необходимые для документации, и поместите их в директорию `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
```
### Предоставьте доступ к статическим файлам { #serve-the-static-files }
* Импортируйте `StaticFiles`.
* Смонтируйте экземпляр `StaticFiles()` в определённый путь.
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
### Протестируйте статические файлы { #test-the-static-files }
Запустите своё приложение и откройте <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
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Это подтверждает, что ваше приложение умеет отдавать статические файлы и что вы поместили файлы документации в нужное место.
Теперь можно настроить приложение так, чтобы документация использовала эти статические файлы.
### Отключить автоматическую документацию для статических файлов { #disable-the-automatic-docs-for-static-files }
Так же, как и при использовании пользовательского CDN, первым шагом будет отключение автоматической документации, так как по умолчанию она использует CDN.
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
### Подключить пользовательскую документацию со статическими файлами { #include-the-custom-docs-for-static-files }
Аналогично пользовательскому 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 сделает это за вас «за кулисами», но для этого ему нужен этот вспомогательный «redirect» эндпоинт.
///
### Создайте *операцию пути* для теста статических файлов { #create-a-path-operation-to-test-static-files }
Чтобы убедиться, что всё работает, создайте *операцию пути*:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
### Тестирование UI со статическими файлами { #test-static-files-ui }
Теперь вы можете отключить Wi‑Fi, открыть свою документацию по адресу <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 { #custom-request-and-apiroute-class }
В некоторых случаях может понадобиться переопределить логику, используемую классами `Request` и `APIRoute`.
В частности, это может быть хорошей альтернативой логике в middleware.
Например, если вы хотите прочитать или изменить тело запроса до того, как оно будет обработано вашим приложением.
/// danger | Опасность
Это «продвинутая» возможность.
Если вы только начинаете работать с **FastAPI**, возможно, стоит пропустить этот раздел.
///
## Сценарии использования { #use-cases }
Некоторые сценарии:
* Преобразование тел запросов, не в формате JSON, в JSON (например, <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>).
* Распаковка тел запросов, сжатых с помощью gzip.
* Автоматическое логирование всех тел запросов.
## Обработка пользовательского кодирования тела запроса { #handling-custom-request-body-encodings }
Посмотрим как использовать пользовательский подкласс `Request` для распаковки gzip-запросов.
И подкласс `APIRoute`, чтобы использовать этот пользовательский класс запроса.
### Создать пользовательский класс `GzipRequest` { #create-a-custom-gziprequest-class }
/// tip | Совет
Это учебный пример, демонстрирующий принцип работы. Если вам нужна поддержка Gzip, вы можете использовать готовый [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}.
///
Сначала создадим класс `GzipRequest`, который переопределит метод `Request.body()` и распакует тело запроса при наличии соответствующего HTTP-заголовка.
Если в заголовке нет `gzip`, он не будет пытаться распаковывать тело.
Таким образом, один и тот же класс маршрута сможет обрабатывать как gzip-сжатые, так и несжатые запросы.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
### Создать пользовательский класс `GzipRoute` { #create-a-custom-gziproute-class }
Далее создадим пользовательский подкласс `fastapi.routing.APIRoute`, который будет использовать `GzipRequest`.
На этот раз он переопределит метод `APIRoute.get_route_handler()`.
Этот метод возвращает функцию. Именно эта функция получает HTTP-запрос и возвращает HTTP-ответ.
Здесь мы используем её, чтобы создать `GzipRequest` из исходного HTTP-запроса.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
/// note | Технические детали
У `Request` есть атрибут `request.scope` — это просто Python-`dict`, содержащий метаданные, связанные с HTTP-запросом.
У `Request` также есть `request.receive` — функция для «получения» тела запроса.
И `dict` `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**.
## Доступ к телу запроса в обработчике исключений { #accessing-the-request-body-in-an-exception-handler }
/// 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` в роутере { #custom-apiroute-class-in-a-router }
Вы также можете задать параметр `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 { #extending-openapi }
Иногда может понадобиться изменить сгенерированную схему OpenAPI.
В этом разделе показано, как это сделать.
## Обычный процесс { #the-normal-process }
Обычный (по умолчанию) процесс выглядит так.
Приложение `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 и выше.
///
## Переопределение значений по умолчанию { #overriding-the-defaults }
Используя информацию выше, вы можете той же вспомогательной функцией сгенерировать схему 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** { #normal-fastapi }
Сначала напишите приложение **FastAPI** как обычно:
{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
### Сгенерируйте схему OpenAPI { #generate-the-openapi-schema }
Затем используйте ту же вспомогательную функцию для генерации схемы OpenAPI внутри функции `custom_openapi()`:
{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
### Измените схему OpenAPI { #modify-the-openapi-schema }
Теперь можно добавить расширение ReDoc, добавив кастомный `x-logo` в «объект» `info` в схеме OpenAPI:
{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
### Кэшируйте схему OpenAPI { #cache-the-openapi-schema }
Вы можете использовать свойство `.openapi_schema` как «кэш» для хранения сгенерированной схемы.
Так приложению не придётся генерировать схему каждый раз, когда пользователь открывает документацию API.
Она будет создана один раз, а затем тот же кэшированный вариант будет использоваться для последующих запросов.
{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
### Переопределите метод { #override-the-method }
Теперь вы можете заменить метод `.openapi()` на вашу новую функцию.
{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
### Проверьте { #check-it }
Перейдите на <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 @@
# Общее — Как сделать — Рецепты { #general-how-to-recipes }
Здесь несколько указателей на другие места в документации для общих или частых вопросов.
## Фильтрация данных — Безопасность { #filter-data-security }
Чтобы убедиться, что вы не возвращаете больше данных, чем следует, прочитайте документацию: [Руководство — Модель ответа — Возвращаемый тип](../tutorial/response-model.md){.internal-link target=_blank}.
## Теги в документации — OpenAPI { #documentation-tags-openapi }
Чтобы добавить теги к вашим *операциям пути* и группировать их в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Теги](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
## Краткое описание и описание в документации — OpenAPI { #documentation-summary-and-description-openapi }
Чтобы добавить краткое описание и описание к вашим *операциям пути* и отобразить их в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Краткое описание и описание](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}.
## Описание ответа в документации — OpenAPI { #documentation-response-description-openapi }
Чтобы задать описание ответа, отображаемое в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Описание ответа](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}.
## Документация — пометить операцию пути устаревшей — OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Чтобы пометить *операцию пути* как устаревшую и показать это в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Пометить операцию пути устаревшей](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}.
## Преобразование любых данных к формату, совместимому с JSON { #convert-any-data-to-json-compatible }
Чтобы преобразовать любые данные к формату, совместимому с JSON, прочитайте документацию: [Руководство — JSON-совместимый кодировщик](../tutorial/encoder.md){.internal-link target=_blank}.
## Метаданные OpenAPI — Документация { #openapi-metadata-docs }
Чтобы добавить метаданные в вашу схему OpenAPI, включая лицензию, версию, контакты и т.д., прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md){.internal-link target=_blank}.
## Пользовательский URL OpenAPI { #openapi-custom-url }
Чтобы настроить URL OpenAPI (или удалить его), прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}.
## URL документации OpenAPI { #openapi-docs-urls }
Чтобы изменить URL, используемые для автоматически сгенерированных пользовательских интерфейсов документации, прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}.

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

@ -0,0 +1,60 @@
# GraphQL { #graphql }
Так как **FastAPI** основан на стандарте **ASGI**, очень легко интегрировать любую библиотеку **GraphQL**, также совместимую с ASGI.
Вы можете комбинировать обычные *операции пути* FastAPI с GraphQL в одном приложении.
/// tip | Совет
**GraphQL** решает некоторые очень специфические задачи.
У него есть как **преимущества**, так и **недостатки** по сравнению с обычными **веб-API**.
Убедитесь, что **выгоды** для вашего случая использования перевешивают **недостатки**. 🤓
///
## Библиотеки GraphQL { #graphql-libraries }
Ниже приведены некоторые библиотеки **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-with-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 { #older-graphqlapp-from-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>, так как он основан на аннотациях типов, а не на пользовательских классах и типах.
///
## Подробнее { #learn-more }
Подробнее о **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 @@
# Как сделать — Рецепты { #how-to-recipes }
Здесь вы найдете разные рецепты и руководства «как сделать» по различным темам.
Большинство из этих идей более-менее независимы, и в большинстве случаев вам стоит изучать их только если они напрямую относятся к вашему проекту.
Если что-то кажется интересным и полезным для вашего проекта, смело изучайте; в противном случае, вероятно, можно просто пропустить.
/// tip | Совет
Если вы хотите изучить FastAPI структурированно (рекомендуется), вместо этого читайте [Учебник — Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} по главам.
///

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

@ -0,0 +1,104 @@
# Разделять схемы OpenAPI для входа и выхода или нет { #separate-openapi-schemas-for-input-and-output-or-not }
При использовании **Pydantic v2** сгенерированный OpenAPI становится чуть более точным и **корректным**, чем раньше. 😎
На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON схемы** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**.
Посмотрим, как это работает, и как это изменить при необходимости.
## Pydantic‑модели для входа и выхода { #pydantic-models-for-input-and-output }
Предположим, у вас есть Pydantic‑модель со значениями по умолчанию, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Модель для входа { #model-for-input }
Если использовать эту модель как входную, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
…то поле `description` **не будет обязательным**, потому что у него значение по умолчанию `None`.
### Входная модель в документации { #input-model-in-docs }
В документации это видно: у поля `description` нет **красной звёздочки** — оно не отмечено как обязательное:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image01.png">
</div>
### Модель для выхода { #model-for-output }
Но если использовать ту же модель как выходную, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
…то, поскольку у `description` есть значение по умолчанию, даже если вы **ничего не вернёте** для этого поля, оно всё равно будет иметь это **значение по умолчанию**.
### Модель для данных ответа { #model-for-output-response-data }
Если поработать с интерактивной документацией и посмотреть ответ, то, хотя код ничего не добавил в одно из полей `description`, JSON‑ответ содержит значение по умолчанию (`null`):
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image02.png">
</div>
Это означает, что у него **всегда будет какое‑то значение**, просто иногда это значение может быть `None` (или `null` в JSON).
Следовательно, клиентам, использующим ваш API, не нужно проверять наличие этого значения: они могут **исходить из того, что поле всегда присутствует**, а в некоторых случаях имеет значение по умолчанию `None`.
В OpenAPI это описывается тем, что поле помечается как **обязательное**, поскольку оно всегда присутствует.
Из‑за этого JSON Schema для модели может отличаться в зависимости от использования для **входа** или **выхода**:
* для **входа** `description` не будет обязательным
* для **выхода** оно будет **обязательным** (и при этом может быть `None`, или, в терминах JSON, `null`)
### Выходная модель в документации { #model-for-output-in-docs }
В документации это тоже видно, что **оба**: `name` и `description`, помечены **красной звёздочкой** как **обязательные**:
<div class="screenshot">
<img src="/img/tutorial/separate-openapi-schemas/image03.png">
</div>
### Модели для входа и выхода в документации { #model-for-input-and-output-in-docs }
Если посмотреть все доступные схемы (JSON Schema) в 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, они тоже будут точнее, с лучшим **удобством для разработчиков** и большей консистентностью. 🎉
## Не разделять схемы { #do-not-separate-schemas }
Однако бывают случаи, когда вы хотите иметь **одну и ту же схему для входа и выхода**.
Главный сценарий — когда у вас уже есть сгенерированный клиентский код/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] *}
### Одна и та же схема для входной и выходной моделей в документации { #same-schema-for-input-and-output-models-in-docs }
Теперь для этой модели будет одна общая схема и для входа, и для выхода — только `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 @@
# Тестирование базы данных { #testing-a-database }
Вы можете изучить базы данных, 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 @@
# Ресурсы { #resources }
Дополнительные ресурсы, внешние ссылки, статьи и многое другое. ✈️
Loading…
Cancel
Save