深夜十二点,你终于写完了期待已久的认证接口。切到浏览器,满心欢喜地刷新 Swagger UI。
等等,接口呢? 你揉了揉眼睛,再次刷新。空空如也。出现了幻觉?
这场景熟悉吗?这是每个FastAPI 开发者从“单体文件” 到 “模块化” 必然会经历的过程。 每个开发者都曾在这个看似简单的 “路由分离” 问题上撞得头破血流。
今天,我要带你彻底解决这个问题,并揭示 FastAPI 路由系统的核心秘密。
陷阱:为什么你的接口“消失了”?
先重现下那个场景:
项目目录结构:
wangerge_notes: TodoApp$ tree ./ -L 2
./
├── __init__.py
├── database.py
├── main.py
├── models.py
├── routers
│ ├── __init__.py
│ └── auth.py
└── todos.db
main.py 文件如下
# 简略版本,省略具体代码,只突出当前问题重点
from fastapi import FastAPI
app = FastAPI()
@app.get("/todos")
async def get_todos():
# 简略的业务逻辑
return {"message": "获取待办事项"}
auth.py 文件如下
# auth.py - 看似正确,实则致命
from fastapi import FastAPI # 引入FastAPI
app = FastAPI() # 创建一个新的应用实例
@app.get("/auth/")
async def get_user():
return {"user": "authenticated"}
终端运行
uvicorn main:app --reload
打开 Swagger UI,刷新,搜索... GET /auth/ 接口在哪里?
问题分析:
你创建了两个完全独立的 FastAPI 应用实例。
- main.py 中的 app
- auth.py 中的 app
当你在终端,使用 main:app 启动程序时,FastAPI 只加载了 main.py 中的 app 实例。FastAPI 完全不知道 auth.py 中 app 实例的存在。
这个问题非常经典,但又很少被人明确指出来。
混乱:两种错误尝试
01、启动错误的 app
有人会说,使用 main:app 启动程序时,FastAPI 加载了 main.py 中的 app 实例。那使用 auth:app 启动程序时,FastAPI 不就加载 auth.py 中的 app 实例。
于是,就在终端使用如下命令启动应用程序:
uvicorn auth:app --reload
发生了什么?
- Swagger UI 确实会显示
GET /auth/接口 - 所有在
main.py中定义的接口全部消失 - 现在,应用程序只有一个认证接口
还有人会说,那就启两个不同的 FastAPI 端口,分别对应 main:app和 auth:app 。这样不就都启来了么?
对于这些人,我想说 “小心你们技术经理,拿着 30 米大刀来找你!!!”
02、合并所有代码
有些开发者会想到另外一条路,把 auth.py 文件中的内容,全部合并到 main.py 文件中。main.py 文件如下:
# main.py
from fastapi import FastAPI
app = FastAPI()
# auth.py 文件中的认证接口,合并到 main.py 文件中
@app.get("/auth/")
async def get_auth():
return {"user": "authenticated"}
# main.py 文件,原有的 50个其他接口...
@app.get("/todos/")
async def get_todos():
# 业务逻辑
pass
# 更多接口...
这么做的后果:
- main.py 文件迅速膨胀:一个月后,少说得有 2000+ 行。
- 冲突不断:团队合作时,每次提交代码都会冲突不断。
- 维护困难:要找特定功能代码,难上加难
- 测试困难:没办法单独测试认证模块接口
救赎——APIRouter 的正确用法
参考:
FastAPI 官方文档-Bigger Applications - Multiple Files
我已经把官方文档中,重点内容整理出来了,大家直接往下看就行。
核心概念:APIRouter 是一个路由收集器,它收集项目中的路由定义,然后由主应用统一加载。
目录结构修改:
wangerge_notes: TodoApp$ tree ./ -L 2
./
├── __init__.py
├── database.py # 数据库配置
├── main.py # 应用入口(<100行)
├── models.py # 数据模型
├── routers # 所有路由模块
│ ├── __init__.py
│ └── auth.py # 认证相关路由
│ └── todos.py # 待办事项路由(main.py 中的接口代码拷贝)
auth.py 文件内容如下:
from fastapi import APIRouter # 注意:不是FastAPI!
router = APIRouter() # 创建路由器实例
@router.get("/auth") # 使用router,而不是app
async def get_user():
return {"user": "authenticated"}
todos.py 文件内容如下:(核心修改如下,简略掉具体业务代码)
from fastapi import APIRouter # 注意:不是 FastAPI!
router = APIRouter() # 这里是 router 不是 app
@router.get("/todo/{todo_id}", status_code=status.HTTP_200_OK)
async def read_todo(db: db_dependency, todo_id: int = Path(gt=0)):
# 省略具体业务
# 后面还有若干接口
关键步骤:在 main.py 中整合
上面两个文件中都创建了 router,接下来,关键是如何把它"挂载"到 main.py 中
# main.py - 简洁版主应用入口
from fastapi import FastAPI
from routers import auth, todos # 分别导入路由器模块
app = FastAPI()
# 重点:将每个路由器挂载到主应用
app.include_router(auth.router)
app.include_router(todos.router)
现在,我们使用 main:app 启动应用,就可以看到全部 API 接口了。
路由器的组织分类
现在 Swagger UI 中,接口列表显示如下:
可以看到,所有的接口都罗列在一起。
对此,我们可以再进一步。把这些接口按业务来做分类展示。修改如下:
修改 auth.py 文件
# 修改前
router = APIRouter()
# 修改后
router = APIRouter(
tags=["auth"]
)
修改 todos.py 文件
# 修改前
router = APIRouter()
# 修改后
router = APIRouter(
tags=["todos"]
)
保存代码,切回浏览器的 Swagger UI 页面,刷新页面。展示如下:
这样,可读性就好多了。
更多分类情况参考:fastapi.APIRouter.include_router
【立即检查】你的 FastAPI 路由
- 确认只有一个
app = FastAPI()实例 - 全局搜索,确保没有重复创建 - 检查所有路由文件都使用
APIRouter- 而不是FastAPI - 验证主应用正确加载了所有路由 - 查看 main.py 中的
include_router调用
想要获取本章完整代码,请在评论区回复 【FastAPI】,代码直接复制就能跑。
关于 FastAPI 的其他疑问
如何快速搞定 Python 虚拟环境和 FastAPI 安装
相关内容我都给大家做好了,感兴趣的朋友来我主页找一找,直接就可以看到。
关注我,每天分享「Python」、「职场」有趣干货,千万不要错过!