mirror of https://github.com/tiangolo/fastapi.git
313 lines
20 KiB
Markdown
313 lines
20 KiB
Markdown
# Кастомные ответы — 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}.
|