【FastAPI筑基-Day11】路由分层与项目工程目录拆分|告别单文件,模块化大型项目架构
专栏:FastAPI零基础后端实战系列 标签:FastAPI、路由拆分、APIRouter、工程化、项目目录、模块化 前置学习:Day1~Day10,掌握统一响应、全局异常处理
一、前言
前面所有示例代码全部写在同一个 main.py 文件中,写小 demo 没问题。一旦业务变多:用户模块、文章模块、文件模块、管理后台接口全部堆在一个文件里,会出现这些问题:
- 文件代码行数爆炸,几千行,阅读维护极其痛苦
- 多人协作开发,所有人修改同一个文件,极易产生代码冲突
- 不方便按业务模块拆分,模块职责混乱
- 不方便单独管理每个模块的路由前缀、接口标签
Day11 核心目标:使用 APIRouter 做路由分层,搭建一套可直接用于开发的标准工程目录。 本篇会从零搭出一个包含用户、文章两个业务模块的完整模块化项目,并整合 Day10 写好的统一响应与全局异常处理。学完本篇,你将具备企业级多模块后端项目的目录搭建与路由分层能力。
知识点清单:
- ✅ APIRouter 路由对象基础使用
- ✅ 按业务模块拆分多个路由文件
- ✅ 路由统一前缀、tags 接口文档分组
- ✅ 公共工具、模型、异常处理抽离
- ✅ 完整标准项目目录结构
- ✅ 整合 Day10 写好的统一响应、全局异常
二、APIRouter 是什么
APIRouter 相当于小型 FastAPI 实例,可以单独写接口、加路由前缀、设置文档标签,最后统一注册到主 app 对象上。
简单理解:
FastAPI():主应用入口APIRouter():子模块路由,每个业务模块一个 router
用一张图看整体注册关系:
graph TD
A["main.py 主应用 app"] -->|include_router| B["user router 前缀 /user 标签 用户管理"]
A -->|include_router| C["article router 前缀 /article 标签 文章管理"]
B --> D["/user/info 、 /user/add"]
C --> E["/article/list 、 /article/detail/ID"]
A --> F["CORS 跨域 + 全局异常拦截"]
A -.复用.-> G["common/response.py 统一响应"]
B -.复用.-> G
C -.复用.-> G
主 app 只负责注册与全局配置,每个业务模块自己的接口、前缀、标签全部在各自的 router 文件里管理,互不干扰。
三、标准项目目录结构
我们搭建完整工程目录,这是企业 FastAPI 通用目录:
fastapi_demo/
├── main.py # 程序入口,注册路由、全局异常、跨域配置
├── routers/ # 所有业务路由文件夹
│ ├── __init__.py
│ ├── user.py # 用户模块接口
│ └── article.py # 文章模块接口
├── schemas/ # pydantic数据模型(请求/响应模型)
│ ├── __init__.py
│ ├── user_schema.py
│ └── article_schema.py
├── common/ # 公共工具类
│ ├── __init__.py
│ └── response.py # Day10封装统一响应函数
└── requirements.txt # 项目依赖
目录说明:
routers:存放各个业务模块接口,每个文件对应一个业务schemas:存放 Pydantic 模型,请求体、响应体定义全部放这里,不和接口代码混在一起common:公共能力,统一返回、日志工具、自定义异常等
requirements.txt
fastapi>=0.110.0
uvicorn>=0.24.0
pydantic>=2.0
四、分步编写代码
1. common/response.py 统一响应(复用 Day10 代码)
from typing import Any
from fastapi.responses import JSONResponse
def success_response(data: Any = None, msg: str = "请求成功") -> JSONResponse:
return JSONResponse(
status_code=200,
content={
"code": 200,
"msg": msg,
"data": data
}
)
def fail_response(code: int = 400, msg: str = "请求失败", data: Any = None) -> JSONResponse:
return JSONResponse(
status_code=200,
content={
"code": code,
"msg": msg,
"data": data
}
)
2. schemas/user_schema.py 用户 pydantic 模型
from pydantic import BaseModel, Field
class UserCreate(BaseModel):
username: str = Field(min_length=2, max_length=10, description="用户名")
age: int = Field(ge=0, le=120, description="年龄")
class UserResp(BaseModel):
id: int
username: str
age: int
3. routers/user.py 用户模块路由
使用 APIRouter,设置路由前缀 /user,接口文档标签 ["用户管理"]:
from fastapi import APIRouter
from common.response import success_response
from schemas.user_schema import UserCreate
# 创建路由实例
router = APIRouter(
prefix="/user",
tags=["用户管理"]
)
@router.get("/info")
def get_user_info():
"""获取用户信息"""
user = {"id": 1001, "username": "张三", "age": 22}
return success_response(data=user)
@router.post("/add")
def add_user(user: UserCreate):
"""新增用户"""
return success_response(data=user.model_dump(), msg="用户创建成功")
4. routers/article.py 文章模块路由
from fastapi import APIRouter
from common.response import success_response
router = APIRouter(
prefix="/article",
tags=["文章管理"]
)
@router.get("/list")
def get_article_list():
"""获取文章列表"""
articles = [
{"id": 1, "title": "FastAPI入门"},
{"id": 2, "title": "路由分层学习"}
]
return success_response(data=articles)
@router.get("/detail/{article_id}")
def get_article_detail(article_id: int):
"""获取文章详情"""
return success_response(data={"article_id": article_id, "content": "文章内容......"})
5. main.py 主入口文件
职责:创建 app、跨域配置、全局异常捕获、导入注册所有子路由,不写业务接口!
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from common.response import fail_response
# 导入子路由
from routers.user import router as user_router
from routers.article import router as article_router
app = FastAPI(title="Day11 FastAPI模块化项目", version="1.0.0")
# 跨域配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(user_router)
app.include_router(article_router)
# 全局参数校验异常
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
err_msg = exc.errors()[0]["msg"]
return fail_response(code=400, msg=f"参数校验失败:{err_msg}")
# 全局系统异常
@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
return fail_response(code=500, msg=f"服务器异常:{str(exc)}")
@app.get("/")
def root():
return {"msg": "FastAPI模块化项目启动成功,请访问 /docs 查看接口文档"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
五、运行项目 & 查看效果
- 安装依赖
pip install -r requirements.txt
- 启动
python main.py
- 访问文档地址:
http://127.0.0.1:8000/docs
可以看到文档已经自动按 tags 分为两大组:用户管理、文章管理,接口地址自动拼接各自前缀:
/user/info/article/list
实际运行结果
本地用 curl 逐个请求验证,全部返回统一响应格式:
$ curl http://127.0.0.1:8000/user/info
{"code":200,"msg":"请求成功","data":{"id":1001,"username":"张三","age":22}}
$ curl -X POST http://127.0.0.1:8000/user/add -H "Content-Type: application/json" -d '{"username":"李四","age":25}'
{"code":200,"msg":"用户创建成功","data":{"username":"李四","age":25}}
$ curl http://127.0.0.1:8000/article/list
{"code":200,"msg":"请求成功","data":[{"id":1,"title":"FastAPI入门"},{"id":2,"title":"路由分层学习"}]}
$ curl http://127.0.0.1:8000/article/detail/5
{"code":200,"msg":"请求成功","data":{"article_id":5,"content":"文章内容......"}}
参数校验失败时,Day10 的全局异常拦截同样生效:
$ curl -X POST http://127.0.0.1:8000/user/add -H "Content-Type: application/json" -d '{"username":"张","age":200}'
{"code":400,"msg":"参数校验失败:String should have at least 2 characters","data":null}
路由拆分后,统一响应与全局异常无需任何改造,直接全模块复用。
重点:所有业务代码全部移到 routers、schemas,main.py 只做配置与路由注册。
六、APIRouter 常用参数说明
router = APIRouter(
prefix="/user", # 当前模块全部接口统一前缀
tags=["用户管理"], # swagger文档分组标签
responses={400: {"description": "参数错误"}} # 当前模块统一的响应说明
)
prefix:避免每个接口重复写前缀,统一管理tags:接口文档分类,多个 router 可以设置不同标签,文档界面更清晰
七、拓展小知识点
- 路由还可以嵌套:
APIRouter可以 include 另外一个APIRouter,适合大型项目多级模块 - 可以给不同路由设置不同依赖,例如部分模块需要登录校验
schemas目录区分请求模型、响应模型,不要把所有模型写在同一个文件
八、Day11 核心总结
- APIRouter 实现路由拆分,解决单文件臃肿问题,适配多人协作开发
- 企业标准目录:
routers放路由、schemas放 pydantic 模型、common放公共工具 - main.py 只做应用初始化、中间件、异常、注册路由,不再写业务接口
- prefix 统一路由前缀,tags 实现接口文档分组
- 完美兼容前面写的统一响应、全局异常、CORS 跨域
九、下期预告
Day12:FastAPI Depends 依赖注入精讲
公共参数抽取、接口登录鉴权、权限拦截,学会依赖注入,实现 token 登录校验与接口权限控制。