Ir para o conteúdo

Resposta Personalizada - HTML, Stream, File e outras

🌐 Tradução por IA e humanos

Esta tradução foi feita por IA orientada por humanos. 🤝

Ela pode conter erros de interpretação do significado original ou soar pouco natural, etc. 🤖

Você pode melhorar esta tradução ajudando-nos a orientar melhor o LLM de IA.

Versão em inglês

Por padrão, o FastAPI retornará respostas JSON.

Você pode sobrescrever isso retornando uma 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" específico, no cabeçalho HTTP Content-Type como parte do OpenAPI gerado).

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

O conteúdo que você retorna da sua função de operação de rota será colocado dentro dessa Response.

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.

Respostas JSON

Por padrão, o FastAPI retorna respostas JSON.

Se você declarar um Modelo de Resposta, o FastAPI irá usá-lo para serializar os dados para JSON, usando Pydantic.

Se você não declarar um modelo de resposta, o FastAPI usará o jsonable_encoder explicado em Codificador Compatível com JSON e o colocará em uma JSONResponse.

Se você declarar uma response_class com um media type JSON (application/json), como no caso de JSONResponse, os dados que você retorna serão automaticamente convertidos (e filtrados) com qualquer response_model do Pydantic que você declarou no decorador de operação de rota. Mas os dados não serão serializados para bytes JSON com Pydantic; em vez disso, serão convertidos com o jsonable_encoder e então passados para a classe JSONResponse, que fará a serialização para bytes usando a biblioteca padrão de JSON do Python.

Performance com JSON

Resumindo, se você quer o máximo de performance, use um Modelo de Resposta e não declare uma response_class no decorador de operação de rota.

# Code above omitted 👆

@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item

# Code below omitted 👇
👀 Full file preview
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]

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)

Atenção

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 como você passou HTMLResponse em response_class também, o FastAPI saberá como documentar isso no OpenAPI e na documentação interativa como um HTML com text/html:

Respostas disponíveis

Aqui estão algumas das respostas 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 str ou bytes.
  • status_code - Um código de status HTTP do tipo int.
  • headers - Um 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.

Detalhes Técnicos

Mas se você declarar um modelo de resposta ou tipo de retorno, isso será usado diretamente para serializar os dados para JSON, e uma resposta com o media type correto para JSON será retornada diretamente, sem usar a classe JSONResponse.

Esta é a forma ideal para obter a melhor performance.

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 utilizado 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 um gerador assíncrono ou um gerador/iterador comum (uma função com yield) e transmite (stream) o corpo da resposta.

import anyio
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"
        await anyio.sleep(0)


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

Detalhes Técnicos

Uma tarefa async só pode ser cancelada quando alcança um await. Se não houver await, o gerador (função com yield) não pode ser cancelado adequadamente e pode continuar executando mesmo após o cancelamento ser solicitado.

Como este pequeno exemplo não precisa de nenhuma instrução await, adicionamos um await anyio.sleep(0) para dar ao event loop a chance de lidar com o cancelamento.

Isso seria ainda mais importante com streams grandes ou infinitos.

Dica

Em vez de retornar uma StreamingResponse diretamente, você deveria provavelmente seguir o estilo em Transmitir Dados, é muito mais conveniente e lida com cancelamento nos bastidores para você.

Se você estiver transmitindo JSON Lines, siga o tutorial Transmitir JSON Lines.

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 personalizados a serem incluídos, como um dicionário.
  • media_type - Uma string com o media type. Se não for definida, o nome do arquivo ou path será usado para inferir um media type.
  • filename - Se definido, será incluído no cabeçalho Content-Disposition.

Respostas de arquivos incluirão os cabeçalhos apropriados Content-Length, Last-Modified e ETag.

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 path 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 personalizada, herdando de Response e usando-a.

Por exemplo, vamos supor que você queira usar orjson com algumas configurações.

Vamos supor 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 CustomORJSONResponse. A principal coisa que você tem que fazer é criar um método Response.render(content) que retorne o conteúdo como bytes:

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. 😉

orjson ou Modelo de Resposta

Se o que você procura é performance, provavelmente é melhor usar um Modelo de Resposta do que uma resposta com orjson.

Com um modelo de resposta, o FastAPI usará o Pydantic para serializar os dados para JSON, sem passos intermediários, como convertê-los com jsonable_encoder, o que aconteceria em qualquer outro caso.

E, por baixo dos panos, o Pydantic usa os mesmos mecanismos em Rust que o orjson para serializar para JSON, então você já terá a melhor performance com um modelo de resposta.

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 parâmetro que define isso é o default_response_class.

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

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI(default_response_class=HTMLResponse)


@app.get("/items/")
async def read_items():
    return "<h1>Items</h1><p>This is a list of items.</p>"

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: Respostas Adicionais no OpenAPI.