FastAPI 教程

FastAPI 基本路由

在 FastAPI 中,路由(Routing)(官方文档称之为“路径操作” Path Operation)是定义 API 如何接收 HTTP 请求并返回响应的核心机制。

FastAPI 的路由设计极其直观:HTTP 方法装饰器 + 带有类型提示的 Python 函数

下面我将从基础用法参数传递常见陷阱,为你系统梳理 FastAPI 的基本路由。


一、 核心概念:装饰器与函数

在 FastAPI 中,路由由两部分组成:

  1. 路径操作装饰器:告诉 FastAPI 监听哪个 HTTP 方法(如 GET, POST)和哪个 URL 路径(如 "/items")。
  2. 路径操作函数:当请求匹配到该路径和方法时,实际执行的 Python 函数。
纯文本
plaintext
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 会自动进行类型转换和验证。

纯文本
plaintext
@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 部分)。

纯文本
plaintext
# 访问示例: /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 能完美区分它们:在路径中声明的是路径参数,未在路径中声明的是查询参数。

纯文本
plaintext
# 访问示例: /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 模型作为函数参数。

纯文本
plaintext
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 是从上到下匹配路由的。一旦找到匹配的路径,就会停止查找。因此,具体的路由必须放在通用的路由之前

纯文本
plaintext
# ❌ 错误顺序
@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 返回!
纯文本
plaintext
# ✅ 正确顺序:将固定路径放在动态路径之前
@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

纯文本
plaintext
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 中挂载

纯文本
plaintext
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_routerprefix="/api/v1"

掌握了这些,你就已经能构建出 80% 的常规 RESTful API 了。

你想接下来了解如何对路由进行权限校验(依赖注入),还是如何处理文件上传?随时告诉我!

发表回复

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