FastAPI请求(request)
在“FastAPI系列03:FastAPI路由(router)”一节中,我们在全流程上了解了FastAPI的请求响应过程,也初步解析了路由参数,也就是请求参数。本节我们详细讲解FastAPI程序对于不同请求参数的校验和处理。
1、Request入门
在FastAPI中可以通过直接访问ASGI scope和receive channel来获得客户端请求信息:
async def app(scope, receive, send):
assert scope['type'] == 'http'
request = Request(scope, receive)
content = '%s %s' % (request.method, request.url.path)
response = Response(content, media_type='text/plain')
await response(scope, receive, send)
在此基础上,FastAPI还提供了Request
对象,通过对HTTP请求报文的解析以更简便地处理来自客户端的请求信息。我们知道在HTTP协议中HTTP请求报文主要包括:
- 请求行(Request Line)
- 请求头(Headers)
- 空行(表示头部结束)
- 请求体(Body,可选)
如:
POST /login HTTP/1.1
Host: example.com
User-Agent: curl/7.64.1
Accept: */*
Content-Type: application/json
Content-Length: 48
{
"username": "alice",
"password": "123456"
}
在FastAPI,用户可以通过Request
对象获取它们对应的信息集合。
2、客户端请求基本信息
request.client:客户端信息集合
@app.get("/req_client")
async def req_client(request: Request):
# request.client返回一个Address(host: str port: int)命名元组(namedtuple)
#
return {
"host": request.client.host,
"port": request.client.port
}
request.method:HTTP请求方法
@app.get("/req_method")
async def req_method(request: Request):
# request.method 返回HTTP请求方法
return {
"method": request.method
}
request.url:请求URL
@app.get("/req_url")
async def req_url(request: Request):
# request.url返回一个URL Components对象(scheme:str, netloc:str:str, path:str, query:str, fragment:str, username:str|None, password:str|None, hostname:str|None, port:int, is_secure:bool)
# scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
return {
"scheme": request.url.scheme,
"netloc": request.url.netloc,
"path": request.url.path,
"query": request.url.query,
"fragment": request.url.fragment,
"username": request.url.username,
"password": request.url.password,
"hostname": request.url.hostname,
"port": request.url.port,
"is_secure": request.url.is_secure
}
事实上,request还包含了一个url构造方法request.url_for(),用以构造URL对象。
同时,request.url返回的URL Components对象中还包含了对query_params进行操作的几个方法include_query_params,replace_query_params,remove_query_params。但这些操作在当前语境下似乎没有太大意义。
request.base_url:请求的基准url
同样返回一个URL Components对象,在此不做赘述。
request.headers:请求头信息集合
@app.get("/req_headers")
async def req_headers(request: Request):
# request.headers返回一个Headers对象,主要包括了http header原始信息(request.headers.raw)和解析后的键值对(request.headers.items())
return request.headers
request.cookies:浏览器cookie集合
@app.get("/req_cookies")
async def req_cookies(request: Request):
# request.path_params返回一个字典
return request.cookies
3、请求参数
request.path_params:URL路径参数集合
@app.get("/req_path_params/{name}/{hobby}")
async def req_path_params(request: Request, name: str, hobby: str):
# request.path_params返回一个字典
# http://127.0.0.1:8088/req_path_params/jack/music
return {
"path_name": name,
"path_hobby": hobby,
"path_params": request.path_params
}
request.query_params:URL查询参数集合
@app.get("/req_query_params")
async def req_query_params(request: Request):
# request.query_params返回一个QueryParams字典,可以通过get方法获取参数值
# http://127.0.0.1:8088/req_query_params?name=jack&hobby=music
return request.query_params
request.body():原始请求体
@app.post("/req_body")
async def req_body(request: Request):
# request.body() 返回二进制数据
return await request.body()
request.json():把请求体解析为json
@app.post("/req_json")
async def req_json(request: Request,json_data: dict):
# request.json() 返回json,此时可以利用pydantic进行json数据校验并映射到一个model类实例
return await request.json()
request.form():表单信息集合
@app.post("/req_form")
async def req_form(request: Request, name: str = Form(...), hobby: str = Form(...), ):
return {
"form_name": name,
"form_hobby": hobby,
"form_params": await request.form()
}
以下为从request.form()获得客户端上传文件的信息。
@app.post("/req_file")
async def req_file(name: str = Form(...), resume_file: UploadFile = File(...)):
res = {
"name": name,
"resume_file": resume_file,
"resume_file_name": resume_file.filename,
"resume_file_headers": resume_file.headers,
"resume_file_size": resume_file.size,
"resume_file_content_type": resume_file.content_type,
"resume_file_content": await resume_file.read(),
}
return res
4、参数模型与校验
参数模型
在 FastAPI 中,请求参数模型(Request Parameter Models)主要是指通过 Pydantic 来定义并验证传入的请求数据。FastAPI 支持多种参数来源:路径参数、查询参数、请求体、表单数据、Header、Cookie、File 等,而模型通常用于请求体、查询参数和表单数据的结构化表示。
查询参数模型(Query Parameters as Model)
class QueryParams(BaseModel):
q: str | None = None
limit: int = 10
offset: int = 0
def get_query_params(q: QueryParams = Depends()):
return q
@app.get("/items/")
def read_items(params: QueryParams = Depends(get_query_params)):
return {"query": params.q, "limit": params.limit, "offset": params.offset}
请求体参数模型(Request Body)
class User(BaseModel):
username: str
email: str
age: int
@app.post("/users/")
async def create_user(user: User):
return {"msg": f"User {user.username} created", "data": user}
表单数据模型(Form Data)
用于处理前端表单提交数据(如 login 表单),需用 Form():
class LoginForm(BaseModel):
username: str
password: str
@app.post("/login/")
def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}
Form() 不支持直接传 BaseModel,但你可以通过依赖注入封装:
def get_login_form(
username: str = Form(...),
password: str = Form(...)
) -> LoginForm:
return LoginForm(username=username, password=password)
@app.post("/login/")
def login(form_data: LoginForm = Depends(get_login_form)):
return form_data
参数模型嵌套
在 FastAPI 中,可以通过 Pydantic 的嵌套模型(Nested Models) 来处理结构复杂的请求参数,比如嵌套的 JSON 结构。FastAPI 会自动解析并校验这些嵌套模型,非常适合处理复杂业务逻辑。
嵌套模型基本用法
class Address(BaseModel):
city: str
street: str
zipcode: str
class User(BaseModel):
username: str
email: str
address: Address # 嵌套模型
@app.post("/users/")
def create_user(user: User):
return {
"username": user.username,
"city": user.address.city,
"zipcode": user.address.zipcode
}
嵌套列表模型
class Item(BaseModel):
name: str
price: float
class Order(BaseModel):
user_id: int
items: list[Item]
@app.post("/orders/")
def create_order(order: Order):
total = sum(item.price for item in order.items)
return {"user_id": order.user_id, "total": total}
参数校验
在 FastAPI 中,请求参数的校验(Validation)是通过 Pydantic 和 FastAPI 自身的参数声明功能自动实现的。无论是请求体、查询参数、路径参数,还是表单、Header、Cookie、文件上传等,FastAPI 都能自动进行类型验证、范围限制和格式校验。
请求体校验(基于 Pydantic 模型)
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str = Field(..., min_length=3, max_length=50)
price: float = Field(..., gt=0)
description: str | None = Field(None, max_length=300)
说明:
• Field(…) 表示必填
• min_length, max_length: 字符串长度限制
• gt, lt, ge, le: 数值范围限制
查询参数/路径参数校验
使用 Query, Path, Header, Cookie 等函数进行精细校验
from fastapi import FastAPI, Query, Path
@app.get("/items/{item_id}")
def read_item(
item_id: int = Path(..., ge=1),
q: str = Query(default=None, min_length=3, max_length=50)
):
return {"item_id": item_id, "q": q}
说明:
• Path(…): 用于路径参数
• Query(…): 用于查询参数
• 参数校验器也支持 regex, example, description, alias 等
表单/文件参数校验
from fastapi import Form, File, UploadFile
@app.post("/submit/")
def submit(
username: str = Form(..., min_length=3),
file: UploadFile = File(...)
):
return {"filename": file.filename}
自定义校验逻辑
可以使用 Pydantic 的 @validator 装饰器来自定义逻辑:
from pydantic import validator
class User(BaseModel):
username: str
email: str
@validator("email")
def validate_email(cls, v):
if "@example.com" not in v:
raise ValueError("Must be an @example.com email")
return v
额外的数据类型
FastAPI 在处理请求参数时,除了支持常见的基础类型(str, int, float, bool 等),还支持许多 额外的数据类型,主要依赖 Pydantic 提供的类型支持。这些额外类型可以增强校验能力、提高可读性和开发效率。
额外数据类型示例
from pydantic import BaseModel, constr, conint
from enum import Enum
from typing import Literal
class Status(str, Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
class Product(BaseModel):
id: UUID
name: constr(min_length=3, max_length=50)
quantity: conint(gt=0, le=1000)
status: Status
type: Literal["food", "drink"]
created_at: datetime
FastAPI 支持的额外数据类型一览
类型 | 描述 |
---|---|
EmailStr | 校验邮箱格式 |
AnyUrI, HttpUrl | 校验 URL 格式(是否 http/https、合法域名等) |
constr | 限制字符串长度、正则匹配等 |
conint, confloat | 限制整数或浮点数的范围 |
UUID | 校验 UUID 格式 |
datetime, date, time | 时间日期相关字段类型(自动格式校验 ISO格式) |
Decimal | 精确小数类型,适合用于金额计算 |
IPvAnyAddress, IPv4Address | IP 地址类型,支持 IPV4/IPv6 的校验 |
SecretStr, SecretBytes | 安全字符串/字节类型,用于隐藏字段(如密码) |
Literal | 只允许固定取值,类似枚举 |
Enum | Python 枚举类 |
Json | 表示字段为有效JSON 格式 |
PaymentCardNumber(需插件) | 校验银行卡号(通过 pydantic-extra-types) |
5、请求体
多个参数
在 FastAPI 中,请求体可以接收多个参数(而不是只接收一个 Pydantic 模型)。
多个 Pydantic 模型参数 + Body(…)
from fastapi import Body
class Item(BaseModel):
name: str
price: float
class User(BaseModel):
username: str
email: str
@app.post("/items/")
def create_item(item: Item = Body(...), user: User = Body(...)):
return {"item": item, "user": user}
多个基础类型参数(也使用 Body(…))
@app.post("/params/")
def receive_data(name: str = Body(...), age: int = Body(...)):
return {"name": name, "age": age}
字段
在 FastAPI 中,请求体字段主要用来描述请求体的结构、添加默认值、校验规则、描述信息、示例等。
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(..., title="商品名称", min_length=1, max_length=100)
description: str | None = Field(None, title="描述", max_length=300)
price: float = Field(..., gt=0, description="商品价格,必须大于0")
tax: float | None = Field(default=0.0, ge=0)
@app.post("/items/")
def create_item(item: Item):
return item
二进制数据
在 FastAPI 中可以通过以下三种形式处理请求体中的二进制数据:
• bytes 类型(读取为原始二进制)
• UploadFile 类型(更高效处理大文件,支持文件对象操作)
• File() 标记为从请求体中读取文件数据
使用 bytes 处理二进制数据(直接读取内容)
from fastapi import FastAPI, Body
app = FastAPI()
@app.post("/upload-bytes/")
async def upload_bytes(file_data: bytes = Body(...)):
size = len(file_data)
return {"size": size}
使用 UploadFile 上传二进制文件(推荐)
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/upload-file/")
async def upload_file(file: UploadFile = File(...)):
content = await file.read() # 获取二进制内容
return {
"filename": file.filename,
"content_type": file.content_type,
"size": len(content)
}
多个文件(二进制)上传
from typing import List
@app.post("/upload-multiple/")
async def upload_multiple(files: List[UploadFile] = File(...)):
return [{"filename": f.filename, "size": len(await f.read())} for f in files]
6、共享Request中的数据
在middleware中修改Request请求信息
在FastAPI中Request请求信息将依次经过middleware、router等组件进行处理,但request所包含的信息修改后在scope中是不可改变的,这意味着上一环节处理后不能直接通过修改相应的值从而来传递值。
因此以下代码是无法成立的:
@app.middleware("http")
async def req_scope_middleware(request: Request, call_next):
request.headers['new-header'] = 'new-header-value'
return await call_next(request)
@app.get("/req_scope")
async def req_scope(request: Request):
return request.headers
那如果需要在middleware中修改Request请求信息以方便后续程序使用,可以通过以下方式来完成:
@app.middleware("http")
async def req_scope_middleware(request: Request, call_next):
headers = dict(request.scope['headers'])
headers[b'new-header'] = 'new-header-value'.encode('utf-8')
request.scope['headers'] = [(k, v) for k, v in headers.items()]
return await call_next(request)
@app.get("/req_scope")
async def req_scope(request: Request):
return request.headers
request.state:在一个请求的生命周期中存储和共享数据
对于上面的应用场景中,要在request传递自定义的信息更方便的方式是使用request.state。request.state在每个请求开始时都会被初始化为一个空对象,你可以将任何属性和值添加到这个对象中。这对于将数据从依赖项传递到路由处理程序或者在同一个请求的不同路由处理程序之间共享数据非常有用。
@app.middleware("http")
async def req_state_middleware(request: Request, call_next):
request.state.name = "tom"
request.state.hobby = "music"
return await call_next(request)
@app.get("/req_state")
async def req_state(request: Request):
return {
"state_name": request.state.name,
"state_hobby": request.state.hobby
}
request.session:用以请求之间共享的会话信息
FastAPI在Request对象中还提供了session对象用以共享请求之间的会话信息,可用于跟踪用户状态、存储用户特定信息(如身份验证令牌或用户偏好)等场景。request.session基于SessionMiddleware,要使用request.session需要在程序中加载SessionMiddleware。session为dict类型,可以按dict的操作方式进行操作。
# 引入SessionMiddleware
from starlette.middleware.sessions import SessionMiddleware
app.add_middleware(SessionMiddleware, secret_key="my_secret_key")
@app.get("/req_session")
async def req_session(request: Request):
request.session['name'] = "tom"
request.session['hobby'] = "music"
return request.session