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

18 KiB
Raw Blame History

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

По умолчанию FastAPI возвращает ответы в формате JSON.

Вы можете переопределить это, вернув Response напрямую, как показано в разделе Вернуть Response напрямую.

Но если вы возвращаете Response напрямую (или любой его подкласс, например JSONResponse), данные не будут автоматически преобразованы (даже если вы объявили response_model), и документация не будет автоматически сгенерирована (например, со специфичным «типом содержимого» в HTTP-заголовке Content-Type как частью сгенерированного OpenAPI).

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

Содержимое, которое вы возвращаете из своей функции-обработчика пути, будет помещено внутрь этого Response.

/// note | Примечание

Если вы используете класс ответа без типа содержимого, FastAPI будет ожидать, что у вашего ответа нет содержимого, поэтому он не будет документировать формат ответа в сгенерированной документации OpenAPI.

///

JSON-ответы

По умолчанию FastAPI возвращает ответы в формате JSON.

Если вы объявите Модель ответа, FastAPI использует её для сериализации данных в JSON с помощью Pydantic.

Если вы не объявите модель ответа, FastAPI использует jsonable_encoder, описанный в разделе JSON-совместимый энкодер, и поместит результат в JSONResponse.

Если вы объявите response_class с JSON типом содержимого (application/json), как в случае с JSONResponse, данные, которые вы возвращаете, будут автоматически преобразованы (и отфильтрованы) любой Pydantic-моделью ответа (response_model), объявленной вами в декораторе операции пути. Но данные не будут сериализованы в JSON-байты через Pydantic; вместо этого они будут преобразованы с помощью jsonable_encoder, а затем переданы в класс JSONResponse, который сериализует их в байты, используя стандартную JSON-библиотеку Python.

Производительность JSON

Коротко: если вам нужна максимальная производительность, используйте Модель ответа и не объявляйте response_class в декораторе операции пути.

{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}

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 напрямую, вы также можете переопределить ответ прямо в своей операции пути, просто вернув его.

Тот же пример сверху, возвращающий 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, как было сказано выше.

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

Но если вы объявите модель ответа или тип возвращаемого значения, они будут использованы напрямую для сериализации данных в JSON, и ответ с корректным типом содержимого для JSON будет возвращён напрямую, без использования класса JSONResponse.

Это идеальный способ получить наилучшую производительность.

///

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

Принимает асинхронный генератор или обычный генератор/итератор (функцию с yield) и отправляет тело ответа потоково.

{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}

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

Задача async может быть отменена только при достижении await. Если await отсутствует, генератор (функция с yield) не может быть корректно отменён и может продолжить работу даже после запроса на отмену.

Так как этому небольшому примеру не нужны операторы await, мы добавляем await anyio.sleep(0), чтобы дать циклу событий возможность обработать отмену.

Это ещё более важно для больших или бесконечных потоков.

///

/// tip | Совет

Вместо того чтобы возвращать StreamingResponse напрямую, вероятно, лучше следовать стилю из раздела Передача данных потоком - так гораздо удобнее, и отмена обрабатывается «за кулисами».

Если вы передаёте JSON Lines потоком, следуйте руководству Поток JSON Lines.

///

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 с некоторыми настройками.

Скажем, вы хотите, чтобы возвращался отформатированный 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. 😉

orjson или Модель ответа

Если вы стремитесь увеличить производительность, вероятно, лучше использовать Модель ответа, чем ответ на базе orjson.

С моделью ответа FastAPI использует Pydantic для сериализации данных в JSON, без промежуточных шагов, таких как преобразование через jsonable_encoder, которое происходило бы в любом другом случае.

А под капотом Pydantic использует те же базовые механизмы на Rust, что и orjson, для сериализации в JSON, так что с моделью ответа вы и так получите лучшую производительность.

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

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

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

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

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

/// tip | Совет

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

///

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

Вы также можете объявить тип содержимого и многие другие детали в OpenAPI с помощью responses: Дополнительные ответы в OpenAPI.