FastAPI筑基_Day11_路由分层与项目工程目录拆分

0 阅读6分钟

【FastAPI筑基-Day11】路由分层与项目工程目录拆分|告别单文件,模块化大型项目架构

专栏:FastAPI零基础后端实战系列 标签:FastAPI、路由拆分、APIRouter、工程化、项目目录、模块化 前置学习:Day1~Day10,掌握统一响应、全局异常处理


一、前言

前面所有示例代码全部写在同一个 main.py 文件中,写小 demo 没问题。一旦业务变多:用户模块、文章模块、文件模块、管理后台接口全部堆在一个文件里,会出现这些问题:

  1. 文件代码行数爆炸,几千行,阅读维护极其痛苦
  2. 多人协作开发,所有人修改同一个文件,极易产生代码冲突
  3. 不方便按业务模块拆分,模块职责混乱
  4. 不方便单独管理每个模块的路由前缀、接口标签

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)

五、运行项目 & 查看效果

  1. 安装依赖
pip install -r requirements.txt
  1. 启动
python main.py
  1. 访问文档地址: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 可以设置不同标签,文档界面更清晰

七、拓展小知识点

  1. 路由还可以嵌套:APIRouter 可以 include 另外一个 APIRouter,适合大型项目多级模块
  2. 可以给不同路由设置不同依赖,例如部分模块需要登录校验
  3. schemas 目录区分请求模型、响应模型,不要把所有模型写在同一个文件

八、Day11 核心总结

  1. APIRouter 实现路由拆分,解决单文件臃肿问题,适配多人协作开发
  2. 企业标准目录:routers 放路由、schemas 放 pydantic 模型、common 放公共工具
  3. main.py 只做应用初始化、中间件、异常、注册路由,不再写业务接口
  4. prefix 统一路由前缀,tags 实现接口文档分组
  5. 完美兼容前面写的统一响应、全局异常、CORS 跨域

九、下期预告

Day12:FastAPI Depends 依赖注入精讲

公共参数抽取、接口登录鉴权、权限拦截,学会依赖注入,实现 token 登录校验与接口权限控制。