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

313 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Кастомные ответы — HTML, поток, файл и другие { #custom-response-html-stream-file-others }
По умолчанию **FastAPI** возвращает ответы с помощью `JSONResponse`.
Вы можете переопределить это, вернув `Response` напрямую, как показано в разделе [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}.
Но если вы возвращаете `Response` напрямую (или любой его подкласс, например `JSONResponse`), данные не будут автоматически преобразованы (даже если вы объявили `response_model`), и документация не будет автоматически сгенерирована (например, со специфичным «типом содержимого» в HTTP-заголовке `Content-Type` как частью сгенерированного OpenAPI).
Но вы можете также объявить `Response`, который хотите использовать (например, любой подкласс `Response`), в декораторе операции пути, используя параметр `response_class`.
Содержимое, которое вы возвращаете из своей функции-обработчика пути, будет помещено внутрь этого `Response`.
И если у этого `Response` тип содержимого JSON (`application/json`), как в случае с `JSONResponse` и `UJSONResponse`, данные, которые вы возвращаете, будут автоматически преобразованы (и отфильтрованы) любым объявленным вами в декораторе операции пути Pydantic `response_model`.
/// note | Примечание
Если вы используете класс ответа без типа содержимого, FastAPI будет ожидать, что у вашего ответа нет содержимого, поэтому он не будет документировать формат ответа в сгенерированной документации OpenAPI.
///
## Используйте `ORJSONResponse` { #use-orjsonresponse }
Например, если вы выжимаете максимум производительности, вы можете установить и использовать <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> и задать ответ как `ORJSONResponse`.
Импортируйте класс (подкласс) `Response`, который вы хотите использовать, и объявите его в декораторе операции пути.
Для больших ответов возвращать `Response` напрямую значительно быстрее, чем возвращать словарь.
Это потому, что по умолчанию FastAPI проверяет каждый элемент внутри и убеждается, что он сериализуем в JSON, используя тот же [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}, объяснённый в руководстве. Это позволяет возвращать **произвольные объекты**, например модели из базы данных.
Но если вы уверены, что содержимое, которое вы возвращаете, **сериализуемо в JSON**, вы можете передать его напрямую в класс ответа и избежать дополнительных накладных расходов, которые FastAPI понёс бы, пропуская возвращаемое содержимое через `jsonable_encoder` перед передачей в класс ответа.
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
/// info | Информация
Параметр `response_class` также используется для указания «типа содержимого» ответа.
В этом случае HTTP-заголовок `Content-Type` будет установлен в `application/json`.
И это будет задокументировано как таковое в OpenAPI.
///
/// tip | Совет
`ORJSONResponse` доступен только в FastAPI, а не в Starlette.
///
## HTML-ответ { #html-response }
Чтобы вернуть ответ с HTML напрямую из **FastAPI**, используйте `HTMLResponse`.
- Импортируйте `HTMLResponse`.
- Передайте `HTMLResponse` в параметр `response_class` вашего декоратора операции пути.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Информация
Параметр `response_class` также используется для указания «типа содержимого» ответа.
В этом случае HTTP-заголовок `Content-Type` будет установлен в `text/html`.
И это будет задокументировано как таковое в OpenAPI.
///
### Вернуть `Response` { #return-a-response }
Как показано в разделе [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}, вы также можете переопределить ответ прямо в своей операции пути, просто вернув его.
Тот же пример сверху, возвращающий `HTMLResponse`, может выглядеть так:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Предупреждение
`Response`, возвращённый напрямую вашей функцией-обработчиком пути, не будет задокументирован в OpenAPI (например, `Content-Type` не будет задокументирован) и не будет виден в автоматически сгенерированной интерактивной документации.
///
/// info | Информация
Разумеется, фактический заголовок `Content-Type`, статус-код и т.д. возьмутся из объекта `Response`, который вы вернули.
///
### Задокументировать в OpenAPI и переопределить `Response` { #document-in-openapi-and-override-response }
Если вы хотите переопределить ответ внутри функции, но при этом задокументировать «тип содержимого» в OpenAPI, вы можете использовать параметр `response_class` И вернуть объект `Response`.
Тогда `response_class` будет использоваться только для документирования *операции пути* в OpenAPI, а ваш `Response` будет использован как есть.
#### Вернуть `HTMLResponse` напрямую { #return-an-htmlresponse-directly }
Например, это может быть что-то вроде:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
В этом примере функция `generate_html_response()` уже генерирует и возвращает `Response` вместо возврата HTML в `str`.
Возвращая результат вызова `generate_html_response()`, вы уже возвращаете `Response`, который переопределит поведение **FastAPI** по умолчанию.
Но поскольку вы также передали `HTMLResponse` в `response_class`, **FastAPI** будет знать, как задокументировать это в OpenAPI и интерактивной документации как HTML с `text/html`:
<img src="/img/tutorial/custom-response/image01.png">
## Доступные ответы { #available-responses }
Ниже перечислены некоторые доступные классы ответов.
Учтите, что вы можете использовать `Response`, чтобы вернуть что угодно ещё, или даже создать собственный подкласс.
/// note | Технические детали
Вы также могли бы использовать `from starlette.responses import HTMLResponse`.
**FastAPI** предоставляет те же `starlette.responses` как `fastapi.responses` для вашего удобства как разработчика. Но большинство доступных классов ответов приходят непосредственно из Starlette.
///
### `Response` { #response }
Базовый класс `Response`, от него наследуются все остальные ответы.
Его можно возвращать напрямую.
Он принимает следующие параметры:
- `content``str` или `bytes`.
- `status_code` — целое число, HTTP статус-код.
- `headers` — словарь строк.
- `media_type` — строка, задающая тип содержимого. Например, `"text/html"`.
FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Также будет добавлен заголовок Content-Type, основанный на `media_type` и с добавлением charset для текстовых типов.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Принимает текст или байты и возвращает HTML-ответ, как описано выше.
### `PlainTextResponse` { #plaintextresponse }
Принимает текст или байты и возвращает ответ в виде простого текста.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Принимает данные и возвращает ответ, кодированный как `application/json`.
Это ответ по умолчанию, используемый в **FastAPI**, как было сказано выше.
### `ORJSONResponse` { #orjsonresponse }
Быстрая альтернативная реализация JSON-ответа с использованием <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, как было сказано выше.
/// info | Информация
Требуется установка `orjson`, например командой `pip install orjson`.
///
### `UJSONResponse` { #ujsonresponse }
Альтернативная реализация JSON-ответа с использованием <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
/// info | Информация
Требуется установка `ujson`, например командой `pip install ujson`.
///
/// warning | Предупреждение
`ujson` менее аккуратен, чем встроенная реализация Python, в обработке некоторых крайних случаев.
///
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
/// tip | Совет
Возможно, `ORJSONResponse` окажется более быстрым вариантом.
///
### `RedirectResponse` { #redirectresponse }
Возвращает HTTP-редирект. По умолчанию использует статус-код 307 (Temporary Redirect — временное перенаправление).
Вы можете вернуть `RedirectResponse` напрямую:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Или можно использовать его в параметре `response_class`:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Если вы сделаете так, то сможете возвращать URL напрямую из своей функции-обработчика пути.
В этом случае будет использован статус-код по умолчанию для `RedirectResponse`, то есть `307`.
---
Также вы можете использовать параметр `status_code` в сочетании с параметром `response_class`:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа потоково.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
#### Использование `StreamingResponse` с файлоподобными объектами { #using-streamingresponse-with-file-like-objects }
Если у вас есть <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">файлоподобный</a> объект (например, объект, возвращаемый `open()`), вы можете создать функцию-генератор для итерации по этому файлоподобному объекту.
Таким образом, вам не нужно сначала читать всё в память, вы можете передать эту функцию-генератор в `StreamingResponse` и вернуть его.
Это включает многие библиотеки для работы с облачным хранилищем, обработки видео и т.д.
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
1. Это функция-генератор. Она является «функцией-генератором», потому что содержит оператор(ы) `yield` внутри.
2. Используя блок `with`, мы гарантируем, что файлоподобный объект будет закрыт после завершения работы функции-генератора. То есть после того, как она закончит отправку ответа.
3. Этот `yield from` говорит функции итерироваться по объекту с именем `file_like`. И затем, для каждой итерации, отдавать эту часть как исходящую из этой функции-генератора (`iterfile`).
Таким образом, это функция-генератор, которая внутренне передаёт работу по «генерации» чему-то другому.
Делая это таким образом, мы можем поместить её в блок `with` и тем самым гарантировать, что файлоподобный объект будет закрыт после завершения.
/// tip | Совет
Заметьте, что здесь мы используем стандартный `open()`, который не поддерживает `async` и `await`, поэтому объявляем операцию пути обычной `def`.
///
### `FileResponse` { #fileresponse }
Асинхронно отправляет файл как ответ.
Для создания экземпляра принимает иной набор аргументов, чем другие типы ответов:
- `path` — путь к файлу, который будет отправлен.
- `headers` — любые дополнительные заголовки для включения, в виде словаря.
- `media_type` — строка, задающая тип содержимого. Если не задан, для определения типа содержимого будет использовано имя файла или путь.
- `filename` — если задан, будет включён в заголовок ответа `Content-Disposition`.
Файловые ответы будут содержать соответствующие заголовки `Content-Length`, `Last-Modified` и `ETag`.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
Вы также можете использовать параметр `response_class`:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.
## Пользовательский класс ответа { #custom-response-class }
Вы можете создать собственный класс ответа, унаследовавшись от `Response`, и использовать его.
Например, предположим, что вы хотите использовать <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, но с некоторыми пользовательскими настройками, которые не используются во встроенном классе `ORJSONResponse`.
Скажем, вы хотите, чтобы возвращался отформатированный JSON с отступами, то есть хотите использовать опцию orjson `orjson.OPT_INDENT_2`.
Вы могли бы создать `CustomORJSONResponse`. Главное, что вам нужно сделать — реализовать метод `Response.render(content)`, который возвращает содержимое как `bytes`:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Теперь вместо того, чтобы возвращать:
```json
{"message": "Hello World"}
```
...этот ответ вернёт:
```json
{
"message": "Hello World"
}
```
Разумеется, вы наверняка найдёте гораздо более полезные способы воспользоваться этим, чем просто форматирование JSON. 😉
## Класс ответа по умолчанию { #default-response-class }
При создании экземпляра класса **FastAPI** или `APIRouter` вы можете указать, какой класс ответа использовать по умолчанию.
Параметр, который это определяет, — `default_response_class`.
В примере ниже **FastAPI** будет использовать `ORJSONResponse` по умолчанию во всех операциях пути вместо `JSONResponse`.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Совет
Вы по-прежнему можете переопределять `response_class` в операциях пути, как и раньше.
///
## Дополнительная документация { #additional-documentation }
Вы также можете объявить тип содержимого и многие другие детали в OpenAPI с помощью `responses`: [Дополнительные ответы в OpenAPI](additional-responses.md){.internal-link target=_blank}.