目录

FastAPI 及其路由使用详解

1. FastAPI 到底是什么

2. 为什么它在 Python 后端里这么常见

3. 先看一个最小 FastAPI 程序

4. 什么叫“路由”以及 FastAPI 里的“path operation”

5. 常见路由装饰器怎么用

6. 路由函数参数的真正魔法:FastAPI 会按“位置来源”自动识别参数

6.1 路径参数 Path Parameter

6.2 查询参数 Query Parameter

6.3 请求体 Request Body

7. 更精细地声明参数来源:Query()、Path()、Body()、Header()、Cookie()、Form()、File()

7.1 Query():给查询参数加约束

7.2 Path():给路径参数加校验

7.3 Body():显式声明请求体

7.4 Header():读取请求头

7.5 Form() 和 File():处理表单和文件上传

8. FastAPI 路由函数的返回值与 response_model

9. 路由配置:status_code、tags、summary、description

10. async def 和 def 该怎么选

11. 依赖注入 Depends():FastAPI 路由的第二根脊梁

12. 如果依赖只想执行,不关心返回值怎么办

方式 1:写成函数参数依赖

方式 2:写到装饰器 dependencies 里

13. APIRouter:项目一变大,别再把所有路由塞进一个文件

13.1 基本示例

14. APIRouter 为什么特别适合大项目

routers/users.py

routers/items.py

main.py

15. 路由级、Router 级、应用级依赖怎么选

路由级依赖

Router 级依赖

应用级依赖

16. 路由里如何注入配置对象

17. 自动文档为什么是 FastAPI 路由体系的一部分

18. 一个更完整的路由示例

19. 初学 FastAPI 路由最容易踩的坑

坑 1:以为所有参数都是 body

坑 2:返回什么都行,不写 response_model

坑 3:项目一大还把所有路由写在 main.py

坑 4:明明是公共逻辑,却每个接口都手写一遍

20. 你可以怎么记住 FastAPI 路由的设计哲学

一条 FastAPI 路由,本质上是四层东西叠在一起


FastAPI 及其路由使用详解

1. FastAPI 到底是什么

你可以把 FastAPI 理解成一句话:

用 Python 的类型标注,直接声明 HTTP API 的输入、输出、校验和文档。

这玩意儿最妙的地方不只是“快”,而是它把这些原本分散的事情揉成了一套统一机制:

  • 路由定义

  • 参数提取

  • 数据校验

  • 响应序列化

  • OpenAPI 文档生成

  • Swagger UI / ReDoc 交互文档

官方文档明确说明,FastAPI 是一个基于标准 Python type hints 的 API 框架,并且会自动生成 OpenAPI schema,以及默认提供 /docs/redoc 两套文档界面。(FastAPI)


2. 为什么它在 Python 后端里这么常见

因为它特别适合“接口层”和“数据边界层”。

传统 Web 框架里,经常要自己做这些事:

  • 手动从 URL 里取参数

  • 手动从 query string 里取值

  • 手动解析 JSON

  • 手动判断缺字段、类型不对

  • 手动写接口文档

FastAPI 的思路是:
你只管在函数签名里声明你要什么,FastAPI 负责把请求拆开、校验、转换并注入给你。 这也是它和 Flask 那种“先拿 request 再自己掏参数”的风格最大的差别。官方文档把这种模式贯穿到了路径参数、查询参数、请求体、Header、Cookie、Form、File 这些输入来源上。(FastAPI)


3. 先看一个最小 FastAPI 程序

官方教程当前推荐先安装:

pip install "fastapi[standard]"

然后可以用开发命令运行应用,比如:

fastapi dev main.py

这来自官方的安装和 First Steps 教程。(FastAPI)

最小示例:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

这段代码里有四个核心角色:

  1. FastAPI():创建应用对象

  2. @app.get("/"):注册一个 GET 路由

  3. root():这个路由对应的处理函数

  4. return {...}:返回响应内容

官方 First Steps 文档把这套流程叫作 path operation:路径(path)加操作(operation,比如 GET / POST)一起构成一个接口定义。(FastAPI)


4. 什么叫“路由”以及 FastAPI 里的“path operation”

在 FastAPI 里,路由通常就指你写的这一类接口:

@app.get("/items")
@app.post("/users")
@app.put("/orders/{order_id}")
@app.delete("/files/{file_id}")

它本质上由两部分组成:

  • Path:比如 /items/{item_id}

  • HTTP 方法:GET、POST、PUT、DELETE 等

官方文档把它叫作 path operation,这是 OpenAPI 术语里也很常见的概念。你可以把它简单理解成:一个 URL + 一个 HTTP 动作 = 一个接口路由。(FastAPI)


5. 常见路由装饰器怎么用

最常用的有这些:

@app.get("/items")
@app.post("/items")
@app.put("/items/{item_id}")
@app.delete("/items/{item_id}")
@app.patch("/items/{item_id}")

这些装饰器就是在告诉 FastAPI:

  • 访问什么路径

  • 用什么 HTTP 方法

  • 调哪个 Python 函数处理

response_modelstatus_codetagssummarydescriptiondependencies 等也都可以直接写在这些装饰器参数里。官方文档明确说明,response_model 可以用于任意 path operation 装饰器,而 path operation configuration 里也支持状态码、标签、摘要等配置。(FastAPI)

例如:

from fastapi import FastAPI, status

app = FastAPI()

@app.post(
    "/items",
    status_code=status.HTTP_201_CREATED,
    tags=["items"],
    summary="创建商品",
    description="创建一个新的商品记录"
)
async def create_item():
    return {"ok": True}

6. 路由函数参数的真正魔法:FastAPI 会按“位置来源”自动识别参数

FastAPI 最强的地方之一,就是直接从函数签名判断参数来自哪里

6.1 路径参数 Path Parameter

看这个:

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

因为路径里写了 {item_id},函数里也有 item_id,FastAPI 就知道这是路径参数。而且你写了 int,它就会自动转换和校验。官方路径参数文档明确说明:路径参数可以直接通过标准类型注解声明,并进行数据转换与校验。(FastAPI)

如果传入不是整数,比如 /items/abc,它会自动报验证错误,而不是傻乎乎继续跑。这就是类型驱动接口的威力。


6.2 查询参数 Query Parameter

看这个:

@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

这里 skiplimit 没有出现在路径里,所以 FastAPI 会把它们识别成查询参数,也就是:

/items/?skip=0&limit=10

官方查询参数文档明确说明:路径参数和查询参数可以同时声明,FastAPI 会根据名字和路径模板自动区分它们;而对于非路径参数,有默认值就不是必填,没有默认值就是必填。(FastAPI)

例如:

@app.get("/search/")
async def search(q: str):
    return {"q": q}

这里 q 没有默认值,所以它是必填 query 参数


6.3 请求体 Request Body

当你把一个 Pydantic 模型 写进函数参数里,FastAPI 通常就会把它识别成请求体。

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    description: str | None = None

@app.post("/items/")
async def create_item(item: Item):
    return item

这里客户端需要发 JSON body,FastAPI 会:

  • 解析请求体

  • Item 模型校验

  • 自动生成文档 schema

官方请求体文档明确把 request body 定义为客户端发送给 API 的数据体,并演示了把 Pydantic 模型作为 body 输入的标准写法。(FastAPI)


7. 更精细地声明参数来源:Query()Path()Body()Header()Cookie()Form()File()

虽然 FastAPI 能自动猜很多情况,但工程里你常常需要更明确的控制。这时就该用这些参数工具。官方参数参考文档明确列出了它们:Query()Path()Body()Cookie()Header()Form()File()。(FastAPI)

7.1 Query():给查询参数加约束

from typing import Annotated
from fastapi import Query

@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(min_length=2, max_length=20)] = None
):
    return {"q": q}

这比只写 q: str | None = None 更强,因为你还能顺手加:

  • 最小长度

  • 最大长度

  • 正则

  • 描述

  • 示例

  • alias

官方字符串查询参数验证文档说明,FastAPI 允许你给参数增加额外验证和元信息。(FastAPI)


7.2 Path():给路径参数加校验

from typing import Annotated
from fastapi import Path

@app.get("/items/{item_id}")
async def read_item(
    item_id: Annotated[int, Path(gt=0, description="商品 ID,必须大于 0")]
):
    return {"item_id": item_id}

官方文档说明,Path()Query() 一样,也支持数值校验和元数据。(FastAPI)


7.3 Body():显式声明请求体

from typing import Annotated
from fastapi import Body

@app.post("/echo/")
async def echo(
    message: Annotated[str, Body(embed=True)]
):
    return {"message": message}

这在你不想用完整 Pydantic 模型,只想收一个简单 body 值时很好用。


7.4 Header():读取请求头

from typing import Annotated
from fastapi import Header

@app.get("/headers/")
async def read_headers(
    user_agent: Annotated[str | None, Header()] = None
):
    return {"user_agent": user_agent}

官方 Header 参数文档明确说明,Header 参数的声明方式和 Query、Path、Cookie 类似。(FastAPI)


7.5 Form()File():处理表单和文件上传

from typing import Annotated
from fastapi import File, Form, UploadFile

@app.post("/upload/")
async def upload_file(
    username: Annotated[str, Form()],
    file: UploadFile = File(...)
):
    return {"username": username, "filename": file.filename}

官方文档说明,表单与文件可以一起声明;要接收上传文件或表单数据,需要安装 python-multipart。另外,Form 必须显式写出来,否则 FastAPI 会把对应参数当成 query 参数或 JSON body 参数。(FastAPI)


8. FastAPI 路由函数的返回值与 response_model

很多新手只关注“怎么收参数”,但路由真正稳不稳,输出模型同样关键。

官方文档明确说明,FastAPI 可以通过函数返回类型注解,或通过装饰器上的 response_model,来:

  • 验证返回数据

  • 生成响应 JSON Schema

  • 在自动文档里展示响应结构

  • 按声明类型过滤输出字段(FastAPI)

最推荐的工程写法之一是:

from pydantic import BaseModel

class ItemOut(BaseModel):
    id: int
    name: str
    price: float

@app.get("/items/{item_id}", response_model=ItemOut)
async def read_item(item_id: int):
    data = {
        "id": item_id,
        "name": "Keyboard",
        "price": 199.0,
        "internal_cost": 120.0,
    }
    return data

这里即使你返回了 internal_cost,FastAPI 也会按 response_model=ItemOut 过滤掉它。这一点特别重要,像守门员一样把不该暴露的字段踢出去。官方文档明确写到:response_model 会用于文档、验证,以及把输出转换和过滤成声明的类型;如果同时写了返回类型和 response_model,后者优先。(FastAPI)


9. 路由配置:status_codetagssummarydescription

一个成熟接口不只是“能跑”,还要有好文档

FastAPI 的 path operation 支持很多 OpenAPI 元信息配置,包括:

  • status_code

  • tags

  • summary

  • description

  • response_description

  • dependencies

这些信息会进入 OpenAPI schema,并显示在 /docs 里。官方文档对 path operation configuration 和 advanced configuration 都有明确说明。(FastAPI)

示例:

@app.post(
    "/users/",
    status_code=201,
    tags=["users"],
    summary="创建用户",
    description="创建一个新的用户账号,用户名必须唯一"
)
async def create_user():
    return {"ok": True}

这样 Swagger UI 里接口就不会像没梳头一样乱糟糟。


10. async defdef 该怎么选

这是 FastAPI 里一个很常见的实际问题。官方 async 文档给出的建议很清楚:

  • 如果你调用的是需要 await 的异步库,就用 async def

  • 如果你使用的是不支持 await 的阻塞库,可以用普通 def

  • 你可以在同一个应用里混用 defasync def

  • FastAPI 会正确处理它们(FastAPI)

例如:

@app.get("/async-items/")
async def read_async_items():
    # await 异步数据库/HTTP客户端
    return {"mode": "async"}

@app.get("/sync-items/")
def read_sync_items():
    # 调同步库
    return {"mode": "sync"}

官方还说明:普通 def 的 path operation 会被放到外部线程池里执行,以避免阻塞服务器。(FastAPI)

工程上你可以这么记:

  • 异步库 → async def

  • 同步阻塞库 → def

  • 不确定时,先看你调用的库是否要求 await


11. 依赖注入 Depends():FastAPI 路由的第二根脊梁

如果说“参数自动提取”是 FastAPI 的第一根脊梁,那 依赖注入 就是第二根。

官方文档把 dependency injection 定义为:路由函数声明自己需要什么,FastAPI 负责准备并注入这些依赖。(FastAPI)

例如:

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()

def get_current_user():
    return {"id": 1, "name": "Alice"}

@app.get("/me")
async def read_me(
    user: Annotated[dict, Depends(get_current_user)]
):
    return user

这意味着:

  • 路由本身不负责“怎么拿当前用户”

  • 这个逻辑被抽到了依赖函数里

  • 路由只声明“我需要它”

这非常适合做:

  • 登录态校验

  • 数据库会话注入

  • 配置对象注入

  • 权限检查

  • 公共分页参数

  • 限流或审计逻辑


12. 如果依赖只想执行,不关心返回值怎么办

这也是路由里很常见的情况。比如你只想在访问某个接口前检查 token,但不需要把 token 值传进函数。

官方文档提供了两种方式:

方式 1:写成函数参数依赖

from typing import Annotated
from fastapi import Depends

def verify_token():
    # 校验逻辑
    return True

@app.get("/secure")
async def secure_api(
    _: Annotated[bool, Depends(verify_token)]
):
    return {"ok": True}

方式 2:写到装饰器 dependencies

from fastapi import Depends

@app.get("/secure", dependencies=[Depends(verify_token)])
async def secure_api():
    return {"ok": True}

官方文档明确说明:当你不需要依赖返回值,只需要它被执行时,可以把它放在 path operation decorator 的 dependencies 列表里。(FastAPI)


13. APIRouter:项目一变大,别再把所有路由塞进一个文件

这几乎是 FastAPI 工程化里最重要的路由组织工具。

官方 APIRouter 文档明确说明:APIRouter 用于把 path operations 分组,例如为了把应用拆成多个文件;它最终会被包含进 FastAPI 应用或另一个 APIRouter。同时它还支持 prefixtagsdependencies 等统一配置。(FastAPI)

13.1 基本示例

from fastapi import APIRouter

router = APIRouter(prefix="/users", tags=["users"])

@router.get("/")
async def list_users():
    return [{"id": 1, "name": "Alice"}]

@router.get("/{user_id}")
async def get_user(user_id: int):
    return {"id": user_id, "name": "Alice"}

然后在主应用里注册:

from fastapi import FastAPI
from .routers import users

app = FastAPI()
app.include_router(users.router)

这会得到:

  • GET /users/

  • GET /users/{user_id}


14. APIRouter 为什么特别适合大项目

因为它能把“模块边界”切清楚。

例如一个稍微正常点的项目,通常会长这样:

app/
├── main.py
├── routers/
│   ├── users.py
│   ├── items.py
│   └── auth.py
├── schemas/
│   ├── user.py
│   └── item.py
├── dependencies.py
└── core/
    └── config.py

对应示意代码:

routers/users.py

from fastapi import APIRouter

router = APIRouter(prefix="/users", tags=["users"])

@router.get("/")
async def list_users():
    return [{"id": 1, "name": "Alice"}]

routers/items.py

from fastapi import APIRouter

router = APIRouter(prefix="/items", tags=["items"])

@router.get("/")
async def list_items():
    return [{"id": 101, "name": "Keyboard"}]

main.py

from fastapi import FastAPI
from app.routers import users, items

app = FastAPI(title="Demo API")

app.include_router(users.router)
app.include_router(items.router)

官方 Bigger Applications 文档就是围绕这种多文件组织方式展开的,并且建议把多个地方复用的依赖提取到单独模块里。(FastAPI)


15. 路由级、Router 级、应用级依赖怎么选

这是工程里非常实用的一层思维。

路由级依赖

只对单个接口生效。

@app.get("/private", dependencies=[Depends(verify_token)])
async def private_api():
    return {"ok": True}

Router 级依赖

对某一组接口统一生效。

router = APIRouter(
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(verify_token)]
)

应用级依赖

全局生效。

APIRouter 官方参考中明确列出了 dependencies 参数既可以用于 router,也可用于 path operation;它们都会被应用到对应范围内的路由上。(FastAPI)

工程上可以这么选:

  • 单接口特殊校验 → 路由级

  • 同一模块共享认证 → Router 级

  • 全局统一审计/追踪 → 应用级


16. 路由里如何注入配置对象

这和你前面在学的 Pydantic Settings 很搭。

FastAPI 官方 settings 文档推荐:把配置读取封装成依赖,并用 @lru_cache 缓存,这样不会每次请求都重新读取 .env。(FastAPI)

示意:

from functools import lru_cache
from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()

class Settings:
    app_name = "demo"

@lru_cache
def get_settings():
    return Settings()

@app.get("/info")
async def read_info(settings: Annotated[Settings, Depends(get_settings)]):
    return {"app_name": settings.app_name}

这个模式很稳,测试时也好替换。


17. 自动文档为什么是 FastAPI 路由体系的一部分

很多人以为 /docs 只是附赠玩具,其实它和路由设计是同一套系统长出来的。

FastAPI 会根据你定义的:

  • 路由路径

  • HTTP 方法

  • 参数来源和类型

  • 请求体模型

  • 响应模型

  • tags / summary / description

  • status_code / responses

自动生成 OpenAPI schema,并提供 Swagger UI 和 ReDoc。官方文档明确说明:OpenAPI schema 是自动生成的,Swagger UI 默认在 /docs,ReDoc 默认在 /redoc。(FastAPI)

所以你每写一个路由,其实不是只写了一段处理逻辑,而是在同时写:

  • 运行时行为

  • 输入契约

  • 输出契约

  • 文档契约

这就是 FastAPI 让人上瘾的地方,挺像买一送三,但不是商场打折,是类型系统在干活。


18. 一个更完整的路由示例

下面给你一个比较像真实项目的例子,把你前面学的 Pydantic 和 FastAPI 路由串起来:

from functools import lru_cache
from typing import Annotated

from fastapi import APIRouter, Depends, FastAPI, Header, HTTPException, Path, Query, status
from pydantic import BaseModel, Field

app = FastAPI(title="Agent Backend Demo")


class Settings:
    app_name: str = "agent-backend"


@lru_cache
def get_settings():
    return Settings()


async def verify_token(x_token: Annotated[str | None, Header()] = None):
    if x_token != "secret-token":
        raise HTTPException(status_code=401, detail="invalid token")


class ItemCreate(BaseModel):
    name: str = Field(min_length=2, max_length=50)
    price: float = Field(gt=0)


class ItemOut(BaseModel):
    id: int
    name: str
    price: float


router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(verify_token)]
)


@router.get("/", response_model=list[ItemOut], summary="获取商品列表")
async def list_items(
    skip: Annotated[int, Query(ge=0)] = 0,
    limit: Annotated[int, Query(ge=1, le=100)] = 10,
):
    data = [
        {"id": 1, "name": "Keyboard", "price": 199.0},
        {"id": 2, "name": "Mouse", "price": 99.0},
    ]
    return data[skip: skip + limit]


@router.get("/{item_id}", response_model=ItemOut, summary="获取单个商品")
async def get_item(
    item_id: Annotated[int, Path(gt=0)],
    settings: Annotated[Settings, Depends(get_settings)],
):
    return {"id": item_id, "name": settings.app_name, "price": 88.0}


@router.post(
    "/",
    response_model=ItemOut,
    status_code=status.HTTP_201_CREATED,
    summary="创建商品"
)
async def create_item(item: ItemCreate):
    return {"id": 101, "name": item.name, "price": item.price}


app.include_router(router)

这个例子里你能看到几乎所有主干能力:

  • FastAPI() 创建应用

  • APIRouter() 分组路由

  • prefixtags

  • 路径参数 Path()

  • 查询参数 Query()

  • Header 依赖校验

  • Pydantic 请求体模型

  • response_model

  • status_code

  • 配置对象依赖注入

这些能力都来自官方教程和参考文档的主线组合。(FastAPI)


19. 初学 FastAPI 路由最容易踩的坑

坑 1:以为所有参数都是 body

不是。
FastAPI 会根据函数签名和类型判断参数来自哪里。路径里同名的是 path;不在路径里、又不是 Pydantic body 模型的,多半会被当成 query;显式用 Header()Form()File()Body() 则按对应来源处理。(FastAPI)

坑 2:返回什么都行,不写 response_model

能跑,但不够稳。
不写输出模型,文档和响应过滤能力就少一大块,后续很容易把内部字段漏出去。(FastAPI)

坑 3:项目一大还把所有路由写在 main.py

这会把项目养成意大利面条怪。
路由多了就应该拆 APIRouter,再用 include_router() 组合。(FastAPI)

坑 4:明明是公共逻辑,却每个接口都手写一遍

这就是 Depends() 该出场的时候。认证、数据库 session、配置对象、分页规则、权限检查,都应该尽量抽成依赖。(FastAPI)


20. 你可以怎么记住 FastAPI 路由的设计哲学

最后给你一个非常实用的脑图式总结:

一条 FastAPI 路由,本质上是四层东西叠在一起

第一层:HTTP 映射
路径是什么,方法是什么。(FastAPI)

第二层:输入契约
参数从哪里来,类型是什么,怎么校验。(FastAPI)

第三层:业务执行
真正的 Python 函数逻辑。这个部分是你自己的代码。

第四层:输出契约和文档
返回值长什么样,状态码是多少,文档怎么展示。(FastAPI)

把这四层看懂,你再看 FastAPI 路由,就不是“几个装饰器和几个参数”,而是一套非常完整的 API 声明系统。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐