#APIRouter 模块化:像拼积木一样组织你的大型项目架构
#1. 为什么需要 APIRouter?
#1.1 不用 Router 的问题
想象一个社交应用,有用户、帖子、评论、点赞等功能,全部写在 main.py 里:
# ❌ main.py — 几千行,难以维护
@app.get("/users")
def list_users(): ...
@app.post("/users")
def create_user(): ...
@app.get("/users/{id}")
def get_user(id): ...
@app.put("/users/{id}")
def update_user(id): ...
@app.delete("/users/{id}")
def delete_user(id): ...
@app.get("/posts")
def list_posts(): ...
@app.post("/posts")
def create_post(): ...
# ... 再来 50 个路由#1.2 用 Router 拆分的优势
app/
├── main.py ← 主入口,组装所有路由
├── dependencies.py ← 全局依赖(认证、数据库)
├── routers/
│ ├── __init__.py
│ ├── users.py ← 用户相关路由(/users/...)
│ ├── posts.py ← 帖子相关路由(/posts/...)
│ ├── comments.py ← 评论相关路由(/comments/...)
│ └── auth.py ← 认证相关路由(/auth/...)
└── models/
├── user.py
└── post.py每个文件只管自己的功能,改一处不影响其他模块。
#2. APIRouter 基础用法
#2.1 创建 Router
# routers/users.py
from fastapi import APIRouter, Depends, HTTPException
from typing import List
router = APIRouter(prefix="/users", tags=["用户管理"])
# 路由路径会自动加上 prefix = /users
@router.get("/", response_model=List[UserSchema])
async def list_users(skip: int = 0, limit: int = 100):
return await db.get_users(skip=skip, limit=limit)
@router.get("/{user_id}", response_model=UserSchema)
async def get_user(user_id: int):
user = await db.get_user(user_id)
if not user:
raise HTTPException(404, "User not found")
return user
@router.post("/", response_model=UserSchema, status_code=201)
async def create_user(data: CreateUserSchema):
return await db.create_user(data)
@router.put("/{user_id}", response_model=UserSchema)
async def update_user(user_id: int, data: UpdateUserSchema):
return await db.update_user(user_id, data)
@router.delete("/{user_id}", status_code=204)
async def delete_user(user_id: int):
await db.delete_user(user_id)
return None#2.2 在主应用中注册 Router
# main.py
from fastapi import FastAPI
from routers import users, posts, comments, auth
app = FastAPI(title="我的 API", version="1.0.0")
# 注册所有子路由
app.include_router(users.router)
app.include_router(posts.router)
app.include_router(comments.router)
app.include_router(auth.router)
@app.get("/")
async def root():
return {"message": "Welcome to my API"}#2.3 效果:自动生成 OpenAPI 文档
路由结构 OpenAPI 显示
/users/... ← 标签:用户管理
GET / ← 列出用户
GET /{id} ← 获取用户详情
POST / ← 创建用户
PUT /{id} ← 更新用户
DELETE /{id} ← 删除用户
/posts/... ← 标签:帖子管理
GET / ← 列出帖子
POST / ← 创建帖子
...#3. Router 的高级特性
#3.1 多层嵌套 Router
# routers/content/
# __init__.py
from .posts import router as posts_router
from .comments import router as comments_router
# routers/content/posts.py
router = APIRouter(prefix="/posts", tags=["内容 - 帖子"])
# routers/content/comments.py
router = APIRouter(prefix="/comments", tags=["内容 - 评论"])
# routers/content/__init__.py
from .posts import router as posts_router
from .comments import router as comments_router# routers/__init__.py — 一级路由
from .content.posts import router as posts_router
from .content.comments import router as comments_router
from .users import router as users_router
from .auth import router as auth_router
# main.py
from routers import (
posts_router, comments_router,
users_router, auth_router
)
app = FastAPI()
app.include_router(auth_router, prefix="/api/v1")
app.include_router(users_router, prefix="/api/v1")
app.include_router(posts_router, prefix="/api/v1")
app.include_router(comments_router, prefix="/api/v1")#3.2 Router 级依赖注入
# 路由公共依赖
async def get_current_user(token: str = Header(...)):
return verify_token(token)
# users.py
router = APIRouter(
prefix="/users",
dependencies=[Depends(get_current_user)], # 本文件所有路由都需认证
tags=["用户管理"]
)
@router.get("/") # ✅ 自动需要认证
async def list_users():
...
# auth.py(不需要认证的路由)
router = APIRouter(prefix="/auth", tags=["认证"])
@router.post("/login") # ✅ 不需要认证
async def login():
...#3.3 路由排序与优先级
# ⚠️ 注意路由顺序!FastAPI 按定义顺序匹配
@router.get("/users") # 匹配 /users
async def list_users(): ...
@router.get("/users/{user_id}") # 这个会匹配 /users/123
async def get_user(user_id): ...
# 但 /users/new 不匹配 user_id,
# 如果你想单独处理 /users/new,必须放在 /users/{user_id} 前面:
@router.get("/users")
@router.get("/users/new") # 精确匹配优先
@router.get("/users/{user_id}")#4. 完整的模块化项目结构
#4.1 目录树
pyweb/
├── main.py # 应用入口
├── config.py # 配置文件
├── database.py # 数据库连接
├── models/
│ ├── __init__.py
│ ├── user.py
│ ├── post.py
│ └── schemas.py # Pydantic 模型
├── routers/
│ ├── __init__.py # 导出所有 router
│ ├── users.py
│ ├── posts.py
│ ├── comments.py
│ └── auth.py
├── services/
│ ├── __init__.py
│ ├── user_service.py
│ └── post_service.py # 业务逻辑层
├── dependencies.py # 全局依赖
├── exceptions.py # 自定义异常
└── middleware/
├── __init__.py
├── cors.py
├── logging.py
└── gzip.py#4.2 config.py — 集中配置
from pydantic_settings import BaseSettings
from functools import lru_cache
class Settings(BaseSettings):
app_name: str = "DaomanAPI"
version: str = "1.0.0"
debug: bool = False
database_url: str = "sqlite+aiosqlite:///./app.db"
jwt_secret: str = "change-me-in-production"
jwt_algorithm: str = "HS256"
jwt_expire_minutes: int = 30
cors_origins: list[str] = ["https://daomanpy.com"]
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
@lru_cache()
def get_settings() -> Settings:
return Settings()#4.3 dependencies.py — 全局依赖
from fastapi import Depends, Header, HTTPException
from fastapi.security import HTTPBearer
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_db
from services.auth import verify_token
from config import get_settings
security = HTTPBearer(auto_error=False)
async def get_current_user_optional(
token: str = Depends(security),
db: AsyncSession = Depends(get_db)
):
if not token:
return None
return await verify_token(db, token.credentials)
async def get_current_user(
token: str = Depends(security),
db: AsyncSession = Depends(get_db)
) -> dict:
if not token:
raise HTTPException(401, "请先登录")
user = await verify_token(db, token.credentials)
if not user:
raise HTTPException(401, "Token 无效")
return user#4.4 services/user_service.py — 业务逻辑层
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from models.user import User
from exceptions import NotFoundException, ConflictException
class UserService:
def __init__(self, db: AsyncSession):
self.db = db
async def get_by_id(self, user_id: int) -> User:
result = await self.db.execute(
select(User).where(User.id == user_id)
)
user = result.scalar_one_or_none()
if not user:
raise NotFoundException("用户", str(user_id))
return user
async def get_by_email(self, email: str) -> User | None:
result = await self.db.execute(
select(User).where(User.email == email)
)
return result.scalar_one_or_none()
async def create(self, data: dict) -> User:
# 检查邮箱冲突
existing = await self.get_by_email(data["email"])
if existing:
raise ConflictException("邮箱已被注册")
user = User(**data)
self.db.add(user)
await self.db.commit()
await self.db.refresh(user)
return user#4.5 routers/users.py — 用户路由
from fastapi import APIRouter, Depends, status
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from database import get_db
from dependencies import get_current_user, get_current_user_optional
from models.schemas import (
UserSchema, CreateUserSchema, UpdateUserSchema, UserListSchema
)
from services.user_service import UserService
router = APIRouter(prefix="/users", tags=["用户管理"])
def get_user_service(db: AsyncSession = Depends(get_db)) -> UserService:
return UserService(db)
@router.get("/", response_model=List[UserSchema])
async def list_users(
skip: int = 0,
limit: int = 20,
service: UserService = Depends(get_user_service)
):
users = await service.list_users(skip=skip, limit=limit)
return users
@router.get("/me", response_model=UserSchema)
async def get_me(
current_user: dict = Depends(get_current_user),
service: UserService = Depends(get_user_service)
):
return await service.get_by_id(current_user["id"])
@router.get("/{user_id}", response_model=UserSchema)
async def get_user(
user_id: int,
service: UserService = Depends(get_user_service)
):
return await service.get_by_id(user_id)
@router.post("/", response_model=UserSchema, status_code=status.HTTP_201_CREATED)
async def create_user(
data: CreateUserSchema,
service: UserService = Depends(get_user_service)
):
return await service.create(data.model_dump())
@router.put("/{user_id}", response_model=UserSchema)
async def update_user(
user_id: int,
data: UpdateUserSchema,
current_user: dict = Depends(get_current_user),
service: UserService = Depends(get_user_service)
):
if current_user["id"] != user_id and current_user["role"] != "admin":
raise HTTPException(403, "无权修改他人信息")
return await service.update(user_id, data.model_dump(exclude_unset=True))
@router.delete("/{user_id}", status_code=204)
async def delete_user(
user_id: int,
current_user: dict = Depends(get_current_user),
service: UserService = Depends(get_user_service)
):
if current_user["id"] != user_id and current_user["role"] != "admin":
raise HTTPException(403, "无权删除")
await service.delete(user_id)#4.6 main.py — 最终组装
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from config import get_settings
from database import engine, Base
from routers import (
users_router, posts_router,
comments_router, auth_router
)
settings = get_settings()
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动:创建数据库表
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
# 关闭:释放连接池
await engine.dispose()
app = FastAPI(
title=settings.app_name,
version=settings.version,
lifespan=lifespan,
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(auth_router)
app.include_router(users_router)
app.include_router(posts_router)
app.include_router(comments_router)
@app.get("/health")
async def health():
return {"status": "ok"}
@app.get("/")
async def root():
return {"message": f"Welcome to {settings.app_name}"}#5. Router 的最佳实践
| 实践 | 说明 |
|---|---|
| 按功能模块拆分 | 每个 router 负责一个实体(如 users、posts) |
| 使用 prefix | 统一添加路径前缀,避免路径重复 |
| 使用 tags | 自动生成有组织的 OpenAPI 文档 |
| 依赖分组 | 用 dependencies=[] 为一类路由批量添加认证 |
| 业务逻辑放 service | router 只做路由转发,service 处理业务 |
| Schema 独立文件 | Pydantic 模型和 SQLAlchemy 模型分开 |
#6. 小结
APIRouter 核心用法速查:
router = APIRouter(
prefix="/users", # 路径前缀
tags=["用户管理"], # OpenAPI 文档分组
dependencies=[Depends(...)], # 本模块通用依赖
)
router.include_router(sub_router) # 嵌套子路由
app.include_router(router) # 主应用注册💡 核心思想:Router 就像乐高积木的接口。把每个功能模块做成一个独立 Router,主应用只需要把它们拼起来,就能组成完整的大型应用。
🔗 扩展阅读

