FastAPI 教程

FastAPI 请求和响应

在 FastAPI 中,请求(Request)响应(Response)的处理是其最核心、也是最优雅的部分。FastAPI 基于 Pydantic 模型和 Python 类型提示,实现了自动的数据解析、验证、序列化和文档生成。

下面我将从请求处理响应控制高级技巧,为你系统梳理 FastAPI 的请求与响应机制。


一、 请求处理 (Handling Requests)

FastAPI 通过函数参数来声明它期望接收的数据。根据参数的定义方式,FastAPI 会自动从不同的位置提取数据。

1. 路径参数 (Path Parameters)

从 URL 路径中提取。

纯文本
plaintext
@app.get("/items/{item_id}")
def get_item(item_id: int):  # item_id 必须与路径中的 {item_id} 同名
    return {"item_id": item_id}

2. 查询参数 (Query Parameters)

从 URL 的 ?key=value 部分提取。任何不在路径中声明的参数都会被视为查询参数。

纯文本
plaintext
# 访问: /items/?skip=0&limit=10
@app.get("/items/")
def list_items(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

3. 请求体 (Request Body) – POST/PUT/PATCH

当需要发送复杂的 JSON 数据时,使用 Pydantic 模型作为参数。FastAPI 会自动读取请求体中的 JSON,验证其结构,并转换为 Pydantic 对象。

纯文本
plaintext
from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str
    price: float
    description: str | None = None
    tags: list[str] = []

@app.post("/items/")
def create_item(item: Item):
    # item 是一个已经过验证的 Pydantic 对象
    print(item.name) 
    return item

4. 混合使用:路径 + 查询 + 请求体

FastAPI 能智能区分它们,你只需要正确声明参数即可。

纯文本
plaintext
@app.put("/items/{item_id}")
def update_item(
    item_id: int,           # 路径参数
    q: str | None = None,   # 查询参数
    item: Item              # 请求体
):
    return {"item_id": item_id, "query": q, "updated_item": item}

5. 其他请求来源

  • Header: 使用 Header 依赖。
    python from fastapi import Header def read_items(user_agent: str | None = Header(None)): return {"User-Agent": user_agent}
  • Cookie: 使用 Cookie 依赖。
    python from fastapi import Cookie def read_items(session_id: str | None = Cookie(None)): return {"session_id": session_id}
  • Form Data: 使用 Form 依赖 (需安装 python-multipart)。
    python from fastapi import Form def login(username: str = Form(), password: str = Form()): return {"username": username}
  • Files: 使用 FileUploadFile
    python from fastapi import File, UploadFile def upload_file(file: UploadFile = File(...)): return {"filename": file.filename}

二、 响应控制 (Handling Responses)

默认情况下,FastAPI 会将你的返回值(字典、列表、Pydantic 模型)自动转换为 JSON 格式,并设置状态码为 200 OK。但你可以通过以下方式精细控制响应。

1. 使用 response_model (推荐 🌟)

这是 FastAPI 最强大的特性之一。它不仅定义了返回数据的结构,还会:

  • 自动序列化:将 Pydantic 模型转为 JSON。
  • 数据过滤:只返回模型中定义的字段(例如隐藏密码)。
  • 生成文档:在 Swagger UI 中显示预期的响应结构。
纯文本
plaintext
class UserOut(BaseModel):
    username: str
    email: str
    # 注意:这里没有定义 password

class UserIn(BaseModel):
    username: str
    email: str
    password: str

@app.post("/users/", response_model=UserOut)
def create_user(user: UserIn):
    # 即使返回了包含 password 的对象,response_model 也会将其过滤掉
    return {"username": user.username, "email": user.email, "password": "secret"}
    # 客户端实际收到: {"username": "...", "email": "..."}

2. 自定义状态码

使用 status_code 参数或 Response 对象。

纯文本
plaintext
from fastapi import status

# 方法 A: 在装饰器中指定 (推荐用于固定状态码)
@app.post("/items/", status_code=status.HTTP_201_CREATED)
def create_item(item: Item):
    return item

# 方法 B: 在函数内部动态指定 (推荐用于条件性状态码)
from fastapi.responses import JSONResponse

@app.delete("/items/{item_id}")
def delete_item(item_id: int):
    if not item_exists(item_id):
        return JSONResponse(status_code=404, content={"detail": "Item not found"})
    return {"message": "Deleted"}

3. 直接返回 Response 对象

如果你需要返回非 JSON 内容(如 HTML、XML、图片),或者需要完全控制 Header,可以直接返回 Response 或其子类。

纯文本
plaintext
from fastapi.responses import PlainTextResponse, HTMLResponse

@app.get("/text", response_class=PlainTextResponse)
def get_text():
    return "Hello World"  # 返回纯文本,而不是 JSON 字符串

@app.get("/html", response_class=HTMLResponse)
def get_html():
    return "<h1>Hello HTML</h1>"

4. 自定义响应头 (Headers)

纯文本
plaintext
from fastapi.responses import JSONResponse

@app.get("/headers")
def get_headers():
    content = {"message": "Hello"}
    headers = {"X-Custom-Header": "MyValue"}
    return JSONResponse(content=content, headers=headers)

三、 常见陷阱与最佳实践

1. 陷阱:忘记安装 python-multipart

如果你尝试使用 FormFile 参数,但没有安装这个库,FastAPI 会报错。

纯文本
plaintext
pip install python-multipart

2. 陷阱:response_modelreturn 类型不一致

虽然 FastAPI 很宽容,但最好确保 return 的数据能够被 response_model 解析。如果返回的数据缺少 response_model 中必需的字段,可能会报错或返回 null

3. 最佳实践:使用 HTTPException 处理错误

不要手动返回 4xx/5xx 的 JSONResponse,而是抛出异常,让 FastAPI 统一处理。

纯文本
plaintext
from fastapi import HTTPException, status

@app.get("/items/{item_id}")
def get_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Item {item_id} not found",
            headers={"X-Error": "There goes my error"}
        )
    return items_db[item_id]

这样做的好处是:Swagger UI 会自动记录这些可能的错误响应,方便前端开发者处理。

4. 最佳实践:大型项目使用 APIRouter

不要在 main.py 中写所有路由。使用 APIRouter 将不同模块的路由拆分到不同文件,并保持 response_model 的一致性。


四、 总结:请求与响应速查表

场景推荐方式示例
获取 URL 路径变量路径参数def func(id: int)
获取 URL 问号后参数查询参数def func(q: str = None)
接收 JSON 数据Pydantic 模型def func(item: Item)
接收表单/文件Form / UploadFiledef func(file: UploadFile)
返回标准 JSON直接返回 dict/模型return {"ok": True}
过滤敏感字段/规范输出response_model@app.get(..., response_model=UserOut)
返回非 JSON (HTML/文本)response_classresponse_class=HTMLResponse
处理业务错误HTTPExceptionraise HTTPException(404, ...)

掌握了这些,你就能够处理绝大多数 Web API 的数据交互需求了。

你想深入了解如何验证更复杂的请求数据(如正则表达式匹配、自定义验证器),还是如何全局处理异常(Global Exception Handlers)?随时告诉我!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注