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

20 KiB
Raw Blame History

Кастомные ответы — HTML, поток, файл и другие

По умолчанию FastAPI возвращает ответы с помощью JSONResponse.

Вы можете переопределить это, вернув Response напрямую, как показано в разделе Вернуть Response напрямую{.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

Например, если вы выжимаете максимум производительности, вы можете установить и использовать orjson и задать ответ как ORJSONResponse.

Импортируйте класс (подкласс) Response, который вы хотите использовать, и объявите его в декораторе операции пути.

Для больших ответов возвращать Response напрямую значительно быстрее, чем возвращать словарь.

Это потому, что по умолчанию FastAPI проверяет каждый элемент внутри и убеждается, что он сериализуем в JSON, используя тот же JSON Compatible Encoder{.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 напрямую из 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

Как показано в разделе Вернуть Response напрямую{.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

Если вы хотите переопределить ответ внутри функции, но при этом задокументировать «тип содержимого» в OpenAPI, вы можете использовать параметр response_class И вернуть объект Response.

Тогда response_class будет использоваться только для документирования операции пути в OpenAPI, а ваш Response будет использован как есть.

Вернуть HTMLResponse напрямую

Например, это может быть что-то вроде:

{* ../../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:

Доступные ответы

Ниже перечислены некоторые доступные классы ответов.

Учтите, что вы можете использовать Response, чтобы вернуть что угодно ещё, или даже создать собственный подкласс.

/// note | Технические детали

Вы также могли бы использовать from starlette.responses import HTMLResponse.

FastAPI предоставляет те же starlette.responses как fastapi.responses для вашего удобства как разработчика. Но большинство доступных классов ответов приходят непосредственно из Starlette.

///

Response

Базовый класс Response, от него наследуются все остальные ответы.

Его можно возвращать напрямую.

Он принимает следующие параметры:

  • contentstr или 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

Принимает текст или байты и возвращает HTML-ответ, как описано выше.

PlainTextResponse

Принимает текст или байты и возвращает ответ в виде простого текста.

{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}

JSONResponse

Принимает данные и возвращает ответ, кодированный как application/json.

Это ответ по умолчанию, используемый в FastAPI, как было сказано выше.

ORJSONResponse

Быстрая альтернативная реализация JSON-ответа с использованием orjson, как было сказано выше.

/// info | Информация

Требуется установка orjson, например командой pip install orjson.

///

UJSONResponse

Альтернативная реализация JSON-ответа с использованием ujson.

/// info | Информация

Требуется установка ujson, например командой pip install ujson.

///

/// warning | Предупреждение

ujson менее аккуратен, чем встроенная реализация Python, в обработке некоторых крайних случаев.

///

{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}

/// tip | Совет

Возможно, ORJSONResponse окажется более быстрым вариантом.

///

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

Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа потоково.

{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}

Использование StreamingResponse с файлоподобными объектами

Если у вас есть файлоподобный объект (например, объект, возвращаемый 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

Асинхронно отправляет файл как ответ.

Для создания экземпляра принимает иной набор аргументов, чем другие типы ответов:

  • 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] *}

В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.

Пользовательский класс ответа

Вы можете создать собственный класс ответа, унаследовавшись от Response, и использовать его.

Например, предположим, что вы хотите использовать orjson, но с некоторыми пользовательскими настройками, которые не используются во встроенном классе ORJSONResponse.

Скажем, вы хотите, чтобы возвращался отформатированный JSON с отступами, то есть хотите использовать опцию orjson orjson.OPT_INDENT_2.

Вы могли бы создать CustomORJSONResponse. Главное, что вам нужно сделать — реализовать метод Response.render(content), который возвращает содержимое как bytes:

{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}

Теперь вместо того, чтобы возвращать:

{"message": "Hello World"}

...этот ответ вернёт:

{
  "message": "Hello World"
}

Разумеется, вы наверняка найдёте гораздо более полезные способы воспользоваться этим, чем просто форматирование JSON. 😉

Класс ответа по умолчанию

При создании экземпляра класса FastAPI или APIRouter вы можете указать, какой класс ответа использовать по умолчанию.

Параметр, который это определяет, — default_response_class.

В примере ниже FastAPI будет использовать ORJSONResponse по умолчанию во всех операциях пути вместо JSONResponse.

{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}

/// tip | Совет

Вы по-прежнему можете переопределять response_class в операциях пути, как и раньше.

///

Дополнительная документация

Вы также можете объявить тип содержимого и многие другие детали в OpenAPI с помощью responses: Дополнительные ответы в OpenAPI{.internal-link target=_blank}.