FastAPI 请求体(Request Body)
1. 基础用法
请求体是客户端发送给服务器的数据,FastAPI 使用 Pydantic 模型定义和验证:
frompydanticimportBaseModelfromtypingimportOptionalclassItemCreate(BaseModel):name:strprice:floatdescription:Optional[str]=Nonetags:list[str]=[]@app.post("/items")asyncdefcreate_item(item:ItemCreate):returnitemcurl-XPOST http://localhost:8000/items\-H"Content-Type: application/json"\-d'{"name": "Phone", "price": 599.99, "description": "Smartphone", "tags": ["electronics"]}'# {"name":"Phone","price":599.99,"description":"Smartphone","tags":["electronics"]}识别规则:函数参数中,Pydantic 模型类型自动识别为请求体,单值类型(
str/int等)识别为查询参数。
2. 字段验证(Field)
frompydanticimportBaseModel,FieldfromtypingimportOptionalclassItemCreate(BaseModel):name:str=Field(...,# 必填min_length=1,max_length=100,title="商品名称",description="商品的名称",)price:float=Field(...,gt=0,# 大于 0description="商品价格",)quantity:int=Field(default=1,ge=0,# 大于等于 0)description:Optional[str]=Field(default=None,max_length=500,)discount:float=Field(default=0.0,ge=0.0,le=1.0,# 0.0 ~ 1.0)数值约束
| 约束 | 含义 | 适用类型 |
|---|---|---|
gt | 大于 | int,float |
ge | 大于等于 | int,float |
lt | 小于 | int,float |
le | 小于等于 | int,float |
multiple_of | 必须是某数的倍数 | int,float |
字符串约束
| 约束 | 含义 |
|---|---|
min_length | 最小长度 |
max_length | 最大长度 |
pattern | 正则匹配 |
集合约束
| 约束 | 含义 | 适用类型 |
|---|---|---|
min_length | 最少元素数 | list,set,tuple |
max_length | 最多元素数 | list,set,tuple |
3. 嵌套模型
frompydanticimportBaseModelfromtypingimportOptionalclassAddress(BaseModel):street:strcity:strzip_code:strcountry:str="China"classCompany(BaseModel):name:straddress:Address# 嵌套对象classUserCreate(BaseModel):name:stremail:straddress:Address# 嵌套对象company:Optional[Company]=Nonehobbies:list[str]=[]请求体示例:
{"name":"Alice","email":"alice@example.com","address":{"street":"123 Main St","city":"Shanghai","zip_code":"200000"},"company":{"name":"Tech Corp","address":{"street":"456 Tech Ave","city":"Beijing","zip_code":"100000"}},"hobbies":["reading","coding"]}4. 模型示例(Examples)
frompydanticimportBaseModel,FieldclassItemCreate(BaseModel):name:str=Field(...,examples=["Phone"])price:float=Field(...,gt=0,examples=[599.99])description:str=Field(default="",examples=["A great smartphone"])# 模型级示例(在文档中展示)model_config={"json_schema_extra":{"examples":[{"name":"Laptop","price":1299.99,"description":"High-performance laptop",}]}}5. 多个请求体参数
classItem(BaseModel):name:strprice:floatclassUser(BaseModel):username:stremail:str@app.put("/items/{item_id}")asyncdefupdate_item(item_id:int,item:Item,user:User):return{"item_id":item_id,"item":item,"user":user}请求体需要用参数名作为 key:
{"item":{"name":"Phone","price":599},"user":{"username":"alice","email":"alice@example.com"}}单个嵌入请求体(Body embed)
fromfastapiimportBodyclassItem(BaseModel):name:strprice:float# 默认:直接发送 {"name": "Phone", "price": 599}@app.post("/items")asyncdefcreate_item(item:Item):returnitem# 嵌入:需要 {"item": {"name": "Phone", "price": 599}}@app.post("/items")asyncdefcreate_item(item:Item=Body(embed=True)):returnitem6. Body() 单字段请求体
当请求体只是一个单独的值(不是对象)时:
fromfastapiimportBody@app.post("/items")asyncdefcreate_item(name:str=Body(...,min_length=1),price:float=Body(...,gt=0),description:str=Body(default=""),):return{"name":name,"price":price,"description":description}{"name":"Phone","price":599.99,"description":"Smartphone"}
Body()与Query()的区别:Body()从请求体 JSON 中取值,Query()从 URL 查询参数取值。
7. 请求体 + 路径参数 + 查询参数
fromfastapiimportPath,QueryclassItemUpdate(BaseModel):name:Optional[str]=Noneprice:Optional[float]=None@app.put("/items/{item_id}")asyncdefupdate_item(item_id:int=Path(gt=0),# 路径参数item:ItemUpdate=None,# 请求体urgent:bool=Query(default=False),# 查询参数):return{"item_id":item_id,"item":item,"urgent":urgent}curl-XPUT"http://localhost:8000/items/42?urgent=true"\-H"Content-Type: application/json"\-d'{"name": "Phone Pro", "price": 799.99}'8. 模型继承与复用
classItemBase(BaseModel):name:str=Field(...,min_length=1,max_length=100)description:Optional[str]=Noneprice:float=Field(...,gt=0)classItemCreate(ItemBase):"""创建时所有字段必填"""passclassItemUpdate(ItemBase):"""更新时所有字段可选"""name:Optional[str]=Nonedescription:Optional[str]=Noneprice:Optional[float]=NoneclassItemResponse(ItemBase):"""响应时包含 id,不含敏感字段"""id:intcreated_at:datetime9. 模型验证器
field_validator(字段级验证)
frompydanticimportBaseModel,field_validatorclassUserCreate(BaseModel):username:stremail:strpassword:strconfirm_password:str@field_validator("username")@classmethoddefusername_alphanumeric(cls,v):ifnotv.isalnum():raiseValueError("Username must be alphanumeric")returnv@field_validator("password")@classmethoddefpassword_strength(cls,v):iflen(v)<8:raiseValueError("Password must be at least 8 characters")ifnotany(c.isupper()forcinv):raiseValueError("Password must contain uppercase letter")returnvmodel_validator(模型级验证)
frompydanticimportBaseModel,model_validatorclassUserCreate(BaseModel):password:strconfirm_password:str@model_validator(mode="after")defpasswords_match(self):ifself.password!=self.confirm_password:raiseValueError("Passwords do not match")returnselfclassDateRange(BaseModel):start_date:date end_date:date@model_validator(mode="after")defend_after_start(self):ifself.end_date<self.start_date:raiseValueError("end_date must be after start_date")returnself10. 访问原始请求数据
fromfastapiimportRequest@app.post("/raw")asyncdefraw_request(request:Request):# 原始 JSON bodybody=awaitrequest.json()# 原始 bytesraw=awaitrequest.body()# 表单数据form=awaitrequest.form()return{"body":body}11. 请求体中的特殊类型
frompydanticimportBaseModel,HttpUrl,EmailStrfromdatetimeimportdatetime,datefromdecimalimportDecimalfromuuidimportUUIDclassRichModel(BaseModel):# URL 类型(自动验证格式)website:HttpUrl# 邮箱类型(自动验证格式)email:EmailStr# 日期时间类型created_at:datetime birth_date:date# 高精度数值amount:Decimal# UUIDrequest_id:UUID# 字典类型metadata:dict[str,str]={}# 任意 JSONextra:dict={}{"website":"https://example.com","email":"user@example.com","created_at":"2024-01-15T10:30:00","birth_date":"1990-05-20","amount":"99.99","request_id":"550e8400-e29b-41d4-a716-446655440000","metadata":{"env":"prod"},"extra":{"any":"data"}}12. 请求体与响应模型分离
frompydanticimportBaseModel# 请求模型:包含密码classUserCreate(BaseModel):username:stremail:strpassword:str# 响应模型:不含密码classUserResponse(BaseModel):id:intusername:stremail:str@app.post("/users",response_model=UserResponse,# 响应使用 UserResponsestatus_code=201,)asyncdefcreate_user(user:UserCreate):# 请求使用 UserCreate# 即使返回包含 password,response_model 也会自动过滤return{"id":1,"username":user.username,"email":user.email,"password":"***"}13. 验证错误响应
当请求体验证失败时,FastAPI 自动返回 422:
{"detail":[{"type":"missing","loc":["body","name"],"msg":"Field required","input":null},{"type":"greater_than","loc":["body","price"],"msg":"Input should be greater than 0","input":-10}]}自定义验证错误格式:
fromfastapiimportFastAPI,Requestfromfastapi.exceptionsimportRequestValidationErrorfromfastapi.responsesimportJSONResponse app=FastAPI()@app.exception_handler(RequestValidationError)asyncdefvalidation_exception_handler(request:Request,exc:RequestValidationError):errors=[]forerrorinexc.errors():errors.append({"field":".".join(str(loc)forlocinerror["loc"]),"message":error["msg"],})returnJSONResponse(status_code=422,content={"detail":errors},)速查表
| 用法 | 语法 | 说明 |
|---|---|---|
| 基础模型 | class M(BaseModel) | Pydantic 模型自动识别为请求体 |
| 字段验证 | Field(gt=, min_length=, ...) | 数值/字符串/集合约束 |
| 嵌套模型 | address: Address | 模型嵌套,JSON 对象嵌套 |
| 多请求体 | item: Item, user: User | JSON 用参数名作 key |
| 嵌入模式 | Body(embed=True) | 强制 key 包裹 |
| 单字段 | Body(...) | 非模型的单字段请求体 |
| 字段验证器 | @field_validator | 单字段自定义验证 |
| 模型验证器 | @model_validator | 多字段联合验证 |
| 模型继承 | class Create(Base) | 复用字段定义 |
| 请求/响应分离 | response_model=Resp | 输入输出使用不同模型 |
| 特殊类型 | HttpUrl,EmailStr等 | 自动格式验证 |
| 原始请求 | request: Request | 访问原始 body |
总结:FastAPI 请求体的核心是Pydantic 模型 + 自动类型验证。
BaseModel定义结构,Field()添加约束,嵌套模型处理复杂 JSON,field_validator/model_validator处理自定义逻辑,请求/响应模型分离防止敏感数据泄露。Pydantic v2 的验证性能远超手写验证代码,且自动生成 OpenAPI 文档。