Retornos Adicionais no OpenAPI¶
Aviso
Este é um tema bem avançado.
Se você está começando com o FastAPI, provavelmente você não precisa disso.
Você pode declarar retornos adicionais, com códigos de status adicionais, media types, descrições, etc.
Essas respostas adicionais serão incluídas no esquema do OpenAPI, e também aparecerão na documentação da API.
Porém para as respostas adicionais, você deve garantir que está retornando um Response
como por exemplo o JSONResponse
diretamente, junto com o código de status e o conteúdo.
Retorno Adicional com model
¶
Você pode fornecer o parâmetro responses
aos seus decoradores de caminho.
Este parâmetro recebe um dict
, as chaves são os códigos de status para cada retorno, como por exemplo 200
, e os valores são um outro dict
com a informação de cada um deles.
Cada um desses dict
de retorno pode ter uma chave model
, contendo um modelo do Pydantic, assim como o response_model
.
O FastAPI pegará este modelo, gerará o esquema JSON dele e incluirá no local correto do OpenAPI.
Por exemplo, para declarar um outro retorno com o status code 404
e um modelo do Pydantic chamado Message
, você pode escrever:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
return JSONResponse(status_code=404, content={"message": "Item not found"})
Nota
Lembre-se que você deve retornar o JSONResponse
diretamente.
Informação
A chave model
não é parte do OpenAPI.
O FastAPI pegará o modelo do Pydantic, gerará o JSON Schema
, e adicionará no local correto.
O local correto é:
- Na chave
content
, que tem como valor um outro objeto JSON (dict
) que contém:- Uma chave com o media type, como por exemplo
application/json
, que contém como valor um outro objeto JSON, contendo::- Uma chave
schema
, que contém como valor o JSON Schema do modelo, sendo este o local correto.- O FastAPI adiciona aqui a referência dos esquemas JSON globais que estão localizados em outro lugar, ao invés de incluí-lo diretamente. Deste modo, outras aplicações e clientes podem utilizar estes esquemas JSON diretamente, fornecer melhores ferramentas de geração de código, etc.
- Uma chave
- Uma chave com o media type, como por exemplo
O retorno gerado no OpenAI para esta operação de caminho será:
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
Os esquemas são referenciados em outro local dentro do esquema OpenAPI:
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
Media types adicionais para o retorno principal¶
Você pode utilizar o mesmo parâmetro responses
para adicionar diferentes media types para o mesmo retorno principal.
Por exemplo, você pode adicionar um media type adicional de image/png
, declarando que a sua operação de caminho pode retornar um objeto JSON (com o media type application/json
) ou uma imagem PNG:
from typing import Union
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
200: {
"content": {"image/png": {}},
"description": "Return the JSON item or an image.",
}
},
)
async def read_item(item_id: str, img: Union[bool, None] = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}
Nota
Note que você deve retornar a imagem utilizando um FileResponse
diretamente.
Informação
A menos que você especifique um media type diferente explicitamente em seu parâmetro responses
, o FastAPI assumirá que o retorno possui o mesmo media type contido na classe principal de retorno (padrão application/json
).
Porém se você especificou uma classe de retorno com o valor None
como media type, o FastAPI utilizará application/json
para qualquer retorno adicional que possui um modelo associado.
Combinando informações¶
Você também pode combinar informações de diferentes lugares, incluindo os parâmetros response_model
, status_code
, e responses
.
Você pode declarar um response_model
, utilizando o código de status padrão 200
(ou um customizado caso você precise), e depois adicionar informações adicionais para esse mesmo retorno em responses
, diretamente no esquema OpenAPI.
O FastAPI manterá as informações adicionais do responses
, e combinará com o esquema JSON do seu modelo.
Por exemplo, você pode declarar um retorno com o código de status 404
que utiliza um modelo do Pydantic que possui um description
customizado.
E um retorno com o código de status 200
que utiliza o seu response_model
, porém inclui um example
customizado:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
404: {"model": Message, "description": "The item was not found"},
200: {
"description": "Item requested by ID",
"content": {
"application/json": {
"example": {"id": "bar", "value": "The bar tenders"}
}
},
},
},
)
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
else:
return JSONResponse(status_code=404, content={"message": "Item not found"})
Isso será combinado e incluído em seu OpenAPI, e disponibilizado na documentação da sua API:
Combinar retornos predefinidos e personalizados¶
Você pode querer possuir alguns retornos predefinidos que são aplicados para diversas operações de caminho, porém você deseja combinar com retornos personalizados que são necessários para cada operação de caminho.
Para estes casos, você pode utilizar a técnica do Python de "desempacotamento" de um dict
utilizando **dict_to_unpack
:
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
Aqui, o new_dict
terá todos os pares de chave-valor do old_dict
mais o novo par de chave-valor:
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
Você pode utilizar essa técnica para reutilizar alguns retornos predefinidos nas suas operações de caminho e combiná-las com personalizações adicionais.
Por exemplo:
from typing import Union
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
responses = {
404: {"description": "Item not found"},
302: {"description": "The item was moved"},
403: {"description": "Not enough privileges"},
}
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={**responses, 200: {"content": {"image/png": {}}}},
)
async def read_item(item_id: str, img: Union[bool, None] = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}
Mais informações sobre retornos OpenAPI¶
Para verificar exatamente o que você pode incluir nos retornos, você pode conferir estas seções na especificação do OpenAPI:
- Objeto de Retorno OpenAPI, inclui o
Response Object
. - Objeto de Retorno OpenAPI, você pode incluir qualquer coisa dele diretamente em cada retorno dentro do seu parâmetro
responses
. Incluindodescription
,headers
,content
(dentro dele que você declara diferentes media types e esquemas JSON), elinks
.