引言
在FastAPI中,HTTP请求的参数获取和验证是非常重要的功能。本文将详细介绍FastAPI中几种常见的请求参数形式,以及它们的获取方式和验证机制。
请求参数的常见形式
一个典型的RESTful API中,请求参数主要有以下几种形式:
- 查询字符串参数-Query Parameters:URL中
?
号后面的查询参数,例如?query=test
- 路径参数-Path Parameters:URL路径中的参数,例如
/path/{param}
- 请求体-Request Body:请求体中的JSON格式数据
- 请求头-Headers:请求头中的参数
- 文件上传-Files
- 其他的请求信息-Request
下面我将依次对这几种参数的处理进行说明。
请求参数处理Demo
python
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 请求参数案例 }
# @Date: 2023/11/06 11:15
import uvicorn
from fastapi import FastAPI, Header, UploadFile
from pydantic import BaseModel, Field
app = FastAPI(summary="请求参数案例")
@app.get("/query_params", summary="查询字符串参数Demo")
def with_query_params(name: str, age: int):
return {"name": name, "age": age}
@app.get("/user/{user_id}/path_params", summary="路径参数Demo")
def with_path_params(user_id: int):
return {"user_id": user_id}
class UserLoginIn(BaseModel):
account: str = Field(..., description="账号")
password: str = Field(..., description="密码")
@app.post("/user/login/json_params", summary="json参数demo")
def with_json_params(req_model: UserLoginIn):
return req_model.model_dump()
@app.get("/user/detail/header_params", summary="请求头参数demo")
def with_header_params(
token: str = Header(description="访问token"),
user_agent: str = Header(description="用户代理")
):
return {"token": token, "User-Agent": user_agent}
@app.post("/file_upload/file_params", summary="文件参数demo")
async def with_file_params(file: UploadFile):
return {"filename": file.filename, "file_size": file.size}
@app.get("/users/{user_id}/path_query_params", summary="路径参数+查询字符串参数demo")
def with_path_query_params(user_id: int, age: int = None):
return {"user_id": user_id, "age": age}
def main():
uvicorn.run(app)
if __name__ == '__main__':
main()


请求处理注意点
这里需要注意,如下几点
-
请求头参数需要从 fastapi 导入 Header 来标识是请求头的参数
-
http固定的请求头参数,fastapi处理是要用小写,然后中划线,改成下划线,如下
- Content-Type => content_type
- User-Agent => user_agent
-
-
处理文件参数有点特殊,也是需要从 fastapi 导入 UploadFile 进行处理,这个类帮忙封装了一些文件信息
- 然后还需要下载一个 python-multipart 依赖以支持多媒体传输
-
请求体使用json格式交互用了pydantic的BaseModel 可以写参数的描述信息,所以在API文档中有中文的描述,而路径、查询字符串参数没有。需要使用fastapi的Path、Query对象才有。
- 如果希望get请求的路径、查询字符串参数也可以像body一样使用 pydantic的BaseModel维护,需要借助Depends、 Path、Query对象,但会出现description信息不会显示在API文档中,需要解决则还要再BaseModel类中使用Field与Query等对象嵌套来解决。
针对第3点,我写个Demo展示下如何处理
python
class UserQueryIn(BaseModel):
user_id: int = Path(description="用户ID")
name: Optional[str] = Query(default=None, description="姓名")
age: Optional[int] = Query(default=None, description="年龄")
@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn):
# 业务逻辑处理
# logic_func(req_model)
return req_model.model_dump()

如果直接用BaseModel 的话 fastapi 会认为是body参数,这明显不合理是错误,因此需要通过 fastapi的Depends函数来解决。
python
class UserQueryIn(BaseModel):
user_id: int = Path(description="用户ID")
name: Optional[str] = Query(default=None, description="姓名")
age: Optional[int] = Query(default=None, description="年龄")
@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn = Depends(UserQueryIn)):
# 业务逻辑处理
# logic_func(req_model)
return req_model.model_dump()

但会出现description信息不展示,如果需要把API文档导入到APIFOX中的话没有中文描述还是不太好,因此还可以借助在BaseModel中让Field与Query等对象嵌套来解决。
python
class UserQueryIn(BaseModel):
user_id: int = Field(Path(description="用户ID"))
name: Optional[str] = Field(Query(default=None, description="姓名"))
age: Optional[int] = Field(Query(default=None, description="年龄"))
@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn = Depends(UserQueryIn)):
# 业务逻辑处理
# logic_func(req_model)
return req_model.model_dump()

为了统一使用pydantic的BaseModel类处理请求参数,还是有点小坑,感觉get请求不太好使用BaseModel,希望FastAPI后续能优化下。
为什么要统一使用BaseModel呢?看看如下例子
python
@app.get("/users/{user_id}/path_query_params", summary="路径参数+查询字符串参数demo")
def with_path_query_params(
user_id: int = Path(description="用户ID"),
age: int = Query(default=None, description="年龄查询")
):
# 业务逻辑处理
# logic_func(user_id=user_id, age=age)
return {"user_id": user_id, "age": age}
class UserQueryIn(BaseModel):
user_id: int = Field(Path(gt=0, description="用户ID"))
name: Optional[str] = Field(Query(default=None, min_length=1, description="姓名"))
age: Optional[int] = Field(Query(default=None, gt=0, description="年龄"))
@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn = Depends(UserQueryIn)):
# 业务逻辑处理
# logic_func(req_model)
return req_model.model_dump()
如果查询参数需要改动变化、只需要在BaseModel类中维护就好,后面还可以添加校验器,整体的路由函数更简洁不会一堆的参数,传递给逻辑层处理只要透传一个请求模型 req_model 就可以了,可以说更统一规范、更好维护。麻烦就是要在定义的时候使用Depends与Query等。
请求对象
基本请求信息都可以声明参数获取,但一些额外的请求信息,例如请求的IP、Method、URL等则需要通过Request对象进行获取,具体使用如下:
python
from fastapi import FastAPI, Request
@app.get("/request_obj", summary="请求对象的demo")
def req_obj_demo(req: Request):
print("req client ip", req.client.host)
print("req method", req.method)
print("req base_url", req.base_url)
print("req url", req.url)
print("Request", req)
return {
"client_ip": req.client.host,
"method": req.method,
"base_url": req.base_url,
"url": req.url,
}
Ret
csharp
req client ip 127.0.0.1
req method GET
req base_url http://127.0.0.1:8000/
req url http://127.0.0.1:8000/request_obj
Request <starlette.requests.Request object at 0x104bf8af0>
这个Request对象,再每个路由函数中都存在只要声明的参数类型是FastAPI的Request即可,就可以从中获取请求参数、以及一些其他的请求的信息。这个Request对象一般在fastapi的中间件中使用多,后续再介绍。
总结
通过上面的示例可以看出,FastAPI通过声明参数、Depends注入、Pydantic模型验证等方式,提供了非常便捷的请求参数处理机制。各种参数形式也都得到了很好的支持。使用pydantic的BaseModel传递组织参数,相比其他框架使用字典来组织,代码更清晰、更好维护。
源代码
Github:github.com/HuiDBK/Fast...