13. FastAPI 请求对象Request
Request 提供原始请求上下文
声明 request: Request,FastAPI 会注入本次请求对象。它适合读取请求头、客户端地址、原始正文、中间件写入的上下文;常规业务字段仍建议用明确的参数模型。
python
from json import JSONDecodeError
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
@app.get("/inspect/{item_id}")
async def inspect(item_id: int, request: Request):
return {
"method": request.method,
"path": request.url.path,
"item_id": request.path_params["item_id"],
"tags": request.query_params.getlist("tag"),
"user_agent": request.headers.get("user-agent"),
"Authorization": request.headers.get("Authorization"),
"cookies": request.cookies,
"client_host": request.client.host if request.client else None,
"has_session_cookie": "session" in request.cookies,
}
@app.post("/raw-json")
async def raw_json(request: Request):
try:
data = await request.json()
except (JSONDecodeError, UnicodeDecodeError):
raise HTTPException(400, "正文不是合法 JSON")
return {"received": data}保存为 main.py 并运行 uv run uvicorn main:app --reload。访问 /inspect/12?tag=a&tag=b,观察重复参数通过 getlist 被完整读取;在 /raw-json 发送非法 JSON,应得到主动转换后的 400。
前端访问
bash
GET HTTP://127.0.0.1:8000/inspect/18?tags=user,admin
Content-Type: application/json
Authorization: Bearer 123.456.7890
Cookie: session_id=123456常用入口及代价
| 入口 | 返回内容 | 注意点 |
|---|---|---|
request.headers | 请求头映射 | 不记录令牌和 Cookie 原文 |
request.cookies | Cookie 映射 | 客户端提供,不能直接信任 |
await request.body() | 完整字节串 | 大正文会占内存 |
await request.json() | JSON 解析结果 | 不自动做模型字段验证 |
request.stream() | 异步字节块 | 适合大正文逐块处理 |
request.state | 本次请求附加属性 | 可放 request_id、认证上下文 |
request.app.state | 应用实例资源 | 常放共享 HTTP 客户端 |
使用 async for chunk in request.stream() 消费流后,通常不能再假设能重复调用 .body()、.json() 或 .form()。中间件随意读取正文也可能干扰下游,需要理解缓存与流的行为。
客户端 IP 与反向代理
应用直连时 request.client 是对端;经过反向代理时可能是代理地址。X-Forwarded-For 来自请求头,只有在可信代理边界正确配置后才可用于真实 IP 判断。不能无条件相信外部客户端伪造的转发头。
原始正文常用于 Webhook 签名校验:应对原始字节做签名验证,然后解析 JSON,避免重新序列化改变字节顺序。若需要验证第三方签名,应严格按对应服务协议实现。
练习:添加中间件设置 request.state.request_id,在接口中读取;只返回允许公开的上下文字段,避免把整个请求头原样回显。