在 FastAPI 中,路由(Routing)(官方文档称之为“路径操作” Path Operation)是定义 API 如何接收 HTTP 请求并返回响应的核心机制。
FastAPI 的路由设计极其直观:HTTP 方法装饰器 + 带有类型提示的 Python 函数。
下面我将从基础用法、参数传递到常见陷阱,为你系统梳理 FastAPI 的基本路由。
一、 核心概念:装饰器与函数
在 FastAPI 中,路由由两部分组成:
- 路径操作装饰器:告诉 FastAPI 监听哪个 HTTP 方法(如
GET,POST)和哪个 URL 路径(如"/items")。 - 路径操作函数:当请求匹配到该路径和方法时,实际执行的 Python 函数。
from fastapi import FastAPI
app = FastAPI()
# 1. 装饰器:监听 GET 请求,路径为 "/"
@app.get("/")
# 2. 函数:处理请求并返回数据 (FastAPI 会自动将其转为 JSON)
def read_root():
return {"message": "Hello World"}FastAPI 支持所有标准的 HTTP 方法:@app.get(), @app.post(), @app.put(), @app.delete(), @app.patch(), @app.options(), @app.head(), @app.trace()。
二、 路由的三大参数传递方式
1. 路径参数 (Path Parameters)
用于捕获 URL 路径中的动态部分。直接在函数参数中声明,并必须提供类型提示,FastAPI 会自动进行类型转换和验证。
@app.get("/items/{item_id}")
def get_item(item_id: int):
# 如果访问 /items/abc,FastAPI 会自动返回 422 错误,因为 "abc" 无法转为 int
return {"item_id": item_id, "type": type(item_id).__name__}
# 支持枚举类型限制 (Python 3.8+ 推荐使用 Enum 或 Literal)
from typing import Literal
@app.get("/models/{model_name}")
def get_model(model_name: Literal["alexnet", "resnet", "lenet"]):
return {"model_name": model_name}2. 查询参数 (Query Parameters)
当函数参数不是路径参数的一部分时,FastAPI 会自动将它们解释为查询参数(即 URL 中 ? 后面的 key=value 部分)。
# 访问示例: /items/?skip=0&limit=10
@app.get("/items/")
def list_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
# 可选的查询参数:使用 str | None (或 Optional[str]) 并赋予默认值 None
@app.get("/search/")
def search(q: str | None = None):
if q:
return {"results": f"搜索关键词: {q}"}
return {"results": "没有提供搜索关键词"}3. 路径参数 + 查询参数混合使用
FastAPI 能完美区分它们:在路径中声明的是路径参数,未在路径中声明的是查询参数。
# 访问示例: /users/123/items/?status=active
@app.get("/users/{user_id}/items/")
def get_user_items(user_id: int, status: str | None = None):
return {"user_id": user_id, "status": status}三、 请求体 (Request Body) 基础
虽然严格来说属于“数据验证”范畴,但它是 POST/PUT 路由不可或缺的一部分。当你需要接收复杂的 JSON 数据时,使用 Pydantic 模型作为函数参数。
from pydantic import BaseModel
# 1. 定义数据模型
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
# 2. 在 POST 路由中使用该模型作为参数
@app.post("/items/")
def create_item(item: Item):
# FastAPI 会自动解析请求体中的 JSON,验证类型,并转换为 Item 对象
return {
"item_name": item.name,
"item_price": item.price,
"message": "创建成功"
}四、 ⚠️ 致命陷阱:路由声明顺序
FastAPI 是从上到下匹配路由的。一旦找到匹配的路径,就会停止查找。因此,具体的路由必须放在通用的路由之前。
# ❌ 错误顺序
@app.get("/users/me")
def read_user_me():
return {"user": "the current user"}
@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}
# 如果先定义 /users/me,当访问 /users/me 时,
# FastAPI 会匹配到 /users/{user_id},把 "me" 当作 user_id 返回!# ✅ 正确顺序:将固定路径放在动态路径之前
@app.get("/users/me")
def read_user_me():
return {"user": "the current user"}
@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}五、 进阶:使用 APIRouter 拆分路由 (最佳实践)
当应用变大时,把所有路由写在 main.py 中会导致文件臃肿。FastAPI 提供了 APIRouter,让你可以将路由拆分到不同的文件中,最后再挂载到主应用上。
1. 创建路由模块 routers/users.py:
from fastapi import APIRouter
# 创建一个路由器实例
router = APIRouter()
@router.get("/", tags=["users"])
def get_users():
return [{"name": "Alice"}, {"name": "Bob"}]
@router.get("/{user_id}", tags=["users"])
def get_user(user_id: int):
return {"user_id": user_id}2. 在主应用 main.py 中挂载:
from fastapi import FastAPI
from routers import users # 假设 routers 文件夹下有 __init__.py
app = FastAPI()
# 将 users 路由器的所有路由挂载到 /users 前缀下
app.include_router(users.router, prefix="/users")
@app.get("/")
def root():
return {"message": "主应用"}效果:/users/ 和 /users/{user_id} 现在都可以正常访问,并且在 /docs 中会被自动归类到 “users” 标签下。
总结:FastAPI 路由速查表
| 需求 | 实现方式 | 示例 URL |
|---|---|---|
| 简单返回数据 | @app.get("/path") + return dict | / |
| URL 中的动态变量 | 函数参数名与 {} 内名称一致 + 类型提示 | /items/42 (item_id: int) |
URL ? 后的参数 | 函数参数名不在路径中 + 默认值/类型提示 | /items/?skip=0 (skip: int = 0) |
| 接收 JSON 数据 | 定义 BaseModel + 作为函数参数 | POST /items/ (item: Item) |
| 组织大型项目 | 使用 APIRouter 拆分文件并 include_router | prefix="/api/v1" |
掌握了这些,你就已经能构建出 80% 的常规 RESTful API 了。
你想接下来了解如何对路由进行权限校验(依赖注入),还是如何处理文件上传?随时告诉我!