Corpo da Requisição¶
Quando você precisa enviar dados de um cliente (como de um navegador web) para sua API, você o envia como um corpo da requisição.
O corpo da requisição é a informação enviada pelo cliente para sua API. O corpo da resposta é a informação que sua API envia para o cliente.
Sua API quase sempre irá enviar um corpo na resposta. Mas os clientes não necessariamente precisam enviar um corpo em toda requisição.
Para declarar um corpo da requisição, você utiliza os modelos do Pydantic com todos os seus poderes e benefícios.
Informação
Para enviar dados, você deve usar utilizar um dos métodos: POST
(Mais comum), PUT
, DELETE
ou PATCH
.
Enviar um corpo em uma requisição GET
não tem um comportamento definido nas especificações, porém é suportado pelo FastAPI, apenas para casos de uso bem complexos/extremos.
Como é desencorajado, a documentação interativa com Swagger UI não irá mostrar a documentação para o corpo da requisição para um GET
, e proxies que intermediarem podem não suportar o corpo da requisição.
Importe o BaseModel
do Pydantic¶
Primeiro, você precisa importar BaseModel
do pydantic
:
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
Crie seu modelo de dados¶
Então você declara seu modelo de dados como uma classe que herda BaseModel
.
Utilize os tipos Python padrão para todos os atributos:
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
Assim como quando declaramos parâmetros de consulta, quando um atributo do modelo possui um valor padrão, ele se torna opcional. Caso contrário, se torna obrigatório. Use None
para torná-lo opcional.
Por exemplo, o modelo acima declara um JSON "object
" (ou dict
no Python) como esse:
{
"name": "Foo",
"description": "Uma descrição opcional",
"price": 45.2,
"tax": 3.5
}
...como description
e tax
são opcionais (Com um valor padrão de None
), esse JSON "object
" também é válido:
{
"name": "Foo",
"price": 45.2
}
Declare como um parâmetro¶
Para adicionar o corpo na função de operação de rota, declare-o da mesma maneira que você declarou parâmetros de rota e consulta:
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
...E declare o tipo como o modelo que você criou, Item
.
Resultados¶
Apenas com esse declaração de tipos do Python, o FastAPI irá:
- Ler o corpo da requisição como um JSON.
- Converter os tipos correspondentes (se necessário).
- Validar os dados.
- Se algum dados for inválido, irá retornar um erro bem claro, indicando exatamente onde e o que está incorreto.
- Entregar a você a informação recebida no parâmetro
item
.- Como você o declarou na função como do tipo
Item
, você também terá o suporte do editor (completação, etc) para todos os atributos e seus tipos.
- Como você o declarou na função como do tipo
- Gerar um Esquema JSON com as definições do seu modelo, você também pode utilizá-lo em qualquer lugar que quiser, se fizer sentido para seu projeto.
- Esses esquemas farão parte do esquema OpenAPI, e utilizados nas UIs de documentação automática.
Documentação automática¶
Os esquemas JSON dos seus modelos farão parte do esquema OpenAPI gerado para sua aplicação, e aparecerão na documentação interativa da API:
E também serão utilizados em cada função de operação de rota que utilizá-los:
Suporte do editor de texto:¶
No seu editor de texto, dentro da função você receberá dicas de tipos e completação em todo lugar (isso não aconteceria se você recebesse um dict
em vez de um modelo Pydantic):
Você também poderá receber verificações de erros para operações de tipos incorretas:
Isso não é por acaso, todo o framework foi construído em volta deste design.
E foi imensamente testado na fase de design, antes de qualquer implementação, para garantir que funcionaria para todos os editores de texto.
Houveram mudanças no próprio Pydantic para que isso fosse possível.
As capturas de tela anteriores foram capturas no Visual Studio Code.
Mas você terá o mesmo suporte do editor no PyCharm e na maioria dos editores Python:
Dica
Se você utiliza o PyCharm como editor, você pode utilizar o Plugin do Pydantic para o PyCharm .
Melhora o suporte do editor para seus modelos Pydantic com::
- completação automática
- verificação de tipos
- refatoração
- buscas
- inspeções
Use o modelo¶
Dentro da função, você pode acessar todos os atributos do objeto do modelo diretamente:
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
Corpo da requisição + parâmetros de rota¶
Você pode declarar parâmetros de rota e corpo da requisição ao mesmo tempo.
O FastAPI irá reconhecer que os parâmetros da função que combinam com parâmetros de rota devem ser retirados da rota, e parâmetros da função que são declarados como modelos Pydantic sejam retirados do corpo da requisição.
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}
Corpo da requisição + parâmetros de rota + parâmetros de consulta¶
Você também pode declarar parâmetros de corpo, rota e consulta, ao mesmo tempo.
O FastAPI irá reconhecer cada um deles e retirar a informação do local correto.
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: Union[str, None] = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
Os parâmetros da função serão reconhecidos conforme abaixo:
- Se o parâmetro também é declarado na rota, será utilizado como um parâmetro de rota.
- Se o parâmetro é de um tipo único (como
int
,float
,str
,bool
, etc) será interpretado como um parâmetro de consulta. - Se o parâmetro é declarado como um modelo Pydantic, será interpretado como o corpo da requisição.
Observação
O FastAPI saberá que o valor de q
não é obrigatório por causa do valor padrão = None
.
O Union
em Union[str, None]
não é utilizado pelo FastAPI, mas permite ao seu editor de texto lhe dar um suporte melhor e detectar erros.
Sem o Pydantic¶
Se você não quer utilizar os modelos Pydantic, você também pode utilizar o parâmetro Body. Veja a documentação para Body - Parâmetros múltiplos: Valores singulares no body.