“警惕!FastAPI接口一夜「消失」” 95%程序员靠这招自救:我的路由分离血泪史

15 阅读3分钟

fIDFPdH9l

深夜十二点,你终于写完了期待已久的认证接口。切到浏览器,满心欢喜地刷新 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 应用实例。

  1. main.py 中的 app
  2. 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

发生了什么?

  1. Swagger UI 确实会显示GET /auth/ 接口
  2. 所有在main.py中定义的接口全部消失
  3. 现在,应用程序只有一个认证接口

还有人会说,那就启两个不同的 FastAPI 端口,分别对应 main:appauth: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

# 更多接口...

这么做的后果:

  1. main.py 文件迅速膨胀:一个月后,少说得有 2000+ 行。
  2. 冲突不断:团队合作时,每次提交代码都会冲突不断。
  3. 维护困难:要找特定功能代码,难上加难
  4. 测试困难:没办法单独测试认证模块接口

救赎——APIRouter 的正确用法

参考:

FastAPI 官方文档-Bigger Applications - Multiple Files

FastAPI 官方文档-APIRouter class

我已经把官方文档中,重点内容整理出来了,大家直接往下看就行。

核心概念: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 中,接口列表显示如下:

image-20260111233807964

可以看到,所有的接口都罗列在一起。

对此,我们可以再进一步。把这些接口按业务来做分类展示。修改如下:

修改 auth.py 文件

# 修改前
router = APIRouter()

# 修改后
router = APIRouter(
    tags=["auth"]
)

修改 todos.py 文件

# 修改前
router = APIRouter()

# 修改后
router = APIRouter(
    tags=["todos"]
)

保存代码,切回浏览器的 Swagger UI 页面,刷新页面。展示如下:

image-20260111233701013

这样,可读性就好多了。

更多分类情况参考:fastapi.APIRouter.include_router

【立即检查】你的 FastAPI 路由

  1. 确认只有一个app = FastAPI()实例 - 全局搜索,确保没有重复创建
  2. 检查所有路由文件都使用APIRouter - 而不是FastAPI
  3. 验证主应用正确加载了所有路由 - 查看 main.py 中的include_router调用

想要获取本章完整代码,请在评论区回复 【FastAPI】,代码直接复制就能跑。

关于 FastAPI 的其他疑问

如何正确理解 FastAPI ?

如何 3 分钟搞定FastAPI的数据库设置 ?

如何快速搞定 Python 虚拟环境和 FastAPI 安装

如何快速开发 FastAPI “增删改查” 接口?

如何快速构建 API 接口服务器?

如何使用 FastAPI 自动完成参数验证?

相关内容我都给大家做好了,感兴趣的朋友来我主页找一找,直接就可以看到。

关注我,每天分享「Python」、「职场」有趣干货,千万不要错过!