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 deresponse_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
) oubytes
.status_code
- Um código de status HTTP do tipoint
.headers
- Um dicionáriodict
de strings.media_type
- Umastr
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")
- Essa é a função geradora. É definida como "função geradora" porque contém declarações
yield
nela. - 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. -
Essa declaração
yield from
informa a função para iterar sobre essa coisa nomeada defile_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á transmitidoheaders
- 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çalhoContent-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.