【FastAPI筑基-Day12】Depends依赖注入精讲|公共参数抽取、接口登录鉴权、权限拦截(企业必备)
专栏:FastAPI零基础后端实战系列 标签:FastAPI、Depends、依赖注入、接口鉴权、Token校验、权限控制 前置学习:Day11 模块化路由分层工程结构
一、前言
这篇讲 FastAPI 最核心的设计之一 —— Depends 依赖注入。我们会从最简单的函数依赖开始,逐步实现公共参数抽取、Token 登录鉴权、角色权限拦截,最终产出一套可直接运行的完整鉴权方案。学完本篇,你将掌握企业后端开发中最高频的代码复用与接口安全能力。
在真实后端开发中,90% 接口都存在重复逻辑:
- 获取当前登录用户
- 校验 Token 是否有效
- 统一获取分页参数 page、size
- 接口权限判断
如果每个接口都写一遍,代码极度冗余、维护灾难!
FastAPI 最强神器之一 —— Depends 依赖注入
一句话总结:把公共逻辑抽离出来,所有接口直接复用,无需重复编码。
Day12 带你从零吃透 Depends,实现:
- ✅ 公共参数依赖抽取(分页参数通用封装)
- ✅ 基础函数形式依赖注入
- ✅ 类形式依赖注入(高级用法)
- ✅ 全局接口 Token 登录鉴权
- ✅ 局部接口权限拦截
二、什么是 Depends 依赖注入?
依赖注入(Dependency Injection)是 FastAPI 核心设计思想。
作用:将通用、重复、公共逻辑抽离为依赖函数,接口直接声明依赖即可自动执行、自动传参。
打个比方:你去餐厅点菜,不需要自己进厨房洗菜、切菜、炒菜,你只需要告诉服务员"来一份番茄炒蛋",厨房(依赖函数)自动帮你搞定。Depends 就是那个服务员 —— 你声明需要什么,它自动帮你准备好再递给你。
graph TD
A[接口收到请求] --> B[FastAPI 解析 Depends]
B --> C[自动调用依赖函数]
C --> D{依赖校验通过?}
D -->|是| E[将结果注入接口参数]
D -->|否| F[直接返回错误,接口不执行]
E --> G[接口正常执行业务逻辑]
核心流程:请求进来 → 触发依赖 → 校验/处理 → 注入结果 → 接口执行。
三、函数形式依赖注入(分页参数抽取)
最常见的场景:多个列表接口都需要分页参数 page 和 size。
不用 Depends 的写法(每个接口重复声明):
@router.get("/users")
def get_users(page: int = 1, size: int = 10):
...
@router.get("/orders")
def get_orders(page: int = 1, size: int = 10):
...
用 Depends 的写法(抽一次,处处复用):
from fastapi import APIRouter, Depends
router = APIRouter()
def page_params(page: int = 1, size: int = 10):
"""公共分页参数依赖,统一限制最大 size"""
return {"page": page, "size": min(size, 100)}
@router.get("/users")
def get_users(pager: dict = Depends(page_params)):
return {"code": 200, "pager": pager}
@router.get("/orders")
def get_orders(pager: dict = Depends(page_params)):
return {"code": 200, "pager": pager}
效果:接口无需声明 page、size,自动获取、自动校验、统一限制最大 size!修改分页逻辑只需改 page_params 一处。
四、类形式依赖注入(高级企业写法)
当逻辑复杂、需要多个方法复用,使用类依赖更优雅。
from fastapi import Depends
class PageDepend:
def __init__(self, page: int = 1, size: int = 10):
self.page = page
self.size = min(size, 100)
def offset(self):
return (self.page - 1) * self.size
@router.get("/page/data")
def page_data(pd: PageDepend = Depends()):
return {
"page": pd.page,
"size": pd.size,
"offset": pd.offset()
}
类依赖的优势:
__init__接收参数,FastAPI 自动解析- 可以封装多个工具方法(如
offset()、total_pages()等) - 适合复杂公共逻辑:分页、排序、筛选组合
五、核心实战:基于 Depends 实现 Token 登录鉴权
业务场景:部分接口需要登录后才能访问。
实现逻辑:
- 请求头携带 Token
- 依赖自动拦截校验 Token
- 无效 Token 直接 401 拦截
- 校验通过自动解析用户信息注入接口
1. 编写登录鉴权依赖
from fastapi import Header, HTTPException
VALID_TOKEN = "admin123456"
def get_current_user(token: str = Header(None, description="登录令牌")):
if not token:
raise HTTPException(status_code=401, detail="未登录,请先授权")
if token != VALID_TOKEN:
raise HTTPException(status_code=401, detail="Token无效,登录失败")
return {
"user_id": 1001,
"username": "管理员",
"role": "admin"
}
2. 接口引入登录依赖
@router.get("/user/profile")
def user_profile(user: dict = Depends(get_current_user)):
"""个人信息接口,必须登录"""
return {
"msg": "查询成功",
"user_info": user
}
效果:不带 Token / Token 错误直接拦截,无法进入接口逻辑!
六、全局依赖(所有接口统一拦截)
如果整个模块全部需要登录,无需每个接口写 Depends:
from fastapi import APIRouter, Depends
router = APIRouter(
prefix="/admin",
tags=["后台管理"],
dependencies=[Depends(get_current_user)]
)
@router.get("/index")
def admin_index():
return {"msg": "后台管理首页,已自动鉴权"}
超级适合:后台管理模块、需要统一登录的业务模块。该路由下所有接口自动校验 Token,无需逐个添加。
七、拓展:基于依赖实现角色权限控制
在登录依赖基础上,二次封装权限拦截(依赖嵌套):
from fastapi import Depends, HTTPException
def admin_required(user: dict = Depends(get_current_user)):
if user.get("role") != "admin":
raise HTTPException(status_code=403, detail="权限不足,仅管理员可访问")
return user
@router.get("/admin/only", dependencies=[Depends(admin_required)])
def admin_only_api():
return {"msg": "管理员专属接口访问成功"}
graph TD
A[请求进入] --> B[get_current_user 校验 Token]
B -->|Token 无效| C[401 未登录]
B -->|Token 有效| D[admin_required 校验角色]
D -->|非 admin| E[403 权限不足]
D -->|是 admin| F[接口正常执行]
完美实现:登录校验 + 角色权限双层拦截。依赖可以嵌套组合,像搭积木一样灵活。
八、Day12 完整整合代码(可直接运行)
from fastapi import FastAPI, Depends, Header, HTTPException
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(title="Day12 Depends依赖注入实战")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ---------------------- 1. 分页公共依赖 ----------------------
def page_params(page: int = 1, size: int = 10):
return {"page": page, "size": min(size, 100)}
# ---------------------- 2. 登录鉴权依赖 ----------------------
VALID_TOKEN = "admin123456"
def get_current_user(token: str = Header(None)):
if not token:
raise HTTPException(401, "未登录")
if token != VALID_TOKEN:
raise HTTPException(401, "Token无效")
return {"user_id": 1001, "username": "管理员", "role": "admin"}
# ---------------------- 3. 权限依赖 ----------------------
def admin_check(user: dict = Depends(get_current_user)):
if user["role"] != "admin":
raise HTTPException(403, "权限不足")
return user
# ---------------------- 测试接口 ----------------------
@app.get("/list")
def test_list(pager: dict = Depends(page_params)):
return {"code": 200, "pager": pager}
@app.get("/profile")
def profile(user: dict = Depends(get_current_user)):
return {"code": 200, "user": user}
@app.get("/admin", dependencies=[Depends(admin_check)])
def admin_api():
return {"code": 200, "msg": "管理员接口访问成功"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
实际运行结果
启动服务后访问 /docs,FastAPI 自动生成接口文档,4 个接口一目了然:
接口实测返回(/list 默认分页、size=200 被限制为 100、/profile 无 Token 返回 401):
GET /list => 200 {"code":200,"pager":{"page":1,"size":10}}
GET /list?page=3&size=200 => 200 {"code":200,"pager":{"page":3,"size":100}}
GET /page/data?page=2&size=20 => 200 {"code":200,"page":2,"size":20,"offset":20}
GET /profile(带Token) => 200 {"code":200,"user":{"user_id":1001,"username":"管理员","role":"admin"}}
GET /profile(无Token) => 401 {"detail":"未登录"}
GET /admin(带Token) => 200 {"code":200,"msg":"管理员接口访问成功"}
可见:分页参数自动解析且 size 上限生效;无 Token 直接被 401 拦截,带 Token 才能拿到用户信息。
九、Day12 核心知识点总结
| 知识点 | 说明 |
|---|---|
| Depends 依赖注入 | 抽离公共逻辑,代码解耦、复用 |
| 函数式依赖 | 适合简单公共参数(分页、时间、筛选) |
| 类依赖 | 适合复杂封装、多工具方法场景 |
| 局部依赖 | 单个接口独立鉴权 |
| 全局路由依赖 | 整个模块统一鉴权拦截 |
| 依赖嵌套 | 登录校验 + 权限拦截,双层组合 |
十、下期预告
Day13:JWT动态令牌鉴权实战
告别固定 Token!实现登录生成 JWT、过期刷新、加密校验、无状态鉴权,企业生产级登录体系!