Ir para o conteúdo

Resposta Personalizada - HTML, Stream, File e outras

Por padrão, o FastAPI irá retornar respostas utilizando JSONResponse.

Mas você pode sobrescrever esse comportamento utilizando Response diretamente, como visto em Retornando uma Resposta Diretamente.

Mas se você retornar uma Response diretamente (ou qualquer subclasse, como JSONResponse), os dados não serão convertidos automaticamente (mesmo que você declare um response_model), e a documentação não será gerada automaticamente (por exemplo, incluindo o "media type", no cabeçalho HTTP Content-Type como parte do esquema OpenAPI gerado).

Mas você também pode declarar a Response que você deseja utilizar (e.g. qualquer subclasse de Response), em um decorador de operação de rota utilizando o parâmetro response_class.

Os conteúdos que você retorna em sua função de operador de rota serão colocados dentro dessa Response.

E se a Response tiver um media type JSON (application/json), como é o caso com JSONResponse e UJSONResponse, os dados que você retornar serão automaticamente convertidos (e filtrados) com qualquer response_model do Pydantic que for declarado em sua função de operador de rota.

Nota

Se você utilizar uma classe de Resposta sem media type, o FastAPI esperará que sua resposta não tenha conteúdo, então ele não irá documentar o formato da resposta na documentação OpenAPI gerada.

Utilizando ORJSONResponse

Por exemplo, se você precisa bastante de performance, você pode instalar e utilizar o orjson e definir a resposta para ser uma ORJSONResponse.

Importe a classe, ou subclasse, de Response que você deseja utilizar e declare ela no decorador de operação de rota.

Para respostas grandes, retornar uma Response diretamente é muito mais rápido que retornar um dicionário.

Isso ocorre por que, por padrão, o FastAPI irá verificar cada item dentro do dicionário e garantir que ele seja serializável para JSON, utilizando o mesmoCodificador Compatível com JSON explicado no tutorial. Isso permite que você retorne objetos abstratos, como modelos do banco de dados, por exemplo.

Mas se você tem certeza que o conteúdo que você está retornando é serializável com JSON, você pode passá-lo diretamente para a classe de resposta e evitar o trabalho extra que o FastAPI teria ao passar o conteúdo pelo jsonable_encoder antes de passar para a classe de resposta.

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI()


@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
    return ORJSONResponse([{"item_id": "Foo"}])

Informação

O parâmetro response_class também será usado para definir o "media type" da resposta.

Neste caso, o cabeçalho HTTP Content-Type irá ser definido como application/json.

E será documentado como tal no OpenAPI.

Dica

A ORJSONResponse está disponível apenas no FastAPI, e não no Starlette.

Resposta HTML

Para retornar uma resposta com HTML diretamente do FastAPI, utilize HTMLResponse.

  • Importe HTMLResponse
  • Passe HTMLResponse como o parâmetro de response_class do seu decorador de operação de rota.
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """

Informação

O parâmetro response_class também será usado para definir o "media type" da resposta.

Neste caso, o cabeçalho HTTP Content-Type será definido como text/html.

E será documentado como tal no OpenAPI.

Retornando uma Response

Como visto em Retornando uma Resposta Diretamente, você também pode sobrescrever a resposta diretamente na sua operação de rota, ao retornar ela.

O mesmo exemplo de antes, retornando uma HTMLResponse, poderia parecer com:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/items/")
async def read_items():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)

Aviso

Uma Response retornada diretamente em sua função de operação de rota não será documentada no OpenAPI (por exemplo, o Content-Type não será documentado) e não será visível na documentação interativa automática.

Informação

Obviamente, o cabeçalho Content-Type, o código de status, etc, virão do objeto Response que você retornou.

Documentar no OpenAPI e sobrescrever Response

Se você deseja sobrescrever a resposta dentro de uma função, mas ao mesmo tempo documentar o "media type" no OpenAPI, você pode utilizar o parâmetro response_class E retornar um objeto Response.

A response_class será usada apenas para documentar o OpenAPI da operação de rota, mas sua Response será usada como foi definida.

Retornando uma HTMLResponse diretamente

Por exemplo, poderia ser algo como:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


def generate_html_response():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return generate_html_response()

Neste exemplo, a função generate_html_response() já cria e retorna uma Response em vez de retornar o HTML em uma str.

Ao retornar o resultado chamando generate_html_response(), você já está retornando uma Response que irá sobrescrever o comportamento padrão do FastAPI.

Mas se você passasse uma HTMLResponse em response_class também, o FastAPI saberia como documentar isso no OpenAPI e na documentação interativa como um HTML com text/html:

Respostas disponíveis

Aqui estão algumas dos tipos de resposta disponíveis.

Lembre-se que você pode utilizar Response para retornar qualquer outra coisa, ou até mesmo criar uma subclasse personalizada.

Detalhes Técnicos

Você também pode utilizar from starlette.responses import HTMLResponse.

O FastAPI provê a mesma starlette.responses como fastapi.responses apenas como uma facilidade para você, desenvolvedor. Mas a maioria das respostas disponíveis vêm diretamente do Starlette.

Response

A classe principal de respostas, todas as outras respostas herdam dela.

Você pode retorná-la diretamente.

Ela aceita os seguintes parâmetros:

  • content - Uma sequência de caracteres (str) ou bytes.
  • status_code - Um código de status HTTP do tipo int.
  • headers - Um dicionário dict de strings.
  • media_type - Uma str informando o media type. E.g. "text/html".

O FastAPI (Starlette, na verdade) irá incluir o cabeçalho Content-Length automaticamente. Ele também irá incluir o cabeçalho Content-Type, baseado no media_type e acrescentando uma codificação para tipos textuais.

from fastapi import FastAPI, Response

app = FastAPI()


@app.get("/legacy/")
def get_legacy_data():
    data = """<?xml version="1.0"?>
    <shampoo>
    <Header>
        Apply shampoo here.
    </Header>
    <Body>
        You'll have to use soap here.
    </Body>
    </shampoo>
    """
    return Response(content=data, media_type="application/xml")

HTMLResponse

Usa algum texto ou sequência de bytes e retorna uma resposta HTML. Como você leu acima.

PlainTextResponse

Usa algum texto ou sequência de bytes para retornar uma resposta de texto não formatado.

from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()


@app.get("/", response_class=PlainTextResponse)
async def main():
    return "Hello World"

JSONResponse

Pega alguns dados e retorna uma resposta com codificação application/json.

É a resposta padrão utilizada no FastAPI, como você leu acima.

ORJSONResponse

Uma alternativa mais rápida de resposta JSON utilizando o orjson, como você leu acima.

Informação

Essa resposta requer a instalação do pacote orjson, com o comando pip install orjson, por exemplo.

UJSONResponse

Uma alternativa de resposta JSON utilizando a biblioteca ujson.

Informação

Essa resposta requer a instalação do pacote ujson, com o comando pip install ujson, por exemplo.

Aviso

ujson é menos cauteloso que a implementação nativa do Python na forma que os casos especiais são tratados

from fastapi import FastAPI
from fastapi.responses import UJSONResponse

app = FastAPI()


@app.get("/items/", response_class=UJSONResponse)
async def read_items():
    return [{"item_id": "Foo"}]

Dica

É possível que ORJSONResponse seja uma alternativa mais rápida.

RedirectResponse

Retorna um redirecionamento HTTP. Utiliza o código de status 307 (Redirecionamento Temporário) por padrão.

Você pode retornar uma RedirectResponse diretamente:

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/typer")
async def redirect_typer():
    return RedirectResponse("https://typer.tiangolo.com")

Ou você pode utilizá-la no parâmetro response_class:

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
    return "https://fastapi.tiangolo.com"

Se você fizer isso, então você pode retornar a URL diretamente da sua função de operação de rota

Neste caso, o status_code utilizada será o padrão de RedirectResponse, que é 307.


Você também pode utilizar o parâmetro status_code combinado com o parâmetro response_class:

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
    return "https://docs.pydantic.dev/"

StreamingResponse

Recebe uma gerador assíncrono ou um gerador/iterador comum e retorna o corpo da requisição continuamente (stream).

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()


async def fake_video_streamer():
    for i in range(10):
        yield b"some fake video bytes"


@app.get("/")
async def main():
    return StreamingResponse(fake_video_streamer())

Utilizando StreamingResponse com objetos semelhantes a arquivos

Se você tiver um objeto semelhante a um arquivo (e.g. o objeto retornado por open()), você pode criar uma função geradora para iterar sobre esse objeto.

Dessa forma, você não precisa ler todo o arquivo na memória primeiro, e você pode passar essa função geradora para StreamingResponse e retorná-la.

Isso inclui muitas bibliotecas que interagem com armazenamento em nuvem, processamento de vídeos, entre outras.

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/")
def main():
    def iterfile():  # (1)
        with open(some_file_path, mode="rb") as file_like:  # (2)
            yield from file_like  # (3)

    return StreamingResponse(iterfile(), media_type="video/mp4")
  1. Essa é a função geradora. É definida como "função geradora" porque contém declarações yield nela.
  2. Ao utilizar o bloco with, nós garantimos que o objeto semelhante a um arquivo é fechado após a função geradora ser finalizada. Isto é, após a resposta terminar de ser enivada.
  3. Essa declaração yield from informa a função para iterar sobre essa coisa nomeada de file_like. E então, para cada parte iterada, fornece essa parte como se viesse dessa função geradora (iterfile).

    Então, é uma função geradora que transfere o trabalho de "geração" para alguma outra coisa interna.

    Fazendo dessa forma, podemos colocá-la em um bloco with, e assim garantir que o objeto semelhante a um arquivo é fechado quando a função termina.

Dica

Perceba que aqui estamos utilizando o open() da biblioteca padrão que não suporta async e await, e declaramos a operação de rota com o def básico.

FileResponse

Envia um arquivo de forma assíncrona e contínua (stream). * Recebe um conjunto de argumentos do construtor diferente dos outros tipos de resposta:

  • path - O caminho do arquivo que será transmitido
  • headers - quaisquer cabeçalhos que serão incluídos, como um dicionário.
  • media_type - Uma string com o media type. Se não for definida, o media type é inferido a partir do nome ou caminho do arquivo.
  • filename - Se for definido, é incluído no cabeçalho Content-Disposition.

Respostas de Arquivos incluem o tamanho do arquivo, data da última modificação e ETags apropriados, nos cabeçalhos Content-Length, Last-Modified e ETag, respectivamente.

from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/")
async def main():
    return FileResponse(some_file_path)

Você também pode usar o parâmetro response_class:

from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/", response_class=FileResponse)
async def main():
    return some_file_path

Nesse caso, você pode retornar o caminho do arquivo diretamente da sua função de operação de rota.

Classe de resposta personalizada

Você pode criar sua própria classe de resposta, herdando de Response e usando essa nova classe.

Por exemplo, vamos supor que você queira utilizar o orjson, mas com algumas configurações personalizadas que não estão incluídas na classe ORJSONResponse.

Vamos supor também que você queira retornar um JSON indentado e formatado, então você quer utilizar a opção orjson.OPT_INDENT_2 do orjson.

Você poderia criar uma classe CustomORJSONResponse. A principal coisa a ser feita é sobrecarregar o método render da classe Response, Response.render(content), que retorna o conteúdo em bytes, para retornar o conteúdo que você deseja:

from typing import Any

import orjson
from fastapi import FastAPI, Response

app = FastAPI()


class CustomORJSONResponse(Response):
    media_type = "application/json"

    def render(self, content: Any) -> bytes:
        assert orjson is not None, "orjson must be installed"
        return orjson.dumps(content, option=orjson.OPT_INDENT_2)


@app.get("/", response_class=CustomORJSONResponse)
async def main():
    return {"message": "Hello World"}

Agora em vez de retornar:

{"message": "Hello World"}

...essa resposta retornará:

{
  "message": "Hello World"
}

Obviamente, você provavelmente vai encontrar maneiras muito melhores de se aproveitar disso do que a formatação de JSON. 😉

Classe de resposta padrão

Quando você criar uma instância da classe FastAPI ou um APIRouter você pode especificar qual classe de resposta utilizar por padrão.

O padrão que define isso é o default_response_class.

No exemplo abaixo, o FastAPI irá utilizar ORJSONResponse por padrão, em todas as operações de rota, em vez de JSONResponse.

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI(default_response_class=ORJSONResponse)


@app.get("/items/")
async def read_items():
    return [{"item_id": "Foo"}]

Dica

Você ainda pode substituir response_class em operações de rota como antes.

Documentação adicional

Você também pode declarar o media type e muitos outros detalhes no OpenAPI utilizando responses: Retornos Adicionais no OpenAPI.