当 API 从几个接口膨胀到几十个甚至上百个时,把所有路由堆在一个 main.py 里会让代码迅速失控。FastAPI 提供的 APIRouter 就是为解决这个问题而生的——它让你像搭积木一样组织路由,天然支持按功能模块拆分、复用依赖、统一前缀,是构建可维护大型项目的核心工具。

为什么需要 APIRouter

假设一个博客系统有用户、文章、评论三个模块,每个模块 5~8 个接口。如果全部写在 main.py 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# main.py —— 反例:所有路由堆在一起
app = FastAPI()

@app.post("/users/register")
@app.get("/users/{id}")
@app.put("/users/{id}")
# ... 10 个用户接口

@app.post("/articles")
@app.get("/articles/{id}")
@app.delete("/articles/{id}")
# ... 8 个文章接口

@app.post("/comments")
@app.get("/comments/{id}")
# ... 6 个评论接口

不到三个模块就会超过 20 个路由函数,代码可读性和维护性急剧下降。APIRouter 的核心思想是:每个功能模块独立管理自己的路由,最后统一挂载到主应用上

创建第一个 APIRouter

APIRouter 的用法与 FastAPI 实例几乎一致——同样支持 @router.get()@router.post() 等装饰器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# routers/users.py
from fastapi import APIRouter

router = APIRouter()

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

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

@router.post("/")
async def create_user(name: str, email: str):
return {"id": 3, "name": name, "email": email}

然后将它挂载到主应用:

1
2
3
4
5
6
# main.py
from fastapi import FastAPI
from routers import users

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

此时,routers/users.py 中定义的三个路由分别对应 //{user_id}/。所有路由逻辑都封闭在 users 模块内,主文件只负责组装。

路由前缀(prefix)

上面的例子有个问题:所有模块都直接暴露在根路径下,容易冲突。使用 prefix 参数给每个模块统一添加路径前缀:

1
2
3
app.include_router(users.router, prefix="/users")
app.include_router(articles.router, prefix="/articles")
app.include_router(comments.router, prefix="/comments")

挂载后实际路径变为 /users//users/{user_id}/articles/ 等。prefixinclude_router 时的参数,不在路由文件内部硬编码,这使得同一个路由模块可以被挂载到不同前缀下——例如 /v1/users/v2/users 复用同一套路由逻辑。

tags 与 API 文档分组

在自动生成的 Swagger 文档(/docs)中,tags 用于对接口分组,让 OpenAPI 文档更清晰:

1
2
3
4
5
6
7
# routers/users.py
router = APIRouter(tags=["users"])

# 或者在单个路由上指定
@router.get("/{user_id}", tags=["users"])
async def get_user(user_id: int):
...

然后在 include_router 时也可以统一覆盖:

1
app.include_router(users.router, prefix="/users", tags=["用户管理"])

文档页面中,「用户管理」分组下会列出所有 /users 开头的接口。

依赖注入在路由级别复用

APIRouter 支持在创建时声明全局依赖,该路由器下的所有接口都会执行这些依赖——典型场景是认证校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from fastapi import APIRouter, Depends, Header, HTTPException

async def verify_token(x_token: str = Header(...)):
if x_token != "secret-token":
raise HTTPException(status_code=403, detail="未授权")

# 所有路由都需要 token 校验
router = APIRouter(
prefix="/admin",
dependencies=[Depends(verify_token)],
)

@router.get("/dashboard")
async def dashboard():
return {"message": "管理后台"}

这样一来,dashboard 无需在每个接口的参数中重复声明 Depends(verify_token),路由级别的依赖会自动执行。如果某个特定接口需要免除该依赖,可以覆盖 dependencies=[]

多层级路由嵌套

APIRouter 本身也可以被其他 APIRouter 挂载,实现嵌套:

1
2
3
4
5
6
7
8
9
10
# routers/api_v1.py
from fastapi import APIRouter
from routers import users, articles

api_v1_router = APIRouter()
api_v1_router.include_router(users.router, prefix="/users")
api_v1_router.include_router(articles.router, prefix="/articles")

# main.py
app.include_router(api_v1_router, prefix="/api/v1")

最终路径为 /api/v1/users//api/v1/articles/。版本升级时新增 api_v2_router 即可,旧版本继续维护不受影响。

推荐的项目目录结构

综合以上特性,一个中等规模 FastAPI 项目的推荐结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
project/
├── main.py # 入口:创建 app,挂载路由
├── routers/ # 路由模块
│ ├── __init__.py
│ ├── users.py
│ ├── articles.py
│ └── comments.py
├── dependencies.py # 公共依赖(认证、权限等)
├── models/ # Pydantic 模型 / ORM 模型
│ ├── user.py
│ └── article.py
├── services/ # 业务逻辑层
│ └── user_service.py
└── core/
├── config.py # 配置
└── database.py # 数据库连接

main.py 只做三件事:创建 FastAPI 实例、注册中间件和异常处理器、include_router 组装各模块。

include_router 的常用参数汇总

参数 说明 示例
prefix 路由路径前缀 "/users"
tags OpenAPI 文档分组名 ["用户管理"]
dependencies 路由级别全局依赖 [Depends(auth)]
responses 补充响应文档 {404: {"desc": "未找到"}}
include_in_schema 是否出现在 OpenAPI 文档中 False(隐藏内部路由)

总结

APIRouter 是 FastAPI 项目从”能用”走向”好维护”的分水岭。核心要点:

  1. 每个功能模块一个路由文件,内部定义 router = APIRouter()
  2. 通过 prefix 统一前缀,避免路径冲突,支持版本化;
  3. 通过 dependencies 路由级依赖注入,精简每个接口的参数列表;
  4. 支持嵌套,灵活组织多层级 API 结构。

掌握 APIRouter 之后,你会发现 FastAPI 项目的扩展变得像拼乐高一样自然——新增一个功能模块,就是新建一个路由文件,然后在 main.py 中多加一行 include_router