请求体¶
当你需要从客户端(比如浏览器)向你的API发送数据时,你可以将其作为**请求体**发送。
**请求体**是客户端发送给你的API的数据。**响应体**是你的API发送给客户端的数据。
你的API几乎总是需要发送**响应体**。但客户端并不总是需要发送**请求体**,有时它们只请求一个路径,可能带有一些查询参数,但并不发送请求体。
要声明一个**请求体**,你可以使用Pydantic模型,充分利用其功能和优势。
/// 信息
要发送数据,你应该使用以下方法之一:POST
(更常见)、PUT
、DELETE
或 PATCH
。
在规范中,发送带有GET
请求的请求体的行为是未定义的,尽管如此,FastAPI支持这种做法,仅限于非常复杂或极端的使用场景。
由于不推荐这种做法,Swagger UI的交互式文档在使用GET
时不会显示请求体的文档,中间的代理可能也不支持这种做法。
///
导入Pydantic的BaseModel
¶
首先,你需要从pydantic
导入BaseModel
:
//// 标签 | Python 3.10+
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
////
//// 标签 | Python 3.8+
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
////
创建你的数据模型¶
然后,你可以将你的数据模型声明为一个继承自BaseModel
的类。
为所有属性使用标准的Python类型:
//// 标签 | Python 3.10+
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
////
//// 标签 | Python 3.8+
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
////
与声明查询参数时一样,当模型属性具有默认值时,它不是必需的。否则,它是必需的。使用None
使其成为可选的。
例如,上面的模型声明了一个JSON“对象
”(或Python dict
),如下所示:
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
...由于description
和tax
是可选的(默认值为None
),这个JSON“对象
”也是有效的:
{
"name": "Foo",
"price": 45.2
}
将其声明为参数¶
要将其添加到你的*路径操作*中,你可以像声明路径和查询参数一样进行声明:
//// 标签 | Python 3.10+
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
////
//// 标签 | Python 3.8+
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
////
...并将其类型声明为你创建的模型Item
。
结果¶
仅通过这一Python类型声明,**FastAPI**将:
- 读取请求体作为JSON。
- 转换相应的类型(如果需要)。
- 验证数据。
- 如果数据无效,它将返回一个清晰且详细的错误,指出具体哪里和什么数据不正确。
- 在参数
item
中提供接收到的数据。- 由于你在函数中将其声明为
Item
类型,你还将获得所有属性和其类型的编辑器支持(自动补全等)。
- 由于你在函数中将其声明为
- 为你的模型生成JSON Schema定义,你也可以在项目的其他地方使用这些定义,如果它们对你的项目有意义的话。
- 这些模式将成为生成的OpenAPI模式的一部分,并被自动文档UI使用。
自动文档¶
你的模型的JSON模式将成为你生成的OpenAPI模式的一部分,并显示在交互式API文档中:
它们还将在每个需要它们的*路径操作*的API文档中使用:
编辑器支持¶
在你的编辑器中,在你的函数内部,你将在任何地方获得类型提示和自动补全(如果你接收的是dict
而不是Pydantic模型,则不会发生这种情况):
你还会得到对不正确类型操作的错误检查:
这不是偶然的,整个框架都是围绕这种设计构建的。
在实施之前,它在设计阶段经过了彻底的测试,以确保它能在所有编辑器中工作。
甚至还有一些变化是针对Pydantic本身来支持这一点的。
之前的截图是在Visual Studio Code中拍摄的。
但你会在PyCharm和大多数其他Python编辑器中获得相同的编辑器支持:
/// 提示 如果你使用 PyCharm 作为你的编辑器,你可以使用 Pydantic PyCharm 插件。
它增强了编辑器对 Pydantic 模型的支持,具有以下功能:
- 自动补全
- 类型检查
- 重构
- 搜索
- 检查
///
使用模型¶
在函数内部,你可以直接访问模型对象的所有属性:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: 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
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
请求体 + 路径参数¶
你可以同时声明路径参数和请求体。
FastAPI 会识别出与路径参数匹配的函数参数应该**从路径中获取**,而声明为 Pydantic 模型的函数参数应该**从请求体中获取**。
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}
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 update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}
请求体 + 路径 + 查询参数¶
你也可以同时声明**请求体**、**路径**和**查询**参数。
FastAPI 会识别每一个参数,并从正确的位置获取数据。
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
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 update_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
函数参数将被识别如下:
- 如果参数也在**路径**中声明,它将被用作路径参数。
- 如果参数是**单一类型**(如
int
、float
、str
、bool
等),它将被解释为**查询**参数。 - 如果参数被声明为**Pydantic 模型**类型,它将被解释为请求**体**。
Note
FastAPI 会知道 q
的值不是必需的,因为它的默认值是 = None
。
str | None
(Python 3.10+)或 Union
在 Union[str, None]
(Python 3.8+)中不被 FastAPI 用来确定该值不是必需的,它会知道它不是必需的,因为它有一个默认值 = None
。
但添加类型注解将允许你的编辑器提供更好的支持并检测错误。
不使用 Pydantic¶
如果你不想使用 Pydantic 模型,你也可以使用 Body 参数。请参阅文档 Body - 多参数:请求体中的单一值。