# Дополнительные статус-коды { #additional-status-codes }
По умолчанию **FastAPI** будет возвращать ответы, используя `JSONResponse`, помещая содержимое, которое вы возвращаете из вашей *операции пути*, внутрь этого `JSONResponse`.
Он будет использовать статус-код по умолчанию или тот, который вы укажете в вашей *операции пути*.
Вы можете определить логику (код), которую нужно выполнить перед тем, как приложение начнет запускаться. Это означает, что этот код будет выполнен один раз, перед тем как приложение начнет получать HTTP-запросы.
Вы можете определить логику (код), которую нужно выполнить перед тем, как приложение **запустится**. Это означает, что этот код будет выполнен **один раз**, **перед** тем как приложение **начнет получать HTTP-запросы**.
Аналогично, вы можете определить логику (код), которую нужно выполнить, когда приложение завершает работу. В этом случае код будет выполнен один раз, после обработки, возможно, многих запросов.
Аналогично, вы можете определить логику (код), которую нужно выполнить, когда приложение **завершает работу**. В этом случае код будет выполнен **один раз**, **после обработки**, возможно, **многих HTTP-запросов**.
Поскольку этот код выполняется до того, как приложение начинает принимать запросы, и сразу после того, как оно заканчивает их обрабатывать, он охватывает весь lifespan (жизненный цикл) приложения (слово «lifespan» станет важным через секунду 😉).
Поскольку этот код выполняется до того, как приложение **начинает** принимать HTTP-запросы, и сразу после того, как оно **заканчивает** их обрабатывать, он охватывает весь **lifespan** (жизненный цикл) приложения (слово «lifespan» станет важным через секунду 😉).
Это может быть очень полезно для настройки ресурсов, которые нужны для всего приложения, которые разделяются между запросами и/или которые нужно затем очистить. Например, пул подключений к базе данных или загрузка общей модели Машинного обучения.
Это может быть очень полезно для настройки **ресурсов**, которые нужны для всего приложения, которые **разделяются** между HTTP-запросами и/или которые нужно затем **очистить**. Например, пул подключений к базе данных или загрузка общей модели Машинного обучения.
## Вариант использования { #use-case }
Начнем с примера варианта использования, а затем посмотрим, как это решить.
Начнем с примера **варианта использования**, а затем посмотрим, как это решить.
Представим, что у вас есть несколько моделей Машинного обучения, которые вы хотите использовать для обработки запросов. 🤖
Представим, что у вас есть несколько **моделей Машинного обучения**, которые вы хотите использовать для обработки HTTP-запросов. 🤖
Эти же модели разделяются между запросами, то есть это не одна модель на запрос, не одна на пользователя и т.п.
Эти же модели разделяются между HTTP-запросами, то есть это не одна модель на HTTP-запрос, не одна на пользователя и т.п.
Представим, что загрузка модели может занимать довольно много времени, потому что ей нужно прочитать много данных с диска. Поэтому вы не хотите делать это для каждого запроса.
Представим, что загрузка модели может **занимать довольно много времени**, потому что ей нужно прочитать много **данных с диска**. Поэтому вы не хотите делать это для каждого HTTP-запроса.
Вы могли бы загрузить её на верхнем уровне модуля/файла, но это означало бы, что модель загружается даже если вы просто запускаете простой автоматический тест; тогда этот тест будет медленным, так как ему придется ждать загрузки модели перед запуском независимой части кода.
Вы могли бы загрузить её на верхнем уровне модуля/файла, но это означало бы, что модель будет **загружаться** даже если вы просто запускаете простой автоматический тест; тогда этот тест будет **медленным**, так как ему придется ждать загрузки модели перед запуском независимой части кода.
Именно это мы и решим: давайте загружать модель перед тем, как начнётся обработка запросов, но только непосредственно перед тем, как приложение начнет принимать запросы, а не во время загрузки кода.
Именно это мы и решим: давайте загружать модель перед тем, как начнётся обработка HTTP-запросов, но только непосредственно перед тем, как приложение начнет принимать HTTP-запросы, а не во время загрузки кода.
## Lifespan { #lifespan }
Вы можете определить логику для startup и shutdown, используя параметр `lifespan` приложения `FastAPI` и «менеджер контекста» (через секунду покажу что это).
Вы можете определить логику для *startup* и *shutdown*, используя параметр `lifespan` приложения `FastAPI` и «менеджер контекста» (через секунду покажу что это).
Здесь мы симулируем дорогую операцию startup по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен до того, как приложение начнет принимать запросы, во время startup.
Здесь мы симулируем дорогую операцию *startup* по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен **до** того, как приложение **начнет принимать HTTP-запросы**, во время *startup*.
А затем сразу после `yield` мы выгружаем модель. Этот код будет выполнен после того, как приложение закончит обрабатывать запросы, непосредственно перед shutdown. Это может, например, освободить ресурсы, такие как память или GPU.
А затем сразу после `yield` мы выгружаем модель. Этот код будет выполнен **после** того, как приложение **закончит обрабатывать HTTP-запросы**, непосредственно перед *shutdown*. Это может, например, освободить ресурсы, такие как память или GPU.
/// tip | Совет
`shutdown` произойдёт, когда вы останавливаете приложение.
`shutdown` произойдёт, когда вы **останавливаете** приложение.
Возможно, вам нужно запустить новую версию, или вы просто устали от него. 🤷
Менеджер контекста в Python — это то, что можно использовать в операторе `with`. Например, `open()` можно использовать как менеджер контекста:
**Менеджер контекста** в Python — это то, что можно использовать в операторе `with`. Например, `open()` можно использовать как менеджер контекста:
```Python
with open("file.txt") as file:
file.read()
```
В последних версиях Python есть также асинхронный менеджер контекста. Его используют с `async with`:
В последних версиях Python есть также **асинхронный менеджер контекста**. Его используют с `async with`:
```Python
async with lifespan(app):
@ -80,7 +80,7 @@ async with lifespan(app):
В нашем примере выше мы не используем его напрямую, а передаём его в FastAPI, чтобы он использовал его сам.
Параметр `lifespan` приложения `FastAPI` принимает асинхронный менеджер контекста, поэтому мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
Параметр `lifespan` приложения `FastAPI` принимает **асинхронный менеджер контекста**, поэтому мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
Рекомендуемый способ обрабатывать startup и shutdown — использовать параметр `lifespan` приложения `FastAPI`, как описано выше. Если вы укажете параметр `lifespan`, обработчики событий `startup` и `shutdown` больше вызываться не будут. Либо всё через `lifespan`, либо всё через события — не одновременно.
Рекомендуемый способ обрабатывать *startup* и *shutdown* — использовать параметр `lifespan` приложения `FastAPI`, как описано выше. Если вы укажете параметр `lifespan`, обработчики событий `startup` и `shutdown` больше вызываться не будут. Либо всё через `lifespan`, либо всё через события — не одновременно.
Эту часть, скорее всего, можно пропустить.
///
Есть альтернативный способ определить логику, которую нужно выполнить во время startup и во время shutdown.
Есть альтернативный способ определить логику, которую нужно выполнить во время *startup* и во время *shutdown*.
Вы можете определить обработчики событий (функции), которые нужно выполнить до старта приложения или при его завершении.
@ -110,7 +110,7 @@ async with lifespan(app):
Вы можете добавить более одного обработчика события.
И ваше приложение не начнет принимать запросы, пока все обработчики события `startup` не завершатся.
И ваше приложение не начнет принимать HTTP-запросы, пока все обработчики события `startup` не завершатся.
### Событие `shutdown` { #shutdown-event }
@ -140,7 +140,7 @@ async with lifespan(app):
### `startup` и `shutdown` вместе { #startup-and-shutdown-together }
С высокой вероятностью логика для вашего startup и shutdown связана: вы можете хотеть что-то запустить, а затем завершить, получить ресурс, а затем освободить его и т.д.
С высокой вероятностью логика для вашего *startup* и *shutdown* связана: вы можете хотеть что-то запустить, а затем завершить, получить ресурс, а затем освободить его и т.д.
Делать это в отдельных функциях, которые не разделяют общую логику или переменные, сложнее, так как придётся хранить значения в глобальных переменных или использовать похожие приёмы.
@ -148,9 +148,9 @@ async with lifespan(app):
## Технические детали { #technical-details }
Немного технических подробностей для любопытных умников. 🤓
Просто техническая подробность для любопытных умников. 🤓
Под капотом, в ASGI-технической спецификации, это часть [Протокола Lifespan](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), и он определяет события `startup` и `shutdown`.
Под капотом, в технической спецификации ASGI, это часть [Протокола Lifespan](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), и он определяет события `startup` и `shutdown`.
/// note | Примечание
@ -162,4 +162,4 @@ async with lifespan(app):
## Подприложения { #sub-applications }
🚨 Имейте в виду, что эти события lifespan (startup и shutdown) будут выполнены только для основного приложения, а не для [Подприложения — Mounts](sub-applications.md).
🚨 Имейте в виду, что эти события lifespan (startup и shutdown) будут выполнены только для основного приложения, а не для [Подприложений - Mounts](sub-applications.md).
@ -20,20 +20,6 @@ FastAPI автоматически генерирует спецификации
///
## Генераторы SDK от спонсоров FastAPI { #sdk-generators-from-fastapi-sponsors }
В этом разделе представлены решения с **венчурной поддержкой** и **поддержкой компаний** от компаний, которые спонсируют FastAPI. Эти продукты предоставляют **дополнительные возможности** и **интеграции** сверх высококачественно генерируемых SDK.
Благодаря ✨ [**спонсорству FastAPI**](../help-fastapi.md#sponsor-the-author) ✨ эти компании помогают обеспечивать, чтобы фреймворк и его **экосистема** оставались здоровыми и **устойчивыми**.
Их спонсорство также демонстрирует серьёзную приверженность **сообществу** FastAPI (вам), показывая, что им важно не только предоставлять **отличный сервис**, но и поддерживать **надёжный и процветающий фреймворк** FastAPI. 🙇
Некоторые из этих решений также могут быть open source или иметь бесплатные тарифы, так что вы сможете попробовать их без финансовых затрат. Другие коммерческие генераторы SDK доступны и их можно найти онлайн. 🤓
## Создать TypeScript SDK { #create-a-typescript-sdk }
Сначала рассмотрите возможность использовать [Файлы в запросе](../tutorial/request-files.md) для загрузки бинарных данных и [Пользовательский HTTP-ответ — FileResponse](./custom-response.md#fileresponse--fileresponse-) для отправки бинарных данных вместо кодирования их в JSON.
Сначала рассмотрите возможность использовать [Файлы в запросе](../tutorial/request-files.md) для загрузки бинарных данных и [Пользовательский HTTP-ответ — FileResponse](./custom-response.md#fileresponse) для отправки бинарных данных вместо кодирования их в JSON.
JSON может содержать только строки в кодировке UTF-8, поэтому он не может содержать «сырые» байты.
@ -14,7 +14,7 @@ Base64 может кодировать бинарные данные в стро
## Pydantic `bytes` { #pydantic-bytes }
Вы можете объявить Pydantic-модель с полями `bytes`, а затем использовать `val_json_bytes` в конфиге модели, чтобы указать использовать base64 для валидации входящих JSON-данных; как часть этой валидации строка base64 будет декодирована в байты.
Вы можете объявить Pydantic-модель с полями `bytes`, а затем использовать `val_json_bytes` в конфиге модели, чтобы указать использовать base64 для *валидации* входящих JSON-данных; как часть этой валидации строка base64 будет декодирована в байты.
@ -52,12 +52,12 @@ Base64 может кодировать бинарные данные в стро
## Pydantic `bytes` для выходных данных { #pydantic-bytes-for-output-data }
Вы также можете использовать поля `bytes` с `ser_json_bytes` в конфиге модели для выходных данных, и Pydantic будет сериализовать байты в base64 при формировании JSON-ответа.
Вы также можете использовать поля `bytes` с `ser_json_bytes` в конфиге модели для выходных данных, и Pydantic будет *сериализовать* байты в base64 при формировании JSON-ответа.
## Pydantic `bytes` для входных и выходных данных { #pydantic-bytes-for-input-and-output-data }
И, конечно, вы можете использовать одну и ту же модель, настроенную на использование base64, чтобы обрабатывать и входящие данные (валидация) с `val_json_bytes`, и исходящие данные (сериализация) с `ser_json_bytes` при приеме и отправке JSON-данных.
И, конечно, вы можете использовать одну и ту же модель, настроенную на использование base64, чтобы обрабатывать и входящие данные (*валидировать*) с `val_json_bytes`, и исходящие данные (*сериализовать*) с `ser_json_bytes` при приеме и отправке JSON-данных.
# Обратные вызовы в OpenAPI { #openapi-callbacks }
Вы можете создать API с *операцией пути* (обработчиком пути), которая будет инициировать HTTP-запрос к *внешнему API*, созданному кем-то другим (скорее всего тем же разработчиком, который будет использовать ваш API).
Вы можете создать API с *операцией пути* (обработчиком пути), которая будет инициировать HTTP-запрос к *внешнему API*, созданному кем-то другим (скорее всего тем же разработчиком, который будет *использовать* ваш API).
Процесс, происходящий, когда ваше приложение API обращается к *внешнему API*, называется «callback» (обратный вызов). Программное обеспечение, написанное внешним разработчиком, отправляет HTTP-запрос вашему API, а затем ваш API выполняет обратный вызов, отправляя HTTP-запрос во *внешний API* (который, вероятно, тоже создал тот же разработчик).
Процесс, происходящий, когда ваше приложение API обращается к *внешнему API*, называется «callback» (обратный вызов). Потому что программное обеспечение, написанное внешним разработчиком, отправляет HTTP-запрос вашему API, а затем ваш API выполняет обратный вызов, отправляя HTTP-запрос во *внешний API* (который, вероятно, тоже создал тот же разработчик).
В этом случае вам может понадобиться задокументировать, как должно выглядеть это внешнее API: какую *операцию пути* оно должно иметь, какое тело запроса ожидать, какой ответ возвращать и т.д.
В этом случае вам может понадобиться задокументировать, как это внешнее API *должно* выглядеть: какую *операцию пути* оно должно иметь, какое тело запроса ожидать, какой HTTP-ответ возвращать и т.д.
## Приложение с обратными вызовами { #an-app-with-callbacks }
Когда вы пишете код для документирования обратного вызова, полезно представить, что вы — тот самый *внешний разработчик*. И что вы сейчас реализуете *внешний API*, а не *свой API*.
Временное принятие этой точки зрения (внешнего разработчика) поможет интуитивно понять, куда поместить параметры, какую Pydantic-модель использовать для тела запроса, для ответа и т.д. во *внешнем API*.
Временное принятие этой точки зрения (внешнего разработчика) поможет интуитивно понять, куда поместить параметры, какую Pydantic-модель использовать для тела запроса, для HTTP-ответа и т.д. во *внешнем API*.
К этому моменту у вас есть необходимые *операции пути* обратного вызова (те, которые *внешний разработчик* должен реализовать во *внешнем API*) в созданном выше маршрутизаторе обратных вызовов.
К этому моменту у вас есть необходимые *операции пути* обратного вызова (те, которые *внешний разработчик* должен реализовать во *внешнем API*) в созданном выше роутере обратных вызовов.
Теперь используйте параметр `callbacks` в *декораторе операции пути вашего API*, чтобы передать атрибут `.routes` из этого маршрутизатора обратных вызовов:
Теперь используйте параметр `callbacks` в *декораторе операции пути вашего API*, чтобы передать атрибут `.routes` из этого роутера обратных вызовов:
Обратите внимание, что вы передаёте не сам маршрутизатор (`invoices_callback_router`) в `callback=`, а его атрибут `.routes`, то есть `invoices_callback_router.routes`. FastAPI будет использовать эти маршруты для генерации документации OpenAPI для обратных вызовов.
Обратите внимание, что вы передаёте не сам роутер (`invoices_callback_router`) в `callbacks=`, а его атрибут `.routes`, то есть `invoices_callback_router.routes`. FastAPI будет использовать эти маршруты для генерации документации OpenAPI для обратных вызовов.
## Использовать параметр `Response` { #use-a-response-parameter }
Вы можете объявить параметр типа `Response` в вашей функции-обработчике пути (как можно сделать и для cookie).
Вы можете объявить параметр типа `Response` в вашей *функции-обработчике пути* (как можно сделать и для cookie).
А затем вы можете устанавливать HTTP-заголовки в этом *временном* объекте ответа.
@ -14,13 +14,13 @@
**FastAPI** использует этот *временный* ответ, чтобы извлечь HTTP-заголовки (а также cookie и статус-код) и поместит их в финальный HTTP-ответ, который содержит возвращённое вами значение, отфильтрованное согласно `response_model`.
Вы также можете объявлять параметр `Response` в зависимостях и устанавливать в них заголовки (и cookie).
Вы также можете объявлять параметр `Response` в зависимостях и устанавливать в них HTTP-заголовки (и cookie).
## Вернуть `Response` напрямую { #return-a-response-directly }
Вы также можете добавить HTTP-заголовки, когда возвращаете `Response` напрямую.
Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md), и передайте заголовки как дополнительный параметр:
Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md), и передайте HTTP-заголовки как дополнительный параметр:
**FastAPI** предоставляет те же самые `starlette.responses` как `fastapi.responses` — для вашего удобства как разработчика. Но большинство доступных классов ответов поступают напрямую из Starlette.
И поскольку `Response` часто используется для установки заголовков и cookie, **FastAPI** также предоставляет его как `fastapi.Response`.
И поскольку `Response` часто используется для установки HTTP-заголовков и cookie, **FastAPI** также предоставляет его как `fastapi.Response`.
Помните, что собственные проприетарные заголовки можно добавлять, [используя префикс `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Помните, что собственные проприетарные HTTP-заголовки можно добавлять, [используя префикс `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Но если у вас есть пользовательские заголовки, которые вы хотите показывать клиенту в браузере, вам нужно добавить их в настройки CORS (подробнее см. в [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), используя параметр `expose_headers`, описанный в [документации Starlette по CORS](https://www.starlette.dev/middleware/#corsmiddleware).
Но если у вас есть пользовательские HTTP-заголовки, которые вы хотите показывать клиенту в браузере, вам нужно добавить их в настройки CORS (подробнее см. в [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), используя параметр `expose_headers`, описанный в [документации Starlette по CORS](https://www.starlette.dev/middleware/#corsmiddleware).
### Создание `Settings` только один раз с помощью `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Чтение файла с диска обычно затратная (медленная) операция, поэтому, вероятно, вы захотите сделать это один раз и затем переиспользовать один и тот же объект настроек, а не читать файл при каждом запросе.
Чтение файла с диска обычно затратная (медленная) операция, поэтому, вероятно, вы захотите сделать это один раз и затем переиспользовать один и тот же объект настроек, а не читать файл при каждом HTTP-запросе.
Но каждый раз, когда мы делаем:
@ -222,13 +222,13 @@ def get_settings():
return Settings()
```
мы бы создавали этот объект для каждого запроса и читали файл `.env` на каждый запрос. ⚠️
мы бы создавали этот объект для каждого HTTP-запроса и читали файл `.env` на каждый HTTP-запрос. ⚠️
Но так как мы используем декоратор `@lru_cache` сверху, объект `Settings` будет создан только один раз — при первом вызове. ✔️
Затем при любых последующих вызовах `get_settings()` в зависимостях для следующих запросов, вместо выполнения внутреннего кода `get_settings()` и создания нового объекта `Settings`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова.
Затем при любых последующих вызовах `get_settings()` в зависимостях для следующих HTTP-запросов, вместо выполнения внутреннего кода `get_settings()` и создания нового объекта `Settings`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова.
@ -299,4 +299,4 @@ participant execute as Execute function
* Используя зависимость, вы упрощаете тестирование.
* Можно использовать файлы `.env`.
* `@lru_cache` позволяет не читать файл dotenv снова и снова для каждого запроса, при этом давая возможность переопределять его во время тестирования.
* `@lru_cache` позволяет не читать файл dotenv снова и снова для каждого HTTP-запроса, при этом давая возможность переопределять его во время тестирования.
Если вам нужно передавать потоковые данные, которые можно представить как JSON, воспользуйтесь [стримингом JSON Lines](../tutorial/stream-json-lines.md).
Но если вы хотите передавать в потоке чистые бинарные данные или строки, ниже показано, как это сделать.
Но если вы хотите передавать в потоке **чистые бинарные данные** или строки, ниже показано, как это сделать.
/// note | Примечание
@ -40,7 +40,7 @@ FastAPI будет передавать каждый чанк данных в `S
Это также означает, что с `StreamingResponse` у вас есть и свобода, и ответственность — производить и кодировать байты данных ровно в том виде, в котором они должны быть отправлены, независимо от аннотаций типов. 🤓
Это также означает, что с `StreamingResponse` у вас есть и **свобода**, и **ответственность** — производить и кодировать байты данных ровно в том виде, в котором они должны быть отправлены, независимо от аннотаций типов. 🤓
Он относительно тесно связан с реляционными базами данных (например, MySQL или PostgreSQL), поэтому использовать NoSQL-базу данных (например, Couchbase, MongoDB, Cassandra и т. п.) в качестве основного хранилища не очень просто.
Он был создан для генерации HTML на бэкенде, а не для создания API, используемых современным фронтендом (например, React, Vue.js и Angular) или другими системами (например, устройствами <abbrtitle="Internet of Things – Интернет вещей">IoT</abbr>), которые с ним общаются.
Он был создан для генерации HTML на бэкенде, а не для создания API, используемых современным фронтендом (например, React, Vue.js и Angular) или другими системами (например, устройствами <abbrtitle="Internet of Things - Интернет вещей">IoT</abbr>), которые с ним общаются.
Тогда объявляйте *функции-обработчиков пути* с `async def`, например:
Тогда объявляйте *функции-обработчики пути* с `async def`, например:
```Python hl_lines="2"
@app.get('/')
@ -29,7 +29,7 @@ async def read_results():
---
Если вы используете стороннюю библиотеку, которая взаимодействует с чем-то (база данных, API, файловая система и т.д.) и не поддерживает использование `await` (сейчас это относится к большинству библиотек для БД), тогда объявляйте *функции-обработчиков пути* как обычно, просто с `def`, например:
Если вы используете стороннюю библиотеку, которая взаимодействует с чем-то (база данных, API, файловая система и т.д.) и не поддерживает использование `await` (сейчас это относится к большинству библиотек для БД), тогда объявляйте *функции-обработчики пути* как обычно, просто с `def`, например:
```Python hl_lines="2"
@app.get('/')
@ -48,7 +48,7 @@ def results():
---
**Примечание**: вы можете смешивать `def` и `async def` в *функциях-обработчиков пути* столько, сколько нужно, и объявлять каждую так, как лучше для вашего случая. FastAPI сделает с ними всё как надо.
**Примечание**: вы можете смешивать `def` и `async def` в *функциях-обработчиках пути* столько, сколько нужно, и объявлять каждую так, как лучше для вашего случая. FastAPI сделает с ними всё как надо.
В любом из случаев выше FastAPI всё равно работает асинхронно и очень быстро.
@ -249,7 +249,7 @@ def results():
Именно такая асинхронность сделала NodeJS популярным (хотя NodeJS — не параллельный), и это сильная сторона Go как языка программирования.
Того же уровня производительности вы получаете с **FastAPI**.
Тот же уровень производительности вы получаете с **FastAPI**.
А так как можно одновременно использовать параллелизм и асинхронность, вы получаете производительность выше, чем у большинства протестированных фреймворков на NodeJS и на уровне Go, который — компилируемый язык, ближе к C [(всё благодаря Starlette)](https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1).
@ -340,7 +340,7 @@ burgers = get_burgers(2)
---
Итак, если вы используете библиотеку, которую можно вызывать с `await`, вам нужно создать *функцию-обработчик пути*, которая её использует, с `async def`, например:
Итак, если вы используете библиотеку, которую можно вызывать с `await`, вам нужно создать *функции-обработчики пути*, которые её используют, с `async def`, например:
Не беспокойтесь, если некоторые пункты про **контейнеры**, Docker или Kubernetes пока кажутся неочевидными.
Я расскажу больше про образы контейнеров, Docker, Kubernetes и т.п. в следующей главе: [FastAPI внутри контейнеров — Docker](docker.md).
Я расскажу больше про образы контейнеров, Docker, Kubernetes и т.п. в одной из будущих глав: [FastAPI внутри контейнеров — Docker](docker.md).
///
@ -281,7 +281,7 @@
/// tip | Совет
Я приведу более конкретные примеры с контейнерами в следующей главе: [FastAPI внутри контейнеров — Docker](docker.md).
Я приведу более конкретные примеры с контейнерами в одной из будущих глав: [FastAPI внутри контейнеров — Docker](docker.md).
///
@ -301,9 +301,9 @@
Также возможен **всплеск** использования вашего API: он мог «взорваться» по популярности, или какие‑то сервисы/боты начали его активно использовать. На такие случаи стоит иметь запас ресурсов.
Можно задать **целевое значение**, например **между 50% и 90%** использования ресурсов. Скорее всего, именно эти вещи вы будете измерять и на их основе настраивать развёртывание.
Можно задать **произвольное число** в качестве цели, например **между 50% и 90%** использования ресурсов. Скорее всего, именно эти вещи вы будете измерять и на их основе настраивать развёртывание.
Можно использовать простые инструменты вроде `htop`, чтобы смотреть загрузку CPU и RAM на сервере или по процессам. Или более сложные распределённые системы мониторинга.
Можно использовать простые инструменты вроде `htop`, чтобы смотреть загрузку CPU и RAM на сервере или по процессам. Или более сложные инструменты мониторинга, которые могут быть распределены по серверам и т.п.
@ -275,7 +275,7 @@ CMD fastapi run app/main.py --port 80
#### За прокси-сервером TSL-терминации { #behind-a-tls-termination-proxy }
Если вы запускаете контейнер за прокси-сервером TSL-терминации (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.
Если вы запускаете контейнер за прокси-сервером TSL-терминации (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что можно доверять заголовкам, отправленным этим прокси и сообщающим, что приложение работает за HTTPS, и т.д.
1. Копируем файл `main.py` напрямую в `/code` (без директории `./app`).
2. Используем `fastapi run` для запуска приложения из одного файла `main.py`.
2. Используем `fastapi run`, чтобы «отдавать» приложение из одного файла `main.py`.
Когда вы передаёте файл в `fastapi run`, он автоматически определит, что это одиночный файл, а не часть пакета, и поймёт, как его импортировать и запустить ваше FastAPI-приложение. 😎
Когда вы передаёте файл в `fastapi run`, он автоматически определит, что это одиночный файл, а не часть пакета, и поймёт, как импортировать и «отдавать» ваше FastAPI-приложение. 😎
## Концепции развертывания { #deployment-concepts }
Вы можете развёртывать на **одном сервере** (не кластере) с **Docker Compose**, и у вас не будет простого способа управлять репликацией контейнеров (в Docker Compose), сохраняя общую сеть и **балансировку нагрузки**.
Тогда вы можете захотеть **один контейнер** с **менеджером процессов**, который запускает **несколько воркеров** внутри.
Тогда вы можете захотеть **один контейнер** с **менеджером процессов**, который запускает **несколько воркер-процессов** внутри.
Если у вас простая схема с **одним контейнером**, который затем запускает несколько **воркеров** (или один процесс), можно выполнить подготовительные шаги в этом же контейнере непосредственно перед запуском процесса с приложением.
Если у вас простая схема с **одним контейнером**, который затем запускает несколько **воркер-процессов** (или один процесс), можно выполнить подготовительные шаги в этом же контейнере непосредственно перед запуском процесса с приложением.
Этот Docker-образ был создан в то время, когда Uvicorn не умел управлять и перезапускать «упавших» воркеров, и приходилось использовать Gunicorn вместе с Uvicorn, что добавляло заметную сложность, лишь бы Gunicorn управлял и перезапускал воркеров Uvicorn.
Этот Docker-образ был создан в то время, когда Uvicorn не умел управлять и перезапускать «упавших» воркеров, и приходилось использовать Gunicorn вместе с Uvicorn, что добавляло заметную сложность, лишь бы Gunicorn управлял и перезапускал воркер-процессы Uvicorn.
Но теперь, когда Uvicorn (и команда `fastapi`) поддерживают `--workers`, нет причин использовать базовый Docker-образ вместо сборки своего (кода получается примерно столько же 😅).
В большинстве случаев вы, вероятно, не захотите использовать какой-либо базовый образ, а вместо этого **соберёте образ контейнера с нуля** на основе официального Docker-образа Python.
Заботясь о **порядке**инструкций в `Dockerfile` и используя **кэш Docker**, вы можете **минимизировать время сборки**, чтобы повысить продуктивность (и не скучать). 😎
Заботясь о **порядке** инструкций в `Dockerfile` и используя **кэш Docker**, вы можете **минимизировать время сборки**, чтобы повысить продуктивность (и не скучать). 😎
Если вы торопитесь или вам это не важно, переходите к следующим разделам с пошаговыми инструкциями по настройке всего разными способами.
Если вы торопитесь или вам это не важно, продолжайте со следующих разделов с пошаговыми инструкциями по настройке всего разными способами.
///
@ -65,7 +65,7 @@
Чаще всего всё начинается с **приобретения****имени домена**. Затем вы настраиваете его на DNS‑сервере (возможно, у того же облачного провайдера).
Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет <dfntitle="Со временем не меняется. Не динамический.">постоянный</dfn>**публичный IP-адрес**.
Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет <dfntitle="Не меняется со временем. Не динамический.">постоянный</dfn>**публичный IP-адрес**.
На DNS‑сервере(ах) вы настроите запись («`A record`» - запись типа A), указывающую, что **ваш домен** должен указывать на публичный **IP‑адрес вашего сервера**.
@ -194,7 +194,7 @@ DNS‑серверы ответят браузеру, какой **конкре
Когда вы используете прокси для обработки HTTPS, ваш **сервер приложения** (например, Uvicorn через FastAPI CLI) ничего не знает о процессе HTTPS, он общается обычным HTTP с **прокси‑сервером TLS-терминации**.
Обычно этот **прокси** на лету добавляет некоторые HTTP‑заголовки перед тем, как переслать запрос на **сервер приложения**, чтобы тот знал, что запрос был **проксирован**.
Обычно этот **прокси** на лету добавляет некоторые HTTP‑заголовки перед тем, как переслать запрос на **сервер приложения**, чтобы тот знал, что запрос был **переслан** прокси.
/// note | Технические детали
@ -218,7 +218,7 @@ DNS‑серверы ответят браузеру, какой **конкре
/// tip | Совет
Подробнее об этом вы можете узнать в документации: [За прокси — Включить пересылаемые заголовки прокси](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
Подробнее об этом вы можете узнать в документации: [За прокси — включить пересылаемые заголовки прокси](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
- **Развернуть в FastAPI Cloud** — развертывание вашего приложения в один клик в [FastAPI Cloud](https://fastapicloud.com/).
- **Поток логов приложения** — потоковая передача логов в реальном времени из вашего приложения, развернутого в FastAPI Cloud, с фильтрацией по уровню и текстовым поиском.
Если вы хотите поверхностно ознакомиться с возможностями расширения, откройте палитру команд (<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> или на macOS: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>), выберите «Welcome: Open walkthrough...», а затем «Get started with FastAPI».
Если вы хотите ознакомиться с возможностями расширения, вы можете посмотреть walkthrough расширения, открыв палитру команд (<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> или на macOS: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>) и выбрав «Welcome: Open walkthrough...», а затем walkthrough «Get started with FastAPI».
## Чтение переменных окружения в python { #read-env-vars-in-python }
## Чтение переменных окружения в Python { #read-env-vars-in-python }
Также существует возможность создания переменных окружения **вне** Python, в терминале (или любым другим способом), а затем **чтения их в Python**.
Также существует возможность создания переменных окружения **вне** Python, в терминале (или любым другим способом), а затем **чтения их в Python**.
Например, у вас есть файл `main.py`:
@ -67,7 +67,7 @@ print(f"Hello {name} from Python")
Второй аргумент [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) - это возвращаемое по умолчанию значение.
Если значение не указано, то по умолчанию оно равно `None`. В данном случае мы указываем `«World»` в качестве значения по умолчанию.
Если значение не указано, то по умолчанию оно равно `None`. В данном случае мы указываем `"World"` в качестве значения по умолчанию.
///
@ -157,13 +157,13 @@ Hello World from Python
///
## Типизация и Валидация { #types-and-validation }
## Типы и валидация { #types-and-validation }
Эти переменные окружения могут работать только с **текстовыми строками**, поскольку они являются внешними по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с различными операционными системами, такими как Linux, Windows, macOS).
Это означает, что **любое значение**, считанное в Python из переменной окружения, **будет `str`**, и любое преобразование к другому типу или любая валидация должны быть выполнены в коде.
Подробнее об использовании переменных окружения для работы с **настройками приложения** вы узнаете в [Расширенное руководство пользователя - Настройки и переменные среды](./advanced/settings.md).
Подробнее об использовании переменных окружения для работы с **настройками приложения** вы узнаете в [Расширенном руководстве пользователя - Настройки и переменные окружения](./advanced/settings.md).
Эта информация будет полезна при изучении [Виртуальных окружений](virtual-environments.md).
Эта информация будет полезна при изучении [виртуальных окружений](virtual-environments.md).
## Вывод { #conclusion }
Благодаря этому вы должны иметь базовое представление о том, что такое **переменные окружения** и как использовать их в Python.
Подробнее о них вы также можете прочитать в [статье о переменных окружения на википедии](https://en.wikipedia.org/wiki/Environment_variable).
Подробнее о них вы также можете прочитать в [статье о переменных окружения на Википедии](https://en.wikipedia.org/wiki/Environment_variable).
Во многих случаях не всегда очевидно, как переменные окружения могут быть полезны и применимы. Но они постоянно появляются в различных сценариях разработки, поэтому знать о них полезно.
Например, эта информация понадобится вам в следующем разделе, посвященном [Виртуальным окружениям](virtual-environments.md).
Например, эта информация понадобится вам в следующем разделе, посвященном [виртуальным окружениям](virtual-environments.md).

* Альтернативная документация API в [**ReDoc**](https://github.com/Rebilly/ReDoc).
@ -36,7 +36,7 @@ from datetime import date
from pydantic import BaseModel
# Объявляем параметр как `str`
# Объявляем переменную как `str`
# и получаем поддержку редактора кода внутри функции
def main(user_id: str):
return user_id
@ -71,9 +71,9 @@ my_second_user: User = User(**second_user_data)
///
### Поддержка редакторов { #editor-support }
### Поддержка редакторов кода { #editor-support }
Весь фреймворк был продуман так, чтобы быть простым и интуитивно понятным в использовании, все решения были проверены на множестве редакторов еще до начала разработки, чтобы обеспечить наилучшие условия при написании кода.
Весь фреймворк был продуман так, чтобы быть простым и интуитивно понятным в использовании, все решения были проверены на множестве редакторов кода еще до начала разработки, чтобы обеспечить наилучшие условия при написании кода.
В опросах Python‑разработчиков видно, [что одной из самых часто используемых функций является «автозавершение»](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
@ -81,15 +81,15 @@ my_second_user: User = User(**second_user_data)
Вам редко нужно будет возвращаться к документации.
Вот как ваш редактор может вам помочь:
Вот как ваш редактор кода может вам помочь:
* в [Visual Studio Code](https://code.visualstudio.com/):
Вы будете получать автозавершение кода даже там, где вы считали это невозможным раньше. Как пример, ключ `price` внутри тела JSON (который может быть вложенным), приходящего в запросе.
@ -151,11 +151,11 @@ FastAPI включает в себя чрезвычайно простую в и
Любая интеграция разработана настолько простой в использовании (с зависимостями), что вы можете создать «плагин» для своего приложения в пару строк кода, используя ту же структуру и синтаксис, что и для ваших *операций пути*.
### Проверен { #tested }
### Протестирован { #tested }
* 100% <dfntitle="Количество автоматически проверяемого кода">покрытие тестами</dfn>.
* 100% <dfntitle="Количество автоматически протестированного кода">покрытие тестами</dfn>.
* 100% <dfntitle="Аннотации типов Python, благодаря которым ваш редактор кода и внешние инструменты могут обеспечить вам лучшую поддержку">аннотирование типов</dfn> в кодовой базе.
* Используется в продакшн‑приложениях.
* Используется в приложениях в продакшн.
## Возможности Starlette { #starlette-features }
@ -190,7 +190,7 @@ FastAPI включает в себя чрезвычайно простую в и
* **Никакой нервотрёпки**:
* Не нужно изучать новые схемы в микроязыках.
* Если вы знаете типы в Python, вы знаете, как использовать Pydantic.
* Прекрасно сочетается с вашим **<abbrtitle="Integrated Development Environment - Интегрированная среда разработки: похоже на редактор кода">IDE</abbr>/<dfntitle="Программа, которая проверяет код на ошибки">линтер</dfn>/мозгом**:
* Прекрасно сочетается с вашим **<abbrtitle="Integrated Development Environment - Интегрированная среда разработки: похожая на редактор кода">IDE</abbr>/<dfntitle="Программа, которая проверяет код на ошибки">линтер</dfn>/мозгом**:
* Потому что структуры данных pydantic — это всего лишь экземпляры классов, определённых вами; автозавершение, проверка кода, mypy и ваша интуиция — всё будет работать с вашими валидированными данными.
* Валидация **сложных структур**:
* Использование иерархических моделей Pydantic; `List`, `Dict` и т.п. из модуля `typing`.
Вы можете подписаться на [меня (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com) в нескольких местах, чтобы узнавать новости о FastAPI и друзьях:
Вы можете подписаться на [меня (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), автора, в нескольких местах, чтобы узнавать новости о FastAPI и друзьях:
* [@tiangolo в **GitHub**](https://github.com/tiangolo).
* [@tiangolo в **X (Twitter)**](https://x.com/tiangolo)
* Преобразование тел запросов, не в формате JSON, в JSON (например, [`msgpack`](https://msgpack.org/index.html)).
* Преобразование тел запросов не в формате JSON в JSON (например, [`msgpack`](https://msgpack.org/index.html)).
* Распаковка тел запросов, сжатых с помощью gzip.
* Автоматическое логирование всех тел запросов.
## Обработка пользовательского кодирования тела запроса { #handling-custom-request-body-encodings }
Посмотрим как использовать пользовательский подкласс `Request` для распаковки gzip-запросов.
Посмотрим, как использовать пользовательский подкласс `Request` для распаковки gzip-запросов.
И подкласс `APIRoute`, чтобы использовать этот пользовательский класс запроса.
@ -38,9 +38,9 @@
Сначала создадим класс `GzipRequest`, который переопределит метод `Request.body()` и распакует тело запроса при наличии соответствующего HTTP-заголовка.
Если в заголовке нет `gzip`, он не будет пытаться распаковывать тело.
Если в HTTP-заголовке нет `gzip`, он не будет пытаться распаковывать тело.
Таким образом, один и тот же класс маршрута сможет обрабатывать как gzip-сжатые, так и несжатые запросы.
Таким образом, один и тот же класс маршрута сможет обрабатывать как gzip-сжатые, так и несжатые HTTP-запросы.
В этом примере *операции пути*, объявленные в `router`, будут использовать пользовательский класс `TimedRoute` и получат дополнительный HTTP-заголовок `X-Response-Time` в ответе с временем, затраченным на формирование ответа:
В этом примере *операции пути*, объявленные в `router`, будут использовать пользовательский класс `TimedRoute` и получат дополнительный HTTP-заголовок `X-Response-Time` в HTTP-ответе с временем, затраченным на формирование HTTP-ответа:
@ -8,6 +8,8 @@ FastAPI версии 0.119.0 добавил частичную поддержк
FastAPI 0.126.0 убрал поддержку Pydantic v1, при этом ещё некоторое время продолжал поддерживать `pydantic.v1`.
FastAPI 0.128.0 также убрал поддержку `pydantic.v1`, так что последние версии FastAPI требуют Pydantic v2.
/// warning | Предупреждение
Команда Pydantic прекратила поддержку Pydantic v1 для последних версий Python, начиная с **Python 3.14**.
@ -54,6 +56,16 @@ Pydantic v2 включает всё из Pydantic v1 как подмодуль `
### Поддержка FastAPI для Pydantic v1 внутри v2 { #fastapi-support-for-pydantic-v1-in-v2 }
/// warning | Предупреждение
Эта поддержка FastAPI для моделей `pydantic.v1` была добавлена в **FastAPI 0.119.0** и удалена в **FastAPI 0.128.0**. Она задумывалась как временная помощь при миграции на Pydantic v2.
В текущих версиях FastAPI использование модели `pydantic.v1` в вашем приложении вызовет ошибку.
Остальная часть этого раздела описывает временную поддержку, доступную только в этих старых версиях.
///
Начиная с FastAPI 0.119.0, есть также частичная поддержка Pydantic v1 изнутри Pydantic v2, чтобы упростить миграцию на v2.
Таким образом, вы можете обновить Pydantic до последней версии 2 и сменить импорты на подмодуль `pydantic.v1` — во многих случаях всё просто заработает.
@ -122,6 +134,12 @@ graph TB
### Мигрируйте по шагам { #migrate-in-steps }
/// warning | Предупреждение
Постепенная миграция с использованием моделей Pydantic v1 и v2 в одном приложении, описанная ниже, работает только в **FastAPI 0.119.0 до 0.127.x**. Она была удалена в **FastAPI 0.128.0**, последние версии требуют модели **Pydantic v2**.
///
/// tip | Совет
Сначала попробуйте `bump-pydantic`: если тесты проходят и всё работает, вы справились одной командой. ✨
# Разделять схемы OpenAPI для входа и выхода или нет { #separate-openapi-schemas-for-input-and-output-or-not }
При использовании**Pydantic v2** сгенерированный OpenAPI становится чуть более точным и **корректным**, чем раньше. 😎
С момента выхода**Pydantic v2** сгенерированный OpenAPI становится чуть более точным и **корректным**, чем раньше. 😎
На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON-схемы** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**.
@ -161,7 +161,7 @@ FastAPI — это современный, быстрый (высокопрои
В конце 2025 года вышел [мини-документальный фильм о FastAPI](https://www.youtube.com/watch?v=mpR8ngthqiE), вы можете посмотреть его онлайн:
<aclass="fastapi-feature-banner"href="https://www.youtube.com/watch?v=mpR8ngthqiE"><imgsrc="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg"alt="FastAPI Mini Documentary"></a>
<aclass="fastapi-feature-banner"href="https://www.youtube.com/watch?v=mpR8ngthqiE"><imgsrc="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg"alt="Мини-документальный фильм о FastAPI"></a>
## **Typer**, FastAPI для CLI { #typer-the-fastapi-of-clis }
Более полный пример с дополнительными возможностями см. в <ahref="https://fastapi.tiangolo.com/ru/tutorial/">Учебник - Руководство пользователя</a>.
@ -524,7 +524,7 @@ FastAPI Cloud — основной спонсор и источник финан
#### Развертывание у других облачных провайдеров { #deploy-to-other-cloud-providers }
FastAPI — это open source и стандартизированный фреймворк. Вы можете развернуть приложения FastAPI у любого облачного провайдера на ваш выбор.
FastAPI — это проект с открытым исходным кодом, основанный на стандартах. Вы можете развернуть приложения FastAPI у любого облачного провайдера на ваш выбор.
Следуйте руководствам вашего облачного провайдера по развертыванию приложений FastAPI. 🤓
@ -544,7 +544,7 @@ FastAPI зависит от Pydantic и Starlette.
Используется Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) — для проверки адресов электронной почты.
* [`email-validator`](https://github.com/JoshData/python-email-validator) — для валидации адресов электронной почты.
- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшн.
- 🔒 Безопасное хэширование паролей по умолчанию.
- 🔑 Аутентификация по JWT‑токенам.
- 🔑 Аутентификация JWT (JSON Web Token).
- 📫 Восстановление пароля по электронной почте.
- ✅ Тесты с [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) в роли обратного прокси / балансировщика нагрузки.
- 🚢 Инструкции по развёртыванию с использованием Docker Compose, включая настройку фронтенд‑прокси Traefik для автоматического получения сертификатов HTTPS.
- 🏭 CI (continuous integration) и CD (continuous deployment) на основе GitHub Actions.
- 🏭 CI (непрерывная интеграция) и CD (непрерывное развертывание) на основе GitHub Actions.
Python поддерживает необязательные «подсказки типов» (их также называют «аннотациями типов»).
Python поддерживает необязательные «аннотации типов» (также называемые «подсказками типов»).
Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять <dfntitle="например: str, int, float, bool">тип</dfn> переменной.
Эти **«аннотации типов»**, или просто аннотации, — это специальный синтаксис, позволяющий объявлять <dfntitle="например: str, int, float, bool">тип</dfn> переменной.
Объявляя типы для ваших переменных, редакторы кода и инструменты смогут лучше вас поддерживать.
Это всего лишь **краткое руководство / напоминание** о подсказках типов в Python. Оно охватывает только минимум, необходимый для их использования с **FastAPI**... что на самом деле очень мало.
Это всего лишь **краткое руководство / напоминание** об аннотациях типов в Python. Оно охватывает только минимум, необходимый для их использования с **FastAPI**... что на самом деле очень мало.
**FastAPI** целиком основан на этих подсказках типов — они дают ему множество преимуществ и выгод.
**FastAPI** целиком основан на этих аннотациях типов — они дают ему множество преимуществ и выгод.
Но даже если вы никогда не используете **FastAPI**, вам будет полезно немного узнать о них.
/// note | Примечание
Если вы являетесь экспертом в Python и уже знаете всё о подсказках типов, переходите к следующей главе.
Если вы являетесь экспертом в Python и уже знаете всё об аннотациях типов, переходите к следующей главе.
Вы только что увидели основное место, где объявляют подсказки типов — параметры функции.
Вы только что увидели основное место, где объявляют аннотации типов — параметры функции.
Это также основное место, где вы будете использовать их с **FastAPI**.
@ -293,9 +293,9 @@ def some_function(data: Any):
Вы увидите намного больше всего этого на практике в [Учебник - Руководство пользователя](tutorial/index.md).
## Подсказки типов с аннотациями метаданных { #type-hints-with-metadata-annotations }
## Аннотации типов с аннотациями метаданных { #type-hints-with-metadata-annotations }
В Python также есть возможность добавлять **дополнительные <dfn title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</dfn>** к подсказкам типов с помощью `Annotated`.
В Python также есть возможность добавлять **дополнительные <dfn title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</dfn>** к аннотациям типов с помощью `Annotated`.
Мы также можем добавить список `tags` и дополнительные `responses`, которые будут применяться ко всем *операциям пути*, включённым в этот маршрутизатор.
Мы также можем добавить список `tags` и дополнительные `responses`, которые будут применяться ко всем *операциям пути*, включённым в этот роутер.
И ещё мы можем добавить список `dependencies`, которые будут добавлены ко всем *операциям пути* в маршрутизаторе и будут выполняться/разрешаться для каждого HTTP-запроса к ним.
И ещё мы можем добавить список `dependencies`, которые будут добавлены ко всем *операциям пути* в роутере и будут выполняться/разрешаться для каждого HTTP-запроса к ним.
* Все они будут включать предопределённые `responses`.
* Все эти *операции пути* будут иметь список `dependencies`, вычисляемых/выполняемых перед ними.
* Если вы также объявите зависимости в конкретной *операции пути*, **они тоже будут выполнены**.
* Сначала выполняются зависимости маршрутизатора, затем [`dependencies` в декораторе](dependencies/dependencies-in-path-operation-decorators.md), и затем обычные параметрические зависимости.
* Сначала выполняются зависимости роутера, затем [`dependencies` в декораторе](dependencies/dependencies-in-path-operation-decorators.md), и затем обычные параметрические зависимости.
* Вы также можете добавить [`Security`-зависимости с `scopes`](../advanced/security/oauth2-scopes.md).
/// tip | Подсказка
@ -263,7 +263,7 @@ from ...dependencies import get_token_header
то это бы означало:
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) расположен в (каталоге`app/routers/`)...
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
* перейти в родительский пакет (каталог `app/`)...
* затем перейти в родительский пакет этого пакета (родительского пакета нет, `app` — верхний уровень 😱)...
* и там найти модуль `dependencies` (файл `app/dependencies.py`)...
@ -285,7 +285,7 @@ from ...dependencies import get_token_header
Эта последняя операция пути будет иметь комбинацию тегов: `["items", "custom"]`.
И в документации у неё будут оба ответа: один для `404` и один для `403`.
И в документации у неё будут оба HTTP-ответа: один для `404` и один для `403`.
///
@ -325,7 +325,7 @@ from .routers import items, users
означает:
* Начать в том же пакете, в котором находится этот модуль (файл `app/main.py`) расположен в (каталоге`app/`)...
* Начать в том же пакете, в котором находится этот модуль (файл `app/main.py`) (каталог `app/`)...
* найти подпакет `routers` (каталог `app/routers/`)...
* и импортировать из него подмодули `items` (файл `app/routers/items.py`) и `users` (файл `app/routers/users.py`)...
@ -392,19 +392,19 @@ from .routers.users import router
С помощью `app.include_router()` мы можем добавить каждый `APIRouter` в основное приложение `FastAPI`.
Он включит все маршруты этого маршрутизатора как часть приложения.
Он включит все маршруты этого роутера как часть приложения.
/// note | Технические детали
FastAPI сохраняет исходный `APIRouter` и его `APIRoute` активными, когда маршрутизатор включается в основное приложение.
FastAPI сохраняет исходный `APIRouter` и его `APIRoute` активными, когда роутер включается в основное приложение.
Это означает, что пользовательские подклассы `APIRouter` и `APIRoute` по-прежнему участвуют после подключения маршрутизатора.
Это означает, что пользовательские подклассы `APIRouter` и `APIRoute` по-прежнему участвуют после подключения роутера.
///
/// tip | Подсказка
При подключении маршрутизаторов не нужно беспокоиться о производительности.
При подключении роутеров не нужно беспокоиться о производительности.
Это сделано максимально лёгким и не добавляет накладных расходов на каждый запрос.
@ -435,7 +435,7 @@ FastAPI сохраняет исходный `APIRouter` и его `APIRoute` а
* Префикс `/admin`.
* Тег `admin`.
* Зависимость `get_token_header`.
* Ответ `418`. 🍵
* HTTP-ответ `418`. 🍵
Но это повлияет только на этот `APIRouter` в нашем приложении, а не на любой другой код, который его использует.
@ -461,7 +461,7 @@ FastAPI сохраняет исходный `APIRouter` и его `APIRoute` а
Это потому, что мы хотим включить их *операции пути* в схему OpenAPI и пользовательские интерфейсы.
FastAPI сохраняет исходные маршрутизаторы и операции пути активными и комбинирует префиксы маршрутизаторов, зависимости, теги, ответы и другие метаданные при обработке запросов и генерации OpenAPI.
FastAPI сохраняет исходные роутеры и операции пути активными и комбинирует префиксы роутеров, зависимости, теги, HTTP-ответы и другие метаданные при обработке HTTP-запросов и генерации OpenAPI.
Вы можете сделать это до или после подключения `router` к приложению `FastAPI`. FastAPI всё равно включит *операции пути* из `other_router` в маршрутизацию и OpenAPI.
То же относится к *операциям пути*, добавленным позже в маршрутизаторы. Они также будут видны через более раннее включение.
То же относится к *операциям пути*, добавленным позже в роутеры. Они также будут видны через более раннее включение.
/// warning | Технические детали
Избегайте прямой мутации `router.routes` после включения маршрутизатора. FastAPI рассматривает включение маршрутизатора как «живое», поэтому исходный маршрутизатор и его маршруты остаются частью маршрутизации и генерации OpenAPI.
Избегайте прямой мутации `router.routes` после включения роутера. FastAPI рассматривает включение роутера как «живое», поэтому исходный роутер и его маршруты остаются частью маршрутизации и генерации OpenAPI.
Используйте документированные API, такие как декораторы операций пути и `.include_router()`, чтобы добавлять маршруты и маршрутизаторы.
Используйте документированные API, такие как декораторы операций пути и `.include_router()`, чтобы добавлять маршруты и роутеры.
Считайте `router.routes` низкоуровневым деревом маршрутов, которое может содержать определения маршрутов и включённые маршрутизаторы, и избегайте воспринимать его как плоский список итоговых операций пути.
Считайте `router.routes` низкоуровневым деревом маршрутов, которое может содержать определения маршрутов и включённые роутеры, и избегайте воспринимать его как плоский список итоговых операций пути.
Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д.) JSON-содержимое в следующем формате:
Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д.) JSON-тело запроса в следующем формате:
```JSON hl_lines="11"
{
@ -154,9 +154,9 @@ my_list: list[str]
///
## Тела с чистыми списками элементов { #bodies-of-pure-lists }
## Тела запросов с чистыми списками элементов { #bodies-of-pure-lists }
Если верхний уровень значения тела JSON-объекта представляет собой JSON `array` (в Python — `list`), вы можете объявить тип в параметре функции, так же как в моделях Pydantic:
Если верхний уровень значения JSON-тела запроса представляет собой JSON `array` (в Python — `list`), вы можете объявить тип в параметре функции, так же как в моделях Pydantic:
```Python
images: list[Image]
@ -212,7 +212,7 @@ images: list[Image]
С помощью **FastAPI** вы получаете максимальную гибкость, предоставляемую моделями Pydantic, сохраняя при этом простоту, краткость и элегантность вашего кода.
И дополнительно вы получаете:
Но со всеми преимуществами:
* Поддержку редактора кода (автозавершение доступно везде!)
* Преобразование данных (также известно как парсинг / сериализация)
* Приведёт данные к соответствующим типам (если потребуется).
* Проведёт валидацию данных.
* Если данные некорректны, вернёт понятную и наглядную ошибку, указывающую, где именно и что было некорректно.
* Если данные некорректны, вернёт понятную и наглядную ошибку, указывающую, где именно and что было некорректно.
* Передаст полученные данные в параметр `item`.
* Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т.п.) для всех атрибутов и их типов.
* Сгенерирует определения [JSON Schema](https://json-schema.org) для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта.
то автоматическая создаваемая внутри файла `myapp.py` переменная `__name__` будет иметь значение отличающееся от `"__main__"`.
то автоматически создаваемая внутри файла `myapp.py` переменная `__name__` будет иметь значение, отличающееся от `"__main__"`.
Следовательно, строка:
@ -80,7 +80,7 @@ from myapp import app
## Запуск вашего кода с помощью отладчика { #run-your-code-with-your-debugger }
Так как вы запускаете сервер Uvicorn непосредственно из вашего кода, вы можете вызвать Python программу (ваше FastAPI приложение) напрямую из отладчика.
Так как вы запускаете сервер Uvicorn непосредственно из вашего кода, вы можете запустить Python программу (ваше FastAPI приложение) напрямую из отладчика.
@ -123,7 +123,7 @@ FastAPI поддерживает зависимости, которые выпо
### Всегда делайте `raise` в зависимостях с `yield` и `except` { #always-raise-in-dependencies-with-yield-and-except }
Если вы ловите исключение в зависимости с `yield`, то, если вы не вызываете другой `HTTPException` или что-то подобное, вам следует повторно вызвать исходное исключение.
Если вы ловите исключение в зависимости с `yield`, то, если вы не вызываете другой `HTTPException` или что-то подобное, **вам следует повторно вызвать исходное исключение**.
Вы можете повторно вызвать то же самое исключение с помощью `raise`:
@ -234,6 +234,7 @@ participant operation as Функция-обработчик пути
Зависимости с `yield` со временем эволюционировали, чтобы покрыть разные сценарии и исправить некоторые проблемы.
Если вы хотите посмотреть, что менялось в разных версиях FastAPI, вы можете прочитать об этом подробнее в продвинутом руководстве: [Продвинутые зависимости — зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Контекстные менеджеры { #context-managers }
### Что такое «контекстные менеджеры» { #what-are-context-managers }
При этом у вас останутся те же возможности, что и до сих пор:
* Отличная поддержка редактора кода.
* Преобразование данных из входящих запросов.
* Преобразование данных из входящих HTTP-запросов.
* Преобразование данных для ответа.
* Валидация данных.
* Автоматическая аннотация и документация.
@ -23,32 +23,32 @@
* `UUID`:
* Стандартный "Универсальный уникальный идентификатор", используемый в качестве идентификатора во многих базах данных и системах.
* В запросах и ответах будет представлен как `str`.
* В HTTP-запросах и HTTP-ответах будет представлен как `str`.
* `datetime.datetime`:
* Встроенный в Python `datetime.datetime`.
* В запросах и ответах будет представлен как `str` в формате ISO 8601, например: `2008-09-15T15:53:00+05:00`.
* В HTTP-запросах и HTTP-ответах будет представлен как `str` в формате ISO 8601, например: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* Встроенный в Python `datetime.date`.
* В запросах и ответах будет представлен как `str` в формате ISO 8601, например: `2008-09-15`.
* В HTTP-запросах и HTTP-ответах будет представлен как `str` в формате ISO 8601, например: `2008-09-15`.
* `datetime.time`:
* Встроенный в Python `datetime.time`.
* В запросах и ответах будет представлен как `str` в формате ISO 8601, например: `14:23:55.003`.
* В HTTP-запросах и HTTP-ответах будет представлен как `str` в формате ISO 8601, например: `14:23:55.003`.
* `datetime.timedelta`:
* Встроенный в Python `datetime.timedelta`.
* В запросах и ответах будет представлен в виде общего количества секунд типа `float`.
* В HTTP-запросах и HTTP-ответах будет представлен в виде общего количества секунд типа `float`.
* Pydantic также позволяет представить его как "Кодировку разницы во времени ISO 8601", [см. документацию для получения дополнительной информации](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* В запросах и ответах обрабатывается так же, как и `set`:
* В запросах будет прочитан список, исключены дубликаты и преобразован в `set`.
* В ответах `set` будет преобразован в `list`.
* В HTTP-запросах и HTTP-ответах обрабатывается так же, как и `set`:
* В HTTP-запросах будет прочитан список, исключены дубликаты и преобразован в `set`.
* В HTTP-ответах `set` будет преобразован в `list`.
* В сгенерированной схеме будет указано, что значения `set` уникальны (с помощью JSON-схемы `uniqueItems`).
* `bytes`:
* Встроенный в Python `bytes`.
* В запросах и ответах будет рассматриваться как `str`.
* В сгенерированной схеме будет указано, что это `str` в формате `binary`.
* В HTTP-запросах и HTTP-ответах будет рассматриваться как `str`.
* В сгенерированной схеме будет указано, что это `str` в "формате"`binary`.
* `Decimal`:
* Встроенный в Python `Decimal`.
* В запросах и ответах обрабатывается так же, как и `float`.
* В HTTP-запросах и HTTP-ответах обрабатывается так же, как и `float`.
* Вы можете проверить все допустимые типы данных Pydantic здесь: [Типы данных Pydantic](https://docs.pydantic.dev/latest/usage/types/types/).
Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждого случая.
Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояние, включающее`password`, `password_hash` и отсутствие пароля.
Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". **Пользователь** — пример такой "сущности", с состояниями, которые включают`password`, `password_hash`или отсутствие пароля.
В таких случаях обычно возвращают **HTTP статус-код** в диапазоне **400** (от 400 до 499).
Они похожи на двухсотые HTTP статус-коды (от 200 до 299), которые означают, что запрос обработан успешно.
Они похожи на двухсотые HTTP статус-коды (от 200 до 299). Эти статус-коды "200" означают, что в HTTP-запросе в каком-то смысле был "успех".
Четырёхсотые статус-коды означают, что ошибка произошла по вине клиента.
HTTP статус-коды в диапазоне 400 означают, что произошла ошибка со стороны клиента.
Помните ли ошибки **"404 Not Found"** (и шутки)?
Помните все эти ошибки **"404 Not Found"** (и шутки)?
## Использование `HTTPException` { #use-httpexception }
@ -31,19 +31,19 @@
`HTTPException` - это обычное исключение Python с дополнительными данными, актуальными для API.
Поскольку это исключение Python, то его не `возвращают`, а `вызывают`.
Поскольку это исключение Python, то его не `return`, а `raise`.
Это также означает, что если вы находитесь внутри функции, которая вызывается внутри вашей *функции операции пути*, и вы поднимаете `HTTPException` внутри этой функции, то она не будет выполнять остальной код в *функции операции пути*, а сразу завершит запрос и отправит HTTP-ошибку из `HTTPException` клиенту.
Это также означает, что если вы находитесь внутри вспомогательной функции, которая вызывается внутри вашей *функции-обработчика пути*, и вы вызываете `HTTPException` изнутри этой вспомогательной функции, то остальной код в *функции-обработчике пути* выполняться не будет, запрос сразу завершится, а HTTP-ошибка из `HTTPException` будет отправлена клиенту.
О том, насколько выгоднее `вызывать` исключение, чем `возвращать` значение, будет рассказано в разделе, посвященном зависимостям и безопасности.
Преимущество вызова исключения перед возвратом значения станет более очевидным в разделе о зависимостях и безопасности.
В данном примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`:
В данном примере, когда клиент запрашивает элемент по несуществующему ID, вызовите исключение со статус-кодом `404`:
### Возвращаемый ответ { #the-resulting-response }
Если клиент запросит `http://example.com/items/foo` (`item_id` `"foo"`), то он получит статус-код 200 и ответ в формате JSON:
Если клиент запросит `http://example.com/items/foo` (`item_id` `"foo"`), то он получит HTTP статус-код 200 и ответ в формате JSON:
```JSON
{
@ -51,7 +51,7 @@
}
```
Но если клиент запросит `http://example.com/items/bar` (несуществующий `item_id``"bar"`), то он получит статус-код 404 (ошибка "не найдено") и JSON-ответ в виде:
Но если клиент запросит `http://example.com/items/bar` (несуществующий `item_id``"bar"`), то он получит HTTP статус-код 404 (ошибка "не найдено") и JSON-ответ в виде:
Вы можете добавить пользовательские обработчики исключений с помощью [тех же утилит обработки исключений из Starlette](https://www.starlette.dev/exceptions/).
Допустим, у вас есть пользовательское исключение `UnicornException`, которое вы (или используемая вами библиотека) можете `вызвать`.
Допустим, у вас есть пользовательское исключение `UnicornException`, которое вы (или используемая вами библиотека) можете вызвать с помощью `raise`.
И вы хотите обрабатывать это исключение глобально с помощью FastAPI.
@ -95,7 +95,7 @@
Но оно будет обработано `unicorn_exception_handler`.
Таким образом, вы получите чистую ошибку с кодом состояния HTTP`418` и содержимым JSON:
Таким образом, вы получите чистую ошибку с HTTP статус-кодом `418` и содержимым JSON:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
@ -113,17 +113,17 @@
**FastAPI** имеет некоторые обработчики исключений по умолчанию.
Эти обработчики отвечают за возврат стандартных JSON-ответов при `вызове``HTTPException` и при наличии в запросе недопустимых данных.
Эти обработчики отвечают за возврат стандартных JSON-ответов при вызове `HTTPException` с помощью `raise` и при наличии в HTTP-запросе недопустимых данных.
Вы можете переопределить эти обработчики исключений на свои собственные.
Когда запрос содержит недопустимые данные, **FastAPI** внутренне вызывает ошибку`RequestValidationError`.
Когда HTTP-запрос содержит недопустимые данные, **FastAPI** внутренне вызывает `RequestValidationError`.
А также включает в себя обработчик исключений по умолчанию.
А также включает в себя обработчик исключений по умолчанию для него.
Чтобы переопределить его, импортируйте `RequestValidationError` и используйте его с `@app.exception_handler(RequestValidationError)` для создания обработчика исключений.
Чтобы переопределить его, импортируйте `RequestValidationError` и используйте его с `@app.exception_handler(RequestValidationError)`, чтобы декорировать обработчик исключений.
Обработчик исключения получит объект `Request` и исключение.
@ -146,7 +146,7 @@
}
```
вы получите текстовую версию:
вы получите текстовую версию с:
```
Validation errors:
@ -171,7 +171,7 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
/// warning | Внимание
Имейте в виду, что `RequestValidationError` содержит информацию об имени файла и строке, где произошла ошибка валидации, чтобы вы могли при желании отобразить её в логах с релевантными данными.
Имейте в виду, что `RequestValidationError` содержит информацию об имени файла и строке, где происходит ошибка валидации, чтобы вы могли при желании отобразить её в логах вместе с релевантной информацией.
Но это означает, что если вы просто преобразуете её в строку и вернёте эту информацию напрямую, вы можете допустить небольшую утечку информации о своей системе, поэтому здесь код извлекает и показывает каждую ошибку отдельно.
@ -179,13 +179,13 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
### Используйте тело `RequestValidationError` { #use-the-requestvalidationerror-body }
Ошибка `RequestValidationError` содержит полученное `тело` с недопустимыми данными.
Ошибка `RequestValidationError` содержит `body` (тело запроса), которое она получила с недопустимыми данными.
Вы можете использовать его при разработке приложения для регистрации тела и его отладки, возврата пользователю и т.д.
Вы можете использовать его при разработке приложения для логирования тела запроса и его отладки, возврата пользователю и т.д.
Теперь попробуйте отправить недействительный элемент, например:
Теперь попробуйте отправить недопустимый элемент, например:
```JSON
{
@ -194,7 +194,7 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
}
```
Вы получите ответ о том, что данные недействительны, содержащий следующее тело:
Вы получите ответ о том, что данные недопустимы, содержащий полученное тело запроса:
```JSON hl_lines="12-15"
{
@ -215,7 +215,7 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
}
```
#### `HTTPException` в FastAPI или в Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
#### `HTTPException` в FastAPI и`HTTPException` в Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** имеет собственный `HTTPException`.
@ -227,9 +227,9 @@ Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to pa
Но когда вы регистрируете обработчик исключений, вы должны зарегистрировать его для `HTTPException` от Starlette.
Таким образом, если какая-либо часть внутреннего кодa Starlette, расширение или плагин Starlette вызовет исключение Starlette `HTTPException`, ваш обработчик сможет перехватить и обработать его.
Таким образом, если какая-либо часть внутреннего кода Starlette, расширение или плагин Starlette вызовет исключение Starlette `HTTPException`, ваш обработчик сможет перехватить и обработать его.
В данном примере, чтобы иметь возможность использовать оба `HTTPException` в одном коде, исключения Starlette переименованы в `StarletteHTTPException`:
В данном примере, чтобы иметь возможность использовать оба `HTTPException` в одном коде, исключение Starlette переименовано в `StarletteHTTPException`:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
@ -241,4 +241,4 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
В этом примере вы просто `выводите в терминал` ошибку с очень выразительным сообщением, но идея вам понятна. Вы можете использовать исключение, а затем просто повторно использовать стандартные обработчики исключений.
В этом примере вы просто выводите ошибку с очень выразительным сообщением, но идея вам понятна. Вы можете использовать исключение, а затем просто повторно использовать стандартные обработчики исключений.
# Учебник - Руководство пользователя { #tutorial-user-guide }
В этом руководстве шаг за шагом показано, как использовать **FastAPI** с большинством его функций.
Каждый раздел постепенно основывается на предыдущих, но структура разделяет темы, так что вы можете сразу перейти к нужной теме для решения ваших конкретных задач по API.
| `summary` | `str` | Краткое резюме API. <small>Доступно начиная с OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
| `description` | `str` | Краткое описание API. Может быть использован Markdown. |
| `version` | `string` | Версия API. Версия вашего собственного приложения, а не OpenAPI. К примеру `2.5.0`. |
| `terms_of_service` | `str` | Ссылка к условиям пользования API. Если указано, то это должен быть URL-адрес. |
| `contact` | `dict` | Контактная информация для открытого API. Может содержать несколько полей. <details><summary>поля <code>contact</code></summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>Идентификационное имя контактного лица/организации.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL указывающий на контактную информацию. ДОЛЖЕН быть в формате URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>Emailадрес контактного лица/организации. ДОЛЖЕН быть в формате emailадреса.</td></tr></tbody></table></details> |
| `license_info` | `dict` | Информация о лицензии открытого API. Может содержать несколько полей. <details><summary>поля <code>license_info</code></summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>ОБЯЗАТЕЛЬНО</strong> (если установлен параметр <code>license_info</code>). Название лицензии, используемой для API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Выражение лицензии [SPDX](https://spdx.org/licenses/) для API. Поле <code>identifier</code>взаимоисключающее с полем <code>url</code>. <small>Доступно начиная с OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL, указывающий на лицензию, используемую для API. ДОЛЖЕН быть в формате URL.</td></tr></tbody></table></details> |
| `version` | `str` | Версия API. Версия вашего собственного приложения, а не OpenAPI. К примеру `2.5.0`. |
| `terms_of_service` | `str` | Ссылка на условия пользования API. Если указано, то это должен быть URL-адрес. |
| `contact` | `dict` | Контактная информация для открытого API. Может содержать несколько полей. <details><summary>поля <code>contact</code></summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>Идентификационное имя контактного лица/организации.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL, указывающий на контактную информацию. ДОЛЖЕН быть в формате URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>Email-адрес контактного лица/организации. ДОЛЖЕН быть в формате email-адреса.</td></tr></tbody></table></details> |
| `license_info` | `dict` | Информация о лицензии открытого API. Может содержать несколько полей. <details><summary>поля <code>license_info</code></summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>ОБЯЗАТЕЛЬНО</strong> (если установлен параметр <code>license_info</code>). Название лицензии, используемой для API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Выражение лицензии [SPDX](https://spdx.org/licenses/) для API. Поле <code>identifier</code>является взаимоисключающим с полем <code>url</code>. <small>Доступно начиная с OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL, указывающий на лицензию, используемую для API. ДОЛЖЕН быть в формате URL.</td></tr></tbody></table></details> |
Вы можете задать их следующим образом:
@ -48,7 +48,7 @@
* `name` (**обязательно**): `str`-значение с тем же именем тега, которое вы используете в параметре `tags` в ваших *операциях пути* и `APIRouter`ах.
* `description`: `str`-значение с кратким описанием для тега. Может содержать Markdown и будет отображаться в UI документации.
* `externalDocs`: `dict`-значение описывающее внешнюю документацию. Включает в себя:
* `externalDocs`: `dict`-значение, описывающее внешнюю документацию. Включает в себя:
* `description`: `str`-значение с кратким описанием для внешней документации.
* `url` (**обязательно**): `str`-значение с URL-адресом для внешней документации.
@ -64,7 +64,7 @@
/// tip | Подсказка
Вам необязательно добавлять метаданные для всех используемых тегов
Вам необязательно добавлять метаданные для всех используемых тегов.
///
@ -94,11 +94,11 @@
## URL-адрес OpenAPI { #openapi-url }
По умолчанию схема OpenAPI отображена по адресу `/openapi.json`.
По умолчанию схема OpenAPI отдаётся по адресу `/openapi.json`.
Но вы можете изменить это с помощью параметра `openapi_url`.
К примеру, чтобы задать её отображение по адресу `/api/v1/openapi.json`:
К примеру, чтобы задать её отдачу по адресу `/api/v1/openapi.json`:
## Описание из строк документации { #description-from-docstring }
Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в <dfntitle="многострочная строка, первое выражение внутри функции (не присвоенное какой-либо переменной), используемая для документации">строке документации</dfn> функции, и **FastAPI** прочитает её оттуда.
Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в <dfntitle="многострочная строка, первое выражение внутри функции (не присвоенное какой-либо переменной), используемое для документации">строке документации</dfn> функции, и **FastAPI** прочитает её оттуда.
Вы можете использовать [Markdown](https://en.wikipedia.org/wiki/Markdown) в строке документации, и он будет интерпретирован и отображён корректно (с учетом отступа в строке документации).
@ -94,7 +94,7 @@ OpenAPI указывает, что каждой *операции пути* не
**Значение по умолчанию** у **параметра функции** — это **настоящее значение по умолчанию**, что более интуитивно для Python. 😌
Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра.
Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор кода** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра.
Если вы не используете `Annotated`, а применяете **(устаревший) стиль со значением по умолчанию**, то при вызове этой функции без FastAPI в **других местах** вам нужно **помнить** о том, что надо передать аргументы, чтобы всё работало корректно, иначе значения будут не такими, как вы ожидаете (например, вместо `str` будет `QueryInfo` или что-то подобное). И ни редактор, ни Python не будут ругаться при самом вызове функции — ошибка проявится лишь при операциях внутри.
Если вы не используете `Annotated`, а применяете **(устаревший) стиль со значением по умолчанию**, то при вызове этой функции без FastAPI в **других местах** вам нужно **помнить** о том, что надо передать аргументы, чтобы всё работало корректно, иначе значения будут не такими, как вы ожидаете (например, вместо `str` будет `QueryInfo` или что-то подобное). И ни редактор кода, ни Python не будут ругаться при самом вызове функции — ошибка проявится лишь при операциях внутри.
Так как `Annotated` может содержать больше одной аннотации метаданных, теперь вы можете использовать ту же функцию и с другими инструментами, например с [Typer](https://typer.tiangolo.com/). 🚀
## Больше валидаций { #add-more-validations }
## Добавим больше валидаций { #add-more-validations }
Вы можете определить <dfntitle="Регулярное выражение (regex, regexp) — это последовательность символов, задающая шаблон поиска для строк.">регулярное выражение</dfn>`pattern`, которому должен соответствовать параметр:
@ -173,7 +173,7 @@ q: str = Query(default="rick")
Данный шаблон регулярного выражения проверяет, что полученное значение параметра:
* `^`: начинается с следующих символов, до них нет символов.
* `^`: начинается со следующих символов, до них нет символов.
* `fixedquery`: имеет точное значение `fixedquery`.
* `$`: заканчивается здесь, после `fixedquery` нет никаких символов.
@ -191,7 +191,7 @@ q: str = Query(default="rick")
/// note | Примечание
Наличие значения по умолчанию любого типа, включая `None`, делает параметр необязательным.
Наличие значения по умолчанию любого типа, включая `None`, делает параметр необязательным (не обязательным).
вы получите множественные значения *query-параметров*`q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции-обработчика пути*, в *параметре функции*`q`.
В таких случаях можно использовать **кастомную функцию-валидатор**, которая применяется после обычной валидации (например, после проверки, что значение — это `str`).
Этого можно добиться, используя [`AfterValidator` Pydantic](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) внутри `Annotated`.
Этого можно добиться, используя [Pydantic `AfterValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) внутри `Annotated`.
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbrtitle="International Standard Book Number - Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма на<abbrtitle="Internet Movie Database - Интернетная база данных фильмов: веб‑сайт с информацией о фильмах">IMDB</abbr>:
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbrtitle="International Standard Book Number - Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма в<abbrtitle="Internet Movie Database - Интернет-база данных фильмов: веб‑сайт с информацией о фильмах">IMDB</abbr>:
Если вам нужна валидация, требующая общения с каким‑либо **внешним компонентом** — базой данных или другим API — вместо этого используйте **Зависимости FastAPI**, вы познакомитесь с ними позже.
Эти кастомные валидаторы предназначены для проверок, которые можно выполнить, имея **только** те же **данные**, что пришли в запросе.
Эти кастомные валидаторы предназначены для проверок, которые можно выполнить, имея **только** те же **данные**, что пришли в HTTP-запросе.
Query-параметры представляют из себя набор пар ключ-значение, которые идут после знака `?` в URL-адресе, разделенные символами `&`.
Query — это набор пар ключ-значение, которые идут после знака `?` в URL-адресе, разделенные символами `&`.
Например, в этом URL-адресе:
@ -12,25 +12,25 @@ Query-параметры представляют из себя набор па
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...параметры запроса такие:
...query-параметры такие:
* `skip`: со значением `0`
* `limit`: со значением `10`
Будучи частью URL-адреса, они "по умолчанию" являются строками.
Будучи частью URL-адреса, они "естественным образом" являются строками.
Но когда вы объявляете их с использованием типов Python (в примере выше, как `int`), они конвертируются в указанный тип данных и проходят проверку на соответствие ему.
Все те же правила, которые применяются к path-параметрам, также применяются и query-параметрам:
Все те же процессы, которые применяются к path-параметрам, также применяются и к query-параметрам:
* Поддержка от редактора кода (очевидно)
* <dfntitle="преобразование строки, полученной из HTTP-запроса в данные Python">"Парсинг"</dfn> данных
* Проверка на соответствие данных (Валидация)
* <dfntitle="преобразование строки, полученной из HTTP-запроса, в данные Python">"Парсинг"</dfn> данных
* Валидация данных
* Автоматическая документация
## Значения по умолчанию { #defaults }
Поскольку query-параметры не являются фиксированной частью пути, они могут быть необязательными и иметь значения по умолчанию.
Поскольку query-параметры не являются фиксированной частью пути, они могут быть необязательными и иметь значения по умолчанию.
В примере выше значения по умолчанию равны `skip=0` и `limit=10`.
В этом случае, параметр `q` будет необязательным и будет иметь значение `None` по умолчанию.
В этом случае параметр функции`q` будет необязательным и будет иметь значение `None` по умолчанию.
/// tip | Подсказка
Также обратите внимание, что **FastAPI** достаточно умён чтобы заметить, что параметр `item_id` является path-параметром, а `q` нет, поэтому, это параметр запроса.
Также обратите внимание, что **FastAPI** достаточно умён, чтобы заметить, что path-параметр `item_id` является path-параметром, а `q`— нет, поэтому это query-параметр.
///
## Преобразование типа параметра запроса { #query-parameter-type-conversion }
## Преобразование типа query-параметра { #query-parameter-type-conversion }
Вы также можете объявлять параметры с типом `bool`, которые будут преобразованы соответственно:
Вы также можете объявлять типы `bool`, и они будут преобразованы:
или в любом другом варианте написания (в верхнем регистре, с заглавной буквой, и т.п), внутри вашей функции параметр `short` будет иметь значение `True` типа данных `bool` . В противном случае -`False`.
или в любом другом варианте написания (в верхнем регистре, с заглавной буквой и т.п.), внутри вашей функции параметр `short` будет иметь значение `True` типа данных `bool`. В противном случае —`False`.
## Смешивание query-параметров и path-параметров { #multiple-path-and-query-parameters }
Вы можете объявлять несколько query-параметров и path-параметров одновременно, **FastAPI** сам разберётся, что чем является.
## Несколько path-параметров и query-параметров { #multiple-path-and-query-parameters }
Вы можете объявлять несколько path-параметров и query-параметров одновременно, **FastAPI** знает, что чем является.
И вы не обязаны объявлять их в каком-либо определенном порядке.
Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе мы пока что рассмотрели только query-параметры), то он не является обязательным.
Если вы не хотите задавать конкретное значение, но хотите сделать параметр необязательным, вы можете установить значение по умолчанию равным `None`.
Если вы не хотите задавать конкретное значение, но хотите просто сделать параметр необязательным, установите значение по умолчанию равным `None`.
Но если вы хотите сделать query-параметр обязательным, вы можете просто не указывать значение по умолчанию:
Для объявления тела файла необходимо использовать `File`, поскольку в противном случае параметры будут интерпретироваться как параметры запроса или параметры тела (JSON).
Чтобы объявить файлы в теле запроса, необходимо использовать `File`, поскольку иначе параметры будут интерпретироваться как параметры запроса или body-параметры (JSON).
///
Файлы будут загружены как данные формы.
Если вы объявите тип параметра у *функции операции пути* как `bytes`, то **FastAPI** прочитает файл за вас, и вы получите его содержимое в виде `bytes`.
Если вы объявите тип параметра у *функции-обработчика пути* как `bytes`, то **FastAPI** прочитает файл за вас, и вы получите его содержимое в виде `bytes`.
Следует иметь в виду, что все содержимое будет храниться в памяти. Это хорошо подходит для небольших файлов.
@ -64,15 +64,15 @@ $ pip install python-multipart
* Это означает, что он будет хорошо работать с большими файлами, такими как изображения, видео, большие бинарные файлы и т.д., не потребляя при этом всю память.
* Из загруженного файла можно получить метаданные.
* Он реализует [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) `async` интерфейс.
* Он предоставляет реальный объект Python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile), который вы можете передать непосредственно другим библиотекам, которые ожидают файл в качестве объекта.
* Он предоставляет реальный объект Python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile), который вы можете передать непосредственно другим библиотекам, которые ожидают file-like объект.
### `UploadFile` { #uploadfile }
`UploadFile` имеет следующие атрибуты:
* `filename`: Строка `str` с исходным именем файла, который был загружен (например, `myimage.jpg`).
* `content_type`: Строка `str` с типом содержимого (MIME type / media type) (например, `image/jpeg`).
* `file`: [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (a [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) объект). Это фактический файл Python, который можно передавать непосредственно другим функциям или библиотекам, ожидающим файл в качестве объекта.
* `content_type`: Строка `str` с типом содержимого (MIME-тип / тип содержимого) (например, `image/jpeg`).
* `file`: [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) ([file-like](https://docs.python.org/3/glossary.html#term-file-like-object) объект). Это фактический файл Python, который можно передавать непосредственно другим функциям или библиотекам, ожидающим file-like объект.
`UploadFile` имеет следующие методы `async`. Все они вызывают соответствующие файловые методы (используя внутренний `SpooledTemporaryFile`).
@ -85,19 +85,18 @@ $ pip install python-multipart
Поскольку все эти методы являются `async` методами, вам следует использовать "await" вместе с ними.
Например, внутри `async`*функции операции пути* можно получить содержимое с помощью:
Например, внутри `async`*функции-обработчика пути* можно получить содержимое с помощью:
```Python
contents = await myfile.read()
```
Если вы находитесь внутри обычной `def`*функции операции пути*, можно получить прямой доступ к файлу `UploadFile.file`, например:
Если вы находитесь внутри обычной `def`*функции-обработчика пути*, можно получить прямой доступ к файлу `UploadFile.file`, например:
```Python
contents = myfile.file.read()
```
/// note | Технические детали `async`
При использовании методов `async`**FastAPI** запускает файловые методы в пуле потоков и ожидает их.
@ -106,7 +105,7 @@ contents = myfile.file.read()
/// note | Технические детали Starlette
**FastAPI** наследует `UploadFile`непосредственно из **Starlette**, но добавляет некоторые детали для совместимости с **Pydantic** и другими частями FastAPI.
`UploadFile` из **FastAPI** наследуется непосредственно от `UploadFile` из **Starlette**, но добавляет некоторые необходимые части для совместимости с **Pydantic** и другими частями FastAPI.
Данные из форм обычно кодируются с использованием "media type" `application/x-www-form-urlencoded` когда он не включает файлы.
Данные из форм обычно кодируются с использованием типа содержимого `application/x-www-form-urlencoded`, когда они не включают файлы.
Но когда форма включает файлы, она кодируется как `multipart/form-data`. Если вы используете `File`, **FastAPI** будет знать, что ему нужно получить файлы из нужной части тела.
Но когда форма включает файлы, она кодируется как `multipart/form-data`. Если вы используете `File`, **FastAPI** будет знать, что ему нужно получить файлы из нужной части тела запроса.
Если вы хотите узнать больше об этих кодировках и полях форм, перейдите по ссылке [<abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla">MDN</abbr> web docs for `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
Если вы хотите узнать больше об этих кодировках и полях форм, перейдите к [веб-документации <abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla">MDN</abbr> по `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Внимание
В операции *функции операции пути* можно объявить несколько параметров `File` и `Form`, но нельзя также объявлять поля `Body`, которые предполагается получить в виде JSON, поскольку тело запроса будет закодировано с помощью `multipart/form-data`, а не `application/json`.
В *операции пути* можно объявить несколько параметров `File` и `Form`, но нельзя также объявлять поля `Body`, которые предполагается получить в виде JSON, поскольку HTTP-запрос будет иметь тело, закодированное с помощью `multipart/form-data`, а не `application/json`.
Это не является ограничением **FastAPI**, это часть протокола HTTP.
@ -174,4 +173,4 @@ contents = myfile.file.read()
## Резюме { #recap }
Используйте `File`, `bytes` и `UploadFile` для работы с файлами, которые будут загружаться и передаваться в виде данных формы.
Используйте `File`, `bytes` и `UploadFile`, чтобы объявлять файлы для загрузки в HTTP-запросе, передаваемые как данные формы.
Подобно тому, как вы можете задать модель/схему ответа, вы можете объявить HTTP статус-код, используемый для ответа, с помощью параметра `status_code` в любой из *операций пути*:
Подобно тому, как вы можете задать модель ответа, вы можете объявить HTTP статус-код, используемый для ответа, с помощью параметра `status_code` в любой из *операций пути*:
* `@app.get()`
* `@app.post()`
@ -26,8 +26,8 @@
Это позволит:
* Возвращать указанный код статуса в ответе.
* Документировать его как код статуса ответа в OpenAPI схеме (а значит, и в пользовательских интерфейсах):
* Возвращать указанный статус-код в ответе.
* Документировать его как статус-код ответа в OpenAPI схеме (а значит, и в пользовательских интерфейсах):
@ -47,26 +47,26 @@ FastAPI знает об этом и создаст документацию Open
///
В протоколе HTTP числовой код состояния из 3 цифр отправляется как часть ответа.
В протоколе HTTP числовой статус-код из 3 цифр отправляется как часть ответа.
У кодов статуса есть названия, чтобы упростить их распознавание, но важны именно числовые значения.
У статус-кодов есть названия, чтобы упростить их распознавание, но важны именно числовые значения.
Кратко:
* `100 - 199` – статус-коды информационного типа. Они редко используются разработчиками напрямую. Ответы с этими кодами не могут иметь тела.
* `100 - 199` – статус-коды информационного типа. Они редко используются разработчиками напрямую. Ответы с этими статус-кодами не могут иметь тела.
* **`200 - 299`** – статус-коды, сообщающие об успешной обработке запроса. Они используются чаще всего.
* `200` – это код статуса ответа по умолчанию, который означает, что все прошло "OK".
* `200` – это статус-код по умолчанию, который означает, что все прошло "OK".
* Другим примером может быть статус `201`, "Created". Он обычно используется после создания новой записи в базе данных.
* Особый случай – `204`, "No Content". Этот статус ответа используется, когда нет содержимого для возврата клиенту, и поэтому ответ не должен иметь тела.
* **`300 - 399`** – статус-коды, сообщающие о перенаправлениях. Ответы с этими кодами статуса могут иметь или не иметь тело, за исключением ответов со статусом `304`, "Not Modified", у которых не должно быть тела.
* **`300 - 399`** – статус-коды, сообщающие о перенаправлениях. Ответы с этими статус-кодами могут иметь или не иметь тело, за исключением ответов со статусом `304`, "Not Modified", у которых не должно быть тела.
* **`400 - 499`** – статус-коды, сообщающие о клиентской ошибке. Это ещё одна наиболее часто используемая категория.
* Пример – код `404` для статуса "Not Found".
* Для общих ошибок со стороны клиента можно просто использовать код `400`.
* `500 - 599` – статус-коды, сообщающие о серверной ошибке. Они почти никогда не используются разработчиками напрямую. Когда что-то идет не так в какой-то части кода вашего приложения или на сервере, он автоматически вернёт один из этих кодов статуса.
* `500 - 599` – статус-коды, сообщающие о серверной ошибке. Они почти никогда не используются разработчиками напрямую. Когда что-то идет не так в какой-то части кода вашего приложения или на сервере, он автоматически вернёт один из этих статус-кодов.
/// tip | Подсказка
Чтобы узнать больше о HTTP кодах статуса и о том, для чего каждый из них предназначен, ознакомьтесь с [<abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla">MDN</abbr> документацией об HTTP статус-кодах](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
Чтобы узнать больше о HTTP статус-кодах и о том, для чего каждый из них предназначен, ознакомьтесь с [<abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla">MDN</abbr> документацией об HTTP статус-кодах](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
///
@ -76,7 +76,7 @@ FastAPI знает об этом и создаст документацию Open
# Объявление примеров данных запроса { #declare-request-example-data }
# Объявление примеров данных HTTP-запроса { #declare-request-example-data }
Вы можете объявлять примеры данных, которые ваше приложение может получать.
@ -12,7 +12,7 @@
Эта дополнительная информация будет добавлена как есть в выходную **JSON Schema** этой модели и будет использоваться в документации API.
Вы можете использовать атрибут `model_config`, который принимает `dict`, как описано в [Документация Pydantic: Конфигурация](https://docs.pydantic.dev/latest/api/config/).
Вы можете использовать атрибут `model_config`, который принимает `dict`, как описано в [документации Pydantic: Конфигурация](https://docs.pydantic.dev/latest/api/config/).
Вы можете задать `"json_schema_extra"` с `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`.
@ -26,7 +26,7 @@
/// note | Примечание
OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) добавил поддержку `examples`, который является частью стандарта **JSON Schema**.
OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) добавил поддержку `examples`, которое является частью стандарта **JSON Schema**.
До этого поддерживалось только ключевое слово `example` с одним примером. Оно всё ещё поддерживается в OpenAPI 3.1.0, но помечено как устаревшее и не является частью стандарта JSON Schema. Поэтому рекомендуется мигрировать `example` на `examples`. 🤓
@ -80,13 +80,13 @@ OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) доб
Ещё до того как **JSON Schema** поддержала `examples`, в OpenAPI была поддержка другого поля, также называемого `examples`.
Эти **специфические для OpenAPI**`examples` находятся в другой секции спецификации OpenAPI. Они находятся в **подробностях для каждой операции пути (обработчика пути)**, а не внутри каждого объекта Schema.
Эти **специфические для OpenAPI**`examples` находятся в другой секции спецификации OpenAPI. Они находятся в **подробностях каждой *операции пути***, а не внутри каждой JSON Schema.
И Swagger UI уже какое‑то время поддерживает именно это поле `examples`. Поэтому вы можете использовать его, чтобы **отобразить** разные **примеры в UI документации**.
Структура этого специфичного для OpenAPI поля `examples` — это `dict` с **несколькими примерами** (вместо `list`), каждый с дополнительной информацией, которая также будет добавлена в **OpenAPI**.
Это не помещается внутрь каждого объекта Schema в OpenAPI, это находится снаружи, непосредственно на уровне самой *операции пути*.
Это не помещается внутрь каждой JSON Schema, содержащейся в OpenAPI, это находится снаружи, непосредственно на уровне самой *операции пути*.
### Использование параметра `openapi_examples` { #using-the-openapi-examples-parameter }
Он будет искать в запросе HTTP-заголовок `Authorization`, проверять, что его значение — это `Bearer ` плюс некоторый токен, и вернет токен как `str`.
Он будет искать в HTTP-запросе HTTP-заголовок `Authorization`, проверять, что его значение — это `Bearer ` плюс некоторый токен, и вернет токен как `str`.
Если заголовок `Authorization` отсутствует или его значение не содержит токен `Bearer `, он сразу ответит ошибкой со статус-кодом 401 (`UNAUTHORIZED`).
Если HTTP-заголовок `Authorization` отсутствует или его значение не содержит токен `Bearer `, он сразу ответит ошибкой со статус-кодом 401 (`UNAUTHORIZED`).
Вам даже не нужно проверять наличие токена, чтобы вернуть ошибку. Вы можете быть уверены: если ваша функция была выполнена, в этом токене будет `str`.
Нам необходимо установить `pyjwt` для генерации и проверки JWT-токенов на языке Python.
Нам необходимо установить `PyJWT` для генерации и проверки JWT-токенов на языке Python.
Убедитесь, что вы создали [виртуальное окружение](../../virtual-environments.md), активируйте его, а затем установите `pyjwt`:
@ -42,7 +42,7 @@ $ pip install pyjwt
</div>
/// note | Дополнительная информация
/// note | Примечание
Если вы планируете использовать алгоритмы цифровой подписи, такие как RSA или ECDSA, вам следует установить зависимость библиотеки криптографии `pyjwt[crypto]`.
@ -110,9 +110,9 @@ pwdlib также поддерживает алгоритм хешировани
///
Создайте служебную функцию для хэширования пароля, поступающего от пользователя.
Создайте вспомогательную функцию для хэширования пароля, поступающего от пользователя.
А затем создайте другую — для проверки соответствия полученного пароля и хранимого хэша.
А затем создайте другую вспомогательную функцию — для проверки соответствия полученного пароля и хранимого хэша.
И еще одну — для аутентификации и возврата пользователя.
@ -120,15 +120,15 @@ pwdlib также поддерживает алгоритм хешировани
Когда `authenticate_user` вызывается с именем пользователя, которого нет в базе данных, мы все равно запускаем `verify_password` с использованием фиктивного хэша.
Это гарантирует, что эндпоинт отвечает примерно за одно и то же время вне зависимости от того, существует имя пользователя или нет, предотвращая тайминговые атаки (атака по времени), с помощью которых можно было бы перечислять существующие имена пользователей.
Это гарантирует, что эндпоинт отвечает примерно за одно и то же время вне зависимости от того, существует имя пользователя или нет, предотвращая **тайминговые атаки** (атаки по времени), с помощью которых можно было бы перечислять существующие имена пользователей.
/// note | Технические детали
/// note | Примечание
Если проверить новую (фальшивую) базу данных `fake_users_db`, то можно увидеть, как теперь выглядит хэшированный пароль: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Работа с JWTтокенами { #handle-jwt-tokens }
## Работа с JWT-токенами { #handle-jwt-tokens }
Импортируйте установленные модули.
@ -154,7 +154,7 @@ $ openssl rand -hex 32
Определите Pydantic-модель, которая будет использоваться для формирования ответа на запрос на получение токена.
Создайте служебную функцию для генерации нового токена доступа.
Создайте вспомогательную функцию для генерации нового токена доступа.
@ -188,13 +188,13 @@ JWT может использоваться и для других целей,
Затем вы могли бы добавить права доступа к этой сущности, например "управлять" (для автомобиля) или "редактировать" (для блога).
Затем вы могли бы передать этот JWT-токен пользователю (или боту), и они использовали бы его для выполнения определенных действий (управление автомобилем или редактирование запись в блоге), даже не имея учетной записи, просто используя JWT-токен, сгенерированный вашим API.
Затем вы могли бы передать этот JWT-токен пользователю (или боту), и они использовали бы его для выполнения определенных действий (управление автомобилем или редактирование записи в блоге), даже не имея учетной записи, просто используя JWT-токен, сгенерированный вашим API.
Используя эти идеи, JWT можно применять для гораздо более сложных сценариев.
В отдельных случаях несколько сущностей могут иметь один и тот же идентификатор, скажем, `foo` (пользователь `foo`, автомобиль `foo` и запись в блоге `foo`).
Поэтому, чтобы избежать коллизий идентификаторов, при создании JWT-токена для пользователя можно добавить префикс `username` к значению ключа `sub`. Таким образом, в данном примере значение `sub` было бы `username:johndoe`.
Поэтому, чтобы избежать коллизий идентификаторов, при создании JWT-токена для пользователя можно добавить префикс `username:` к значению ключа `sub`. Таким образом, в данном примере значение `sub`могло бы быть:`username:johndoe`.
Важно помнить, что ключ `sub` должен иметь уникальный идентификатор для всего приложения и представлять собой строку.
@ -238,7 +238,7 @@ Password: `secret`
<imgsrc="/img/tutorial/security/image10.png">
/// note | Техническая информация
/// note | Примечание
Обратите внимание на HTTP-заголовок `Authorization`, значение которого начинается с `Bearer `.
@ -14,7 +14,7 @@ OAuth2 определяет, что при использовании "password
А ваши модели баз данных могут использовать любые другие имена.
Но для логин-операции пути нам нужно использовать именно эти имена, чтобы быть совместимыми со спецификацией (и иметь возможность, например, использовать встроенную систему документации API).
Но для *операции пути* входа в систему нам нужно использовать именно эти имена, чтобы быть совместимыми со спецификацией (и иметь возможность, например, использовать встроенную систему документации API).
В спецификации также указано, что `username` и `password` должны передаваться в виде данных формы (так что никакого JSON здесь нет).
@ -88,7 +88,7 @@ OAuth2 определяет, что при использовании "password
Теперь получим данные о пользователе из (ненастоящей) базы данных, используя `username` из поля формы.
Если такого пользователя нет, то мы возвращаем ошибку "Incorrect username or password" (неверное имя пользователя или пароль).
Если такого пользователя нет, то мы возвращаем ошибку "Incorrect username or password".
Для ошибки используем исключение `HTTPException`:
@ -137,12 +137,12 @@ UserInDB(
```
/// note | Примечание
Более полное объяснение `**user_dict` можно найти в [документации к **Дополнительным моделям**](../extra-models.md#about-user-in-dict).
Более полное объяснение `**user_dict` можно найти в [документации к **Дополнительным моделям**](../extra-models.md#about-user-in-model-dump).
///
## Возврат токена { #return-the-token }
Ответ операции пути `/token` должен быть объектом JSON.
Ответ эндпоинта `token` должен быть объектом JSON.
В нём должен быть `token_type`. В нашем случае, поскольку мы используем токены типа "Bearer", тип токена должен быть `bearer`.
@ -151,7 +151,7 @@ UserInDB(
В этом простом примере мы намеренно поступим небезопасно и вернём тот же `username` в качестве токена.
/// tip | Подсказка
В следующей главе вы увидите реальную защищённую реализацию с хешированием паролей и токенами <abbrtitle="JSON Web Tokens – JSON веб-токены">JWT</abbr>.
В следующей главе вы увидите реальную защищённую реализацию с хешированием паролей и токенами <abbrtitle="JSON Web Tokens - JSON веб-токены">JWT</abbr>.
Но пока давайте сосредоточимся на необходимых нам деталях.
///
@ -266,8 +266,8 @@ UserInDB(
Теперь у вас есть инструменты для реализации полноценной системы безопасности на основе `username` и `password` для вашего API.
Используя эти средства, можно сделать систему безопасности совместимой с любой базой данных и с любой пользовательской или моделью данных.
Используя эти средства, можно сделать систему безопасности совместимой с любой базой данных и с любой моделью пользователя или моделью данных.
Единственная деталь, которой не хватает, — система пока ещё не "защищена" по-настоящему.
В следующей главе вы увидите, как использовать библиотеку безопасного хеширования паролей и токены <abbrtitle="JSON Web Tokens – JSON веб-токены">JWT</abbr>.
В следующей главе вы увидите, как использовать библиотеку безопасного хеширования паролей и токены <abbrtitle="JSON Web Tokens - JSON веб-токены">JWT</abbr>.
# SQL (реляционные) базы данных { #sql-relational-databases }
**FastAPI** не требует использовать SQL (реляционную) базу данных. Но вы можете использовать любую базу данных, которую хотите.
**FastAPI** не требует использовать SQL (реляционную) базу данных. Но вы можете использовать **любую базу данных**, которую хотите.
Здесь мы рассмотрим пример с использованием [SQLModel](https://sqlmodel.tiangolo.com/).
@ -8,7 +8,7 @@
/// tip | Подсказка
Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют <abbrtitle="Object Relational Mapper - Объектно-реляционный маппер: термин для библиотеки, где некоторые классы представляют SQL-таблицы, а экземпляры представляют строки в этих таблицах">"ORMs"</abbr>), FastAPI ничего не навязывает. 😎
Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют <abbrtitle="Object Relational Mapper - Объектно-реляционный маппер: красивый термин для библиотеки, где некоторые классы представляют SQL-таблицы, а экземпляры представляют строки в этих таблицах">"ORMs"</abbr>), FastAPI ничего не навязывает. 😎
///
@ -119,7 +119,7 @@ $ pip install sqlmodel
Так как каждая модель SQLModel также является моделью Pydantic, вы можете использовать её в тех же **аннотациях типов**, в которых используете модели Pydantic.
Например, если вы объявите параметр типа `Hero`, он будет прочитан из **JSON body (тела запроса)**.
Например, если вы объявите параметр типа `Hero`, он будет прочитан из **JSON-тела запроса**.
Аналогично вы можете объявить её как **тип возвращаемого значения** функции, и тогда форма данных отобразится в автоматически сгенерированном UI документации API.
Вы можете предоставлять статические файлы автоматически из директории, используя `StaticFiles`.
/// tip | Совет
Если вам нужно разместить фронтенд, используйте вместо этого `app.frontend()`, подробнее читайте в разделе [Фронтенд](frontend.md).
`app.frontend()` использует `StaticFiles` под капотом, с несколькими дополнительными преимуществами для фронтендов, такими как обработка маршрутизации на стороне клиента.
///
## Использование `StaticFiles` { #use-staticfiles }
* Импортируйте `StaticFiles`.
@ -21,8 +29,7 @@
"Монтирование" означает добавление полноценного "независимого" приложения на определённый путь, которое затем обрабатывает все подпути.
Это отличается от использования `APIRouter`, так как примонтированное приложение является полностью независимым.
OpenAPI и документация из вашего главного приложения не будут содержать ничего из примонтированного приложения, и т.д.
Это отличается от использования `APIRouter`, так как примонтированное приложение является полностью независимым. OpenAPI и документация из вашего главного приложения не будут содержать ничего из примонтированного приложения, и т.д.
Вы можете прочитать больше об этом в [Расширенном руководстве пользователя](../advanced/index.md).
Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт:
Так как этот файл находится в том же пакете, для импорта объекта `app` из модуля `main` (`main.py`) Вы можете использовать относительный импорт:
Если Вы не знаете, как передать информацию в запросе, можете воспользоваться поисковиком (погуглить) и задать вопрос: "Как передать информацию в запросе с помощью `httpx`", можно даже спросить: "Как передать информацию в запросе с помощью `requests`", поскольку дизайн HTTPX основан на дизайне Requests.
Если Вы не знаете, как передать информацию в запросе, можете воспользоваться поиском (Google) и задать вопрос: "Как передать информацию в запросе с помощью `httpx`", можно даже спросить: "Как передать информацию в запросе с помощью `requests`", поскольку дизайн HTTPX основан на дизайне Requests.
Затем Вы просто применяете найденные ответы в тестах.
Например:
* Передаёте *path*-параметры или *query*-параметры, вписав их непосредственно в строку URL.
* Чтобы передать *path*-параметр или *query*-параметр, добавьте его непосредственно в URL.
* Передаёте JSON в теле запроса, передав Python-объект (например: `dict`) через именованный параметр `json`.
* Если же Вам необходимо отправить *форму с данными* вместо JSON, то используйте параметр `data` вместо `json`.
* Если же Вам необходимо отправить *данные формы* вместо JSON, то используйте параметр `data` вместо `json`.
* Для передачи *HTTP-заголовков*, передайте объект `dict` через параметр `headers`.
* Для передачи *cookies* также передайте `dict`, но через параметр `cookies`.