Browse Source

🌐Add Russian translations with missing pages (using LLMs)

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

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

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

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

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

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

@ -0,0 +1,361 @@
# За прокси
В некоторых случаях вам может понадобиться воспользоваться сервером **прокси**, таким как Traefik или Nginx, с конфигурацией, которая добавляет дополнительный префикс пути, не видимый вашему приложению.
В таких случаях вы можете использовать `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] *}
И прокси будет **"обрезать"** **префикс пути** на лету перед передачей запроса на сервер приложения (вероятно, через FastAPI CLI через Uvicorn), оставляя ваше приложение уверенным в том, что оно обслуживается по адресу `/app`, чтобы вам не нужно было обновлять весь ваш код для включения префикса `/api/v1`.
До этого момента все будет работать как обычно.
Но затем, когда вы откроете интегрированный интерфейс документации (фронтенд), он будет ожидать, что получит схему OpenAPI по адресу `/openapi.json`, вместо `/api/v1/openapi.json`.
Таким образом, фронтенд (который выполняется в браузере) попытается получить доступ к `/openapi.json` и не сможет получить схему OpenAPI.
Поскольку у нас есть прокси с префиксом пути `/api/v1` для нашего приложения, фронтенд должен получить схему OpenAPI по адресу `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Браузер")
proxy["Прокси на http://0.0.0.0:9999/api/v1/app"]
server["Сервер на http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Совет
IP `0.0.0.0` обычно используется, чтобы указать, что программа слушает все доступные IP-адреса на этой машине/сервере.
///
UI документации также будет нужна схема OpenAPI, чтобы указать, что этот API `server` находится по адресу `/api/v1` (за прокси). Например:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Другие данные здесь
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Другие данные здесь
}
}
```
В этом примере "Прокси" может быть чем-то вроде **Traefik**. А сервер будет чем-то вроде FastAPI CLI с **Uvicorn**, выполняющим ваше приложение FastAPI.
### Предоставление `root_path`
Для этого вы можете использовать параметр командной строки `--root-path`, например:
<div class="termy">
```console
$ fastapi run main.py --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Если вы используете Hypercorn, у него также есть опция `--root-path`.
/// note | Технические детали
Спецификация ASGI определяет `root_path` для этого случая использования.
А параметр командной строки `--root-path` предоставляет этот `root_path`.
///
### Проверка текущего `root_path`
Вы можете получить текущий `root_path`, используемый вашим приложением для каждого запроса, он является частью словаря `scope` (который является частью спецификации ASGI).
Здесь мы приводим его в сообщении только для демонстрационных целей.
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
Затем, если вы запустите Uvicorn с:
<div class="termy">
```console
$ fastapi run main.py --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Ответ будет таким:
```JSON
{
"message": "Здравствуй, мир",
"root_path": "/api/v1"
}
```
### Установка `root_path` в приложении FastAPI
В качестве альтернативы, если у вас нет возможности предоставить параметр командной строки, такой как `--root-path` или эквивалентный, вы можете установить параметр `root_path` при создании вашего приложения FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
Передача `root_path` в `FastAPI` будет эквивалентна передаче параметра командной строки `--root-path` в Uvicorn или Hypercorn.
### О `root_path`
Учтите, что сервер (Uvicorn) не будет использовать этот `root_path` для чего-либо, кроме передачи его в приложение.
Но если вы пойдете с вашим браузером на <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, вы увидите нормальный ответ:
```JSON
{
"message": "Здравствуй, мир",
"root_path": "/api/v1"
}
```
Таким образом, он не будет ожидать доступа по адресу `http://127.0.0.1:8000/api/v1/app`.
Uvicorn будет ожидать, что прокси будет обращаться к Uvicorn по адресу `http://127.0.0.1:8000/app`, и тогда это будет обязанностью прокси добавить дополнительный префикс `/api/v1` поверх.
## О прокси с обрезанным префиксом пути
Учтите, что использование прокси с обрезанным префиксом пути — это только один из способов его настройки.
Вероятно, в большинстве случаев по умолчанию прокси не будет иметь обрезанного префикса пути.
В таком случае (без обрезанного префикса пути) прокси будет слушать что-то вроде `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
Вы можете легко запустить эксперимент локально с обрезанным префиксом пути, используя <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 --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>
### Проверка ответов
Теперь, если вы перейдете по 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": "Здравствуй, мир",
"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": "Здравствуй, мир",
"root_path": "/api/v1"
}
```
только на этот раз по адресу URL с префиксом, предоставленным прокси: `/api/v1`.
Конечно, идея в том, чтобы все получали доступ к приложению через прокси, поэтому версия с префиксом пути `/api/v1` является "правильной".
А версия без префикса пути (`http://127.0.0.1:8000/app`), предоставленная напрямую Uvicorn, будет исключительно для доступа _прокси_ (Traefik) к нему.
Это демонстрирует, как прокси (Traefik) использует префикс пути и как сервер (Uvicorn) использует `root_path` из параметра `--root-path`.
### Проверка UI документации
Но вот интересная часть. ✨
"Официальный" способ доступа к приложению будет через прокси с префиксом пути, который мы определили. Таким образом, как мы и ожидали, если вы попытаетесь использовать 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">
Но если мы получим доступ к UI документации по "официальному" URL с использованием прокси с портом `9999`, по `/api/v1/docs`, он работает правильно! 🎉
Можно проверить это на <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image02.png">
Точно так, как мы и хотели. ✔️
Это потому, что FastAPI использует этот `root_path` для создания стандартного `server` в OpenAPI с URL, предоставленным `root_path`.
## Дополнительные серверы
/// warning | Предупреждение
Это более сложный случай использования. Смело пропускайте его.
///
По умолчанию **FastAPI** создаст `server` в схеме OpenAPI с URL для `root_path`.
Но вы также можете предоставить другие альтернативные `servers`, например, если вы хотите, чтобы *тот же* UI документации взаимодействовал как с окружением для тестирования, так и с производственным окружением.
Если вы передадите кастомный список `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`.
///
В UI документации по адресу <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 | Совет
UI документации будет взаимодействовать с сервером, который вы выберете.
///
### Отключить автоматический сервер из `root_path`
Если вы не хотите, чтобы **FastAPI** включал автоматический сервер, используя `root_path`, вы можете использовать параметр `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
и тогда он не будет включен в схему OpenAPI.
## Монтирование подприложения
Если вам нужно смонтировать подприложение (как описано в [Подприложения - Монтирование](sub-applications.md){.internal-link target=_blank}), при этом используя прокси с `root_path`, вы можете сделать это нормально, как вы и ожидаете.
FastAPI будет внутри использовать `root_path` умно, так что все будет просто работать. ✨

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -0,0 +1,55 @@
# OpenAPI Вебхуки
Существуют случаи, когда вы хотите сообщить вашим **пользователям API**, что ваше приложение может вызвать *их* приложение (отправив запрос) с некоторыми данными, обычно чтобы **уведомить** о каком-то типе **события**.
Это означает, что вместо обычного процесса, когда ваши пользователи отправляют запросы вашему API, это **ваш API** (или ваше приложение), который может **отправлять запросы их системе** (их API, их приложению).
Обычно это называется **вебхук**.
## Шаги вебхуков
Процесс обычно заключается в том, что **вы определяете** в своем коде, какое сообщение вы будете отправлять, **тело запроса**.
Вы также каким-то образом определяете, в какие **моменты** ваше приложение будет отправлять эти запросы или события.
А **ваши пользователи** каким-то образом (например, в веб-интерфейсе где-то) определяют **URL**, на который ваше приложение должно отправлять эти запросы.
Вся **логика** относительно того, как регистрировать URL для вебхуков и писать код для отправки этих запросов, зависит от вас. Вы пишете это так, как хотите, в **вашем собственном коде**.
## Документирование вебхуков с **FastAPI** и OpenAPI
С помощью **FastAPI**, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), и тела **запросов**, которые ваше приложение будет отправлять.
Это может значительно облегчить вашим пользователям **реализацию их API** для приема ваших **вебхуков**, они даже могут автоматически сгенерировать часть своего собственного кода API.
/// info | Информация
Вебхуки доступны в OpenAPI 3.1.0 и выше, поддерживаются FastAPI `0.99.0` и выше.
///
## Приложение с вебхуками
Когда вы создаете приложение **FastAPI**, существует атрибут `webhooks`, который вы можете использовать для определения *вебхуков*, так же, как вы определяете *операции с путями*, например, с помощью `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
Вебхуки, которые вы определите, окажутся в **OpenAPI** схеме и в автоматическом **документированном интерфейсе**.
/// info | Информация
Объект `app.webhooks` на самом деле просто `APIRouter`, тот же тип, который вы бы использовали при структурировании вашего приложения с несколькими файлами.
///
Обратите внимание, что с вебхуками вы на самом деле не объявляете *путь* (как `/items/`), текст, который вы передаете, является просто **идентификатором** вебхука (именем события), например, в `@app.webhooks.post("new-subscription")` имя вебхука - `new-subscription`.
Это потому, что предполагается, что **ваши пользователи** будут определять фактический **путь 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/openapi-webhooks/image01.png">

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

@ -0,0 +1,204 @@
# Расширенная конфигурация операций пути
## OpenAPI operationId
/// warning | Предупреждение
Если вы не "эксперт" в OpenAPI, вам, вероятно, это не нужно.
///
Вы можете установить OpenAPI `operationId` для использования в вашей *операции пути* с параметром `operation_id`.
Вам нужно будет убедиться, что он уникален для каждой операции.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
### Использование имени функции *операции пути* как operationId
Если вы хотите использовать имена функций своих API в качестве `operationId`, вы можете перебрать все из них и переопределить каждый `operation_id` для их *операций пути* используя `APIRoute.name`.
Вы должны сделать это после добавления всех своих *операций пути*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
/// tip | Совет
Если вы вручную вызываете `app.openapi()`, вам следует обновить `operationId` перед этим.
///
/// warning | Предупреждение
Если вы так делаете, вам нужно убедиться, что каждая из ваших *функций операций пути* имеет уникальное имя.
Даже если они находятся в разных модулях (файлах Python).
///
## Исключение из OpenAPI
Чтобы исключить *операцию пути* из создаваемой схемы OpenAPI (и, следовательно, из систем автоматической документации), используйте параметр `include_in_schema` и установите его в `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
## Расширенное описание из docstring
Вы можете ограничить строки, используемые из docstring функции *операции пути* для OpenAPI.
Добавление `\f` (экранированный символ "form feed") приводит к тому, что **FastAPI** обрезает вывод, используемый для OpenAPI в этой точке.
Это не будет отображаться в документации, но другие инструменты (такие как Sphinx) смогут использовать остальное.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
## Дополнительные ответы
Вы, вероятно, уже видели, как объявлять `response_model` и `status_code` для *операции пути*.
Это определяет метаданные о основном ответе *операции пути*.
Вы также можете объявить дополнительные ответы с их моделями, статусами кодов и т. д.
Здесь есть целая глава в документации об этом, вы можете прочитать ее в разделе [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
## OpenAPI 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">Operation Object</a>.
///
Он содержит всю информацию о *операции пути* и используется для генерации автоматической документации.
Он включает `tags`, `parameters`, `requestBody`, `responses` и т. д.
Эта OpenAPI схема, специфичная для *операции пути*, обычно генерируется автоматически **FastAPI**, но вы также можете расширить ее.
/// tip | Совет
Это низкоуровневая точка расширения.
Если вам нужно только объявить дополнительные ответы, более удобный способ сделать это — с [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.
///
Вы можете расширить схему OpenAPI для *операции пути* с помощью параметра `openapi_extra`.
### Расширения OpenAPI
Этот `openapi_extra` может быть полезен, например, для объявления [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
Если вы откроете автоматическую документацию API, ваше расширение будет отображено внизу конкретной *операции пути*.
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
И если вы посмотрите на результат OpenAPI (по адресу `/openapi.json` в вашем API), вы увидите свое расширение как часть конкретной *операции пути* тоже:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Индивидуальная OpenAPI схема *операции пути*
Словарь в `openapi_extra` будет глубоко объединен с автоматически сгенерированной схемой OpenAPI для *операции пути*.
Таким образом, вы сможете добавить дополнительные данные к автоматически сгенерированной схеме.
Например, вы могли бы решить считать и проверять запрос с помощью вашего собственного кода, не используя автоматические функции FastAPI с Pydantic, но при этом вы могли бы захотеть определить запрос в схеме OpenAPI.
Вы могли бы сделать это с помощью `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
В этом примере мы не объявляли модель Pydantic. Фактически, тело запроса даже не <abbr title="преобразовано из какого-то простого формата, как байты, в объекты Python">проанализировано</abbr> как JSON, оно читается напрямую как `bytes`, и функция `magic_data_reader()` будет отвечать за его парсинг каким-то образом.
Тем не менее, мы можем объявить ожидаемую схему для тела запроса.
### Индивидуальный тип содержимого OpenAPI
Используя эту же хитрость, вы могли бы использовать модель Pydantic для определения JSON 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 @@
# Заголовки ответов
## Используйте параметр `Response`
Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (как вы можете сделать для cookies).
А затем вы можете установить заголовки в этом *временном* объекте ответа.
{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
И затем вы можете вернуть любой объект, который вам нужен, как вы обычно делаете (например, `dict`, модель базы данных и т.д.).
И если вы объявили `response_model`, он все равно будет использован для фильтрации и конвертации объекта, который вы вернули.
**FastAPI** использует этот *временный* ответ для извлечения заголовков (а также cookies и кода состояния) и помещает их в окончательный ответ, содержащий значение, которое вы вернули, отфильтрованное с помощью любого `response_model`.
Вы также можете объявить параметр `Response` в зависимостях и установить заголовки (и cookies) в них.
## Возврат `Response` напрямую
Вы также можете добавить заголовки, когда возвращаете `Response` напрямую.
Создайте ответ, как описано в разделе [Return a Response Directly](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` может часто использоваться для установки заголовков и cookies, **FastAPI** также предоставляет его в `fastapi.Response`.
///
## Пользовательские заголовки
Имейте в виду, что пользовательские проприетарные заголовки могут быть добавлены <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 Базовая Аутентификация
Для самых простых случаев вы можете использовать HTTP Базовую Аутентификацию.
В HTTP Базовой Аутентификации, приложение ожидает заголовок, содержащий имя пользователя и пароль.
Если оно не получает его, то возвращает ошибку HTTP 401 "Unauthorized".
Также возвращается заголовок `WWW-Authenticate` со значением `Basic` и необязательный параметр `realm`.
Это сообщает браузеру показать встроенное окно запроса имени пользователя и пароля.
Затем, когда вы вводите это имя пользователя и пароль, браузер отправляет их в заголовке автоматически.
## Простая HTTP Базовая Аутентификация
* Импортируйте `HTTPBasic` и `HTTPBasicCredentials`.
* Создайте "`сущность` схемы безопасности" с использованием `HTTPBasic`.
* Используйте эту `схему безопасности` с зависимостью в вашем *путевом обработчике*.
* Это возвращает объект типа `HTTPBasicCredentials`:
* Он содержит `username` и `password`, которые были отправлены.
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
Когда вы попробуете открыть URL в первый раз (или нажмёте кнопку "Execute" в документации), браузер запросит у вас ввод имени пользователя и пароля:
<img src="/img/tutorial/security/image12.png">
## Проверка имени пользователя
Вот более полный пример.
Используйте зависимость, чтобы проверить, правильны ли имя пользователя и пароль.
Для этого используйте стандартный модуль Python <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a>, чтобы проверить имя пользователя и пароль.
`secrets.compare_digest()` должен принимать `bytes` или `str`, который содержит только символы ASCII (те, которые в английском языке), это значит, что он не будет работать с такими символами, как `á`, как в `Sebastián`.
Чтобы обработать это, мы сначала преобразуем `username` и `password` в `bytes`, кодируя их с помощью UTF-8.
Затем мы можем использовать `secrets.compare_digest()`, чтобы убедиться, что `credentials.username` равен `"stanleyjobson"`, а `credentials.password` равен `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
Это будет аналогично:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Возвращаем какую-то ошибку
...
```
Но, используя `secrets.compare_digest()`, это будет безопасно от типа атак, называемых "атаки по времени".
### Атаки по времени
Что же такое "атака по времени"?
Представьте, что некоторые злоумышленники пытаются угадать имя пользователя и пароль.
И они отправляют запрос с именем пользователя `johndoe` и паролем `love123`.
Тогда Python-код в вашем приложении будет аналогичен чему-то такому:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Но в тот момент, когда Python сравнивает первую `j` в `johndoe` с первой `s` в `stanleyjobson`, он вернёт `False`, потому что уже знает, что эти строки не одинаковы, считая, что "нет смысла тратить больше вычислений на сравнение оставшихся букв". И ваше приложение скажет "Неверное имя пользователя или пароль".
Но затем злоумышленники пробуют с именем пользователя `stanleyjobsox` и паролем `love123`.
И ваш код в приложении делает что-то похожее:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python должен будет сравнить весь `stanleyjobso` в обоих `stanleyjobsox` и `stanleyjobson` перед тем, как осознать, что эти две строки не одинаковы. Поэтому для возвращения "Неверное имя пользователя или пароль" потребуется немного больше времени.
#### Время ответа помогает злоумышленникам
В этот момент, заметив, что сервер потратил несколько микросекунд дольше для отправки ответа "Неверное имя пользователя или пароль", злоумышленники будут знать, что они угадали _что-то_ правильно, некоторые начальные буквы были правильными.
И затем они могут попробовать снова, зная, что это, вероятно, что-то больше похожее на `stanleyjobsox`, чем на `johndoe`.
#### "Профессиональная" атака
Конечно, злоумышленники не будут пробовать все это вручную, они напишут программу, чтобы сделать это, возможно, с тысячами или миллионами тестов в секунду. И они получат только одну дополнительную правильную букву за раз.
Но, делая это, за несколько минут или часов злоумышленники могут угадать правильное имя пользователя и пароль с "помощью" нашего приложения, просто используя время, взятое на ответ.
#### Исправьте это с помощью `secrets.compare_digest()`
Но в нашем коде мы фактически используем `secrets.compare_digest()`.
Вкратце, это займет одинаковое время для сравнения `stanleyjobsox` с `stanleyjobson`, как и для сравнения `johndoe` с `stanleyjobson`. И то же самое для пароля.
Таким образом, используя `secrets.compare_digest()` в вашем коде приложения, оно будет безопасным от всего этого множества атак на безопасность.
### Возвращение ошибки
После обнаружения, что учетные данные неверны, верните `HTTPException` со статусным кодом 401 (тем же, что возвращается, когда учетные данные не предоставлены) и добавьте заголовок `WWW-Authenticate`, чтобы заставить браузер снова показать приглашение о вводе данных:
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}

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

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

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

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

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

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

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

@ -0,0 +1,67 @@
# Подприложения - Монтирование
Если вам нужно иметь два независимых приложения FastAPI, каждое со своей независимой OpenAPI и собственным интерфейсом документации, вы можете создать основное приложение и "монтировать" одно (или несколько) подпрограмм.
## Монтирование приложения **FastAPI**
"Монтирование" означает добавление полностью "независимого" приложения в определенный путь, которое будет обрабатывать все запросы, поступающие по этому пути, с _operations path_, объявленными в этом подприложении.
### Приложение верхнего уровня
Сначала создайте главное приложение верхнего уровня **FastAPI** и его *operations path*:
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
### Подприложение
Затем создайте свое подприложение и его *operations path*.
Это подприложение является просто еще одним стандартным приложением FastAPI, но именно оно будет "смонтировано":
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
### Монтирование подприложения
В вашем приложении верхнего уровня `app` смонтируйте подприложение `subapi`.
В этом случае оно будет смонтировано по пути `/subapi`:
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
### Проверка автоматической документации API
Теперь выполните команду `fastapi` с вашим файлом:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
И откройте документацию по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
Вы увидите автоматическую документацию API для основного приложения, содержащую только его _operations path_:
<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 для подприложения, содержащую только его _operations path_, все с правильным префиксом подпути `/subapi`:
<img src="/img/tutorial/sub-applications/image02.png">
Если вы попробуете взаимодействовать с любым из этих двух пользовательских интерфейсов, они будут работать корректно, потому что браузер сможет общаться с каждым определенным приложением или подприложением.
### Технические детали: `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 @@
# Шаблоны
Вы можете использовать любой механизм шаблонов с **FastAPI**.
Распространенным выбором является Jinja2, тот же, что используется в Flask и других инструментах.
Есть утилиты для легкой конфигурации, которые вы можете использовать непосредственно в вашем приложении **FastAPI** (предоставлено Starlette).
## Установка зависимостей
Убедитесь, что вы создаете [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активируйте его и установите `jinja2`:
<div class="termy">
```console
$ pip install jinja2
---> 100%
```
</div>
## Использование `Jinja2Templates`
* Импортируйте `Jinja2Templates`.
* Создайте объект `templates`, который можно будет использовать позже.
* Объявите параметр `Request` в *операции маршрута*, который будет возвращать шаблон.
* Используйте созданные вами `templates`, чтобы рендерить и возвращать `TemplateResponse`, передавая имя шаблона, объект запроса и "контекст" — словарь с парами ключ-значение, которые будут использоваться внутри шаблона Jinja2.
{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
/// note | Примечание
До версии FastAPI 0.108.0 и Starlette 0.29.0 первым параметром был `name`.
Также до этого, в предыдущих версиях, объект `request` передавался как часть пар ключ-значение в контексте для Jinja2.
///
/// tip | Совет
Объявляя `response_class=HTMLResponse`, интерфейс документов сможет знать, что ответ будет в формате HTML.
///
/// note | Технические подробности
Вы также можете использовать `from starlette.templating import Jinja2Templates`.
**FastAPI** предоставляет ту же `starlette.templating` под именем `fastapi.templating` просто для вашего удобства, разработчика. Но большинство доступных ответов напрямую из Starlette. То же самое с `Request` и `StaticFiles`.
///
## Написание шаблонов
Затем вы можете написать шаблон в `templates/item.html` с, например:
```jinja hl_lines="7"
{!../../docs_src/templates/templates/item.html!}
```
### Контекстные значения шаблона
В HTML, который содержит:
{% raw %}
```jinja
Item ID: {{ id }}
```
{% endraw %}
...это отобразит `id`, взятый из "контекста" `dict`, который вы передали:
```Python
{"id": id}
```
Например, с ID `42`, это отобразится как:
```html
Item ID: 42
```
### Аргументы `url_for` в шаблоне
Вы также можете использовать `url_for()` внутри шаблона, он принимает в качестве аргументов те же аргументы, которые будут использованы вашей *функцией операции маршрута*.
Таким образом, секция с:
{% raw %}
```jinja
<a href="{{ url_for('read_item', id=id) }}">
```
{% endraw %}
...создаст ссылку на тот же URL, который будет обрабатываться *функцией операции маршрута* `read_item(id=id)`.
Например, с ID `42`, это отобразится как:
```html
<a href="/items/42">
```
## Шаблоны и статичные файлы
Вы также можете использовать `url_for()` внутри шаблона, и использовать его, например, с `StaticFiles`, которые вы закрепили с `name="static"`.
```jinja hl_lines="4"
{!../../docs_src/templates/templates/item.html!}
```
В этом примере это бы связало с файлом CSS в `static/styles.css` с:
```CSS hl_lines="4"
{!../../docs_src/templates/static/styles.css!}
```
И поскольку вы используете `StaticFiles`, этот CSS-файл будет автоматически обслуживаться вашим приложением **FastAPI** по URL `/static/styles.css`.
## Подробнее
Для получения более подробной информации, включая тестирование шаблонов, посетите <a href="https://www.starlette.io/templates/" class="external-link" target="_blank">документацию Starlette по шаблонам</a>.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -0,0 +1,185 @@
# Пользовательские статические ресурсы Docs UI (самостоятельный хостинг)
API документация использует **Swagger UI** и **ReDoc**, и каждому из них требуются некоторые файлы JavaScript и CSS.
По умолчанию эти файлы предоставляются через <abbr title="Сеть доставки контента: служба, обычно состоящая из нескольких серверов, которая предоставляет статические файлы, такие как JavaScript и CSS. Обычно используется для доставки этих файлов с сервера, наиболее близкого к клиенту, что улучшает производительность.">CDN</abbr>.
Но возможно его настроить: можно установить определенный CDN или разместить файлы самостоятельно.
## Пользовательский CDN для JavaScript и CSS
Предположим, что вы хотите использовать другой <abbr title="Сеть доставки контента">CDN</abbr>, например, хотите использовать `https://unpkg.com/`.
Это может быть полезно, если, например, вы живете в стране, где некоторые URL-адреса ограничены.
### Отключение автоматической документации
Первый шаг — отключить автоматическую документацию, так как по умолчанию они используют стандартный CDN.
Чтобы отключить их, установите их URL в `None` при создании вашего приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
### Включение пользовательской документации
Теперь вы можете создать *path operations* для пользовательской документации.
Вы можете повторно использовать внутренние функции 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 | Совет
*Path operation* для `swagger_ui_redirect` является помощником, когда вы используете OAuth2.
Если вы интегрируете свой API с провайдером OAuth2, вы сможете авторизоваться и вернуться к документации API с полученными учетными данными. И взаимодействовать с ним, используя реальную аутентификацию через OAuth2.
Swagger UI сделает это для вас за кулисами, но для этого нужен этот помощник "перенаправления".
///
### Создание *path operation* для его тестирования
Теперь, чтобы проверить, что все работает, создайте *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
### Тестирование
Теперь вы должны иметь возможность перейти на свою документацию по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> и перезагрузить страницу, она загрузит эти ресурсы с нового CDN.
## Самостоятельный хостинг JavaScript и CSS для документации
Самостоятельный хостинг JavaScript и CSS может быть полезен, если, например, вам нужно, чтобы ваше приложение продолжало работать даже в автономном режиме, без доступа в интернет или в локальной сети.
Здесь вы увидите, как самостоятельно размещать эти файлы в том же приложении FastAPI и настраивать документацию для их использования.
### Структура файлов проекта
Допустим, ваша структура файлов проекта выглядит следующим образом:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Теперь создайте каталог для хранения этих статических файлов.
Ваша новая структура файлов может выглядеть так:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static/
```
### Загрузка файлов
Скачайте необходимые для документации статические файлы и поместите их в каталог `static/`.
Вы можете щелкнуть правой кнопкой мыши по каждой ссылке и выбрать опцию, аналогичную `Сохранить ссылку как...`.
**Swagger UI** использует файлы:
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" class="external-link" target="_blank">`swagger-ui-bundle.js`</a>
* <a href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css" class="external-link" target="_blank">`swagger-ui.css`</a>
А **ReDoc** использует файл:
* <a href="https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a>
После этого ваша структура файлов может выглядеть так:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Обслуживание статических файлов
* Импортируйте `StaticFiles`.
* "Подключите" экземпляр `StaticFiles()` в определенном пути.
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
### Тестирование статических файлов
Запустите приложение и перейдите по адресу <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>.
Вы должны увидеть очень длинный JavaScript файл для **ReDoc**.
Он может начинаться с чего-то вроде:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Это подтверждает, что вы можете обслуживать статические файлы из вашего приложения и что вы разместили статические файлы для документации в правильном месте.
Теперь мы можем настроить приложение для использования этих статических файлов в документации.
### Отключение автоматической документации для статических файлов
Как и при использовании пользовательского CDN, первым шагом является отключение автоматической документации, так как по умолчанию они используют CDN.
Чтобы отключить их, установите их URL в `None` при создании вашего приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
### Включение пользовательской документации для статических файлов
И так же, как с пользовательским CDN, теперь вы можете создать *path operations* для пользовательской документации.
Снова можно использовать внутренние функции 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 | Совет
*Path operation* для `swagger_ui_redirect` является помощником, когда вы используете OAuth2.
Если вы интегрируете свой API с провайдером OAuth2, вы сможете авторизоваться и вернуться к документации API с полученными учетными данными. И взаимодействовать с ним, используя реальную аутентификацию через OAuth2.
Swagger UI сделает это для вас за кулисами, но для этого нужен этот помощник "перенаправления".
///
### Создание *path operation* для тестирования статических файлов
Теперь, чтобы убедиться, что все работает, создайте *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
### Тестирование статических файлов 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 @@
# Классы кастомных запросов и APIRoute
В некоторых случаях вы можете захотеть переопределить логику, используемую классами `Request` и `APIRoute`.
В частности, это может быть хорошей альтернативой логике в middleware.
Например, если вы хотите прочитать или изменить тело запроса до его обработки вашим приложением.
/// danger | Опасность
Это "продвинутая" функция.
Если вы только начинаете с **FastAPI**, возможно, вам стоит пропустить этот раздел.
///
## Примеры использования
Некоторые примеры использования включают:
* Конвертацию тел запросов, которые не являются JSON, в JSON (например, <a href="https://msgpack.org/index.html" class="external-link" target="_blank">`msgpack`</a>).
* Распаковку тел запросов, сжатых с помощью gzip.
* Автоматическое логирование всех тел запросов.
## Обработка кастомных кодировок тел запросов
Давайте рассмотрим, как использовать подкласс `Request` для распаковки запросов в формате gzip.
И подкласс `APIRoute` для использования этого кастомного класса запроса.
### Создание кастомного класса `GzipRequest`
/// tip | Подсказка
Это учебный пример, демонстрирующий, как это работает; если вам нужна поддержка Gzip, вы можете использовать предоставленный [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}.
///
Сначала мы создаем класс `GzipRequest`, который будет переопределять метод `Request.body()` для распаковки тела при наличии соответствующего заголовка.
Если в заголовке нет `gzip`, он не будет пытаться распаковать тело.
Таким образом, один и тот же класс маршрута может обрабатывать как сжатые, так и несжатые запросы в формате gzip.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[8:15] *}
### Создание кастомного класса `GzipRoute`
Далее, мы создаем кастомный подкласс `fastapi.routing.APIRoute`, который будет использовать `GzipRequest`.
На этот раз он переопределит метод `APIRoute.get_route_handler()`.
Этот метод возвращает функцию. И именно эта функция принимает запрос и возвращает ответ.
Здесь мы используем его для создания `GzipRequest` из оригинального запроса.
{* ../../docs_src/custom_request_and_route/tutorial001.py hl[18:26] *}
/// note | Технические детали
`Request` имеет атрибут `request.scope`, это просто Python `dict`, содержащий метаданные, связанные с запросом.
`Request` также имеет `request.receive`, это функция для "получения" тела запроса.
`scope` `dict` и функция `receive` обе являются частью спецификации ASGI.
И эти две вещи, `scope` и `receive`, — то, что нужно для создания нового экземпляра `Request`.
Чтобы узнать больше о `Request`, ознакомьтесь с <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">документацией Starlette о запросах</a>.
///
Единственное, что функция, возвращаемая `GzipRequest.get_route_handler`, делает иначе — это преобразует `Request` в `GzipRequest`.
Делая это, наш `GzipRequest` позаботится о распаковке данных (если это необходимо) до передачи их нашим *операциям на пути*.
После этого вся логика обработки остается той же.
Но из-за наших изменений в `GzipRequest.body`, тело запроса будет автоматически распаковано, когда оно понадобится **FastAPI**.
## Доступ к телу запроса в обработчике исключений
/// tip | Подсказка
Для решения этой же проблемы, вероятно, гораздо проще использовать `body` в кастомном обработчике для `RequestValidationError` ([Обработка ошибок](../tutorial/handling-errors.md#use-the-requestvalidationerror-body){.internal-link target=_blank}).
Но этот пример все же действителен и показывает, как взаимодействовать с внутренними компонентами.
///
Мы также можем использовать этот же подход для доступа к телу запроса в обработчике исключений.
Все, что нам нужно сделать, это обработать запрос внутри блока `try`/`except`:
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[13,15] *}
Если произойдет исключение, экземпляр `Request` все равно будет в доступной области, так что мы сможем прочитать и использовать тело запроса при обработке ошибки:
{* ../../docs_src/custom_request_and_route/tutorial002.py hl[16:18] *}
## Кастомный класс `APIRoute` в маршрутизаторе
Вы также можете установить параметр `route_class` для `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *}
В этом примере операции пути под `router` будут использовать кастомный класс `TimedRoute` и добавят в ответ заголовок `X-Response-Time` с временем, затраченным на генерацию ответа:
{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *}

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

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

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

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

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

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

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

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

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

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

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

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

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

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