FastApi_03_中间件 VS 依赖注入

FastAPI 中间件 (Middleware) vs 依赖注入 (Dependency Injection)

虽然两者都可以用于将通用逻辑从主要业务代码中解耦出来,但它们在工作方式、作用范围和适用场景上有显著区别。

1. 核心概念与执行时机

特性 中间件 (Middleware) 依赖注入 (Dependency Injection)
核心概念 一个拦截器/过滤器,包裹着整个应用或特定路径。 一个函数或可调用对象,用于在请求处理期间提供(注入)共享资源或逻辑。
执行时机 在每个请求到达任何路由处理函数之前执行,以及在每个响应返回给客户端之后执行(洋葱模型)。 在特定的路径操作函数执行之前,当该函数声明了 Depends(依赖项) 时才执行。

2. 作用范围

特性 中间件 (Middleware) 依赖注入 (Dependency Injection)
作用范围 全局。影响应用接收到的每一个请求和发出的每一个响应。 局部。可以应用于单个路径操作函数、单个路径装饰器 (dependencies=[...])、APIRouter 或通过 Depends() 在函数内声明。

3. 数据流向与返回

特性 中间件 (Middleware) 依赖注入 (Dependency Injection)
数据返回给路由 通常不直接返回数据给路径操作函数(虽然可以通过 request.state 传递少量信息)。 直接返回数据给路径操作函数,作为函数的参数。
访问 Request/Response 可以访问和修改 Request 对象(在处理函数之前)和 Response 对象(在处理函数之后)。 不能直接访问 RequestResponse(除非显式传递)。

4. 应用场景

场景 中间件 (Middleware) 依赖注入 (Dependency Injection)
日志记录 ✅ 记录所有请求的进入和离开时间、状态码等。 -
CORS (跨域资源共享) ✅ 全局设置允许的源、方法、头等。 -
请求/响应修改 ✅ 统一添加响应头、修改请求体(较少见)。 -
性能监控 ✅ 计算每个请求的处理时间。 -
全局认证/授权检查 ✅ 检查每个请求的令牌(虽然也可以用依赖注入实现)。 ✅ 检查特定路由的令牌。
数据库连接/会话获取 - ✅ 为需要数据库连接的特定路由提供连接。
参数校验与预处理 - ✅ 为特定路由校验查询参数、路径参数或请求体。
资源获取 - ✅ 为特定路由获取用户信息、配置等。

代码示例

示例 1: 中间件 - 全局日志记录

这个中间件会记录每一个进入应用的请求和响应时间。

python 复制代码
from fastapi import FastAPI, Request
from fastapi.middleware.base import BaseHTTPMiddleware
import time
import logging

# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI()

#构造中间件的日志处理函数
class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):   #(request: Request, call_next) 固定参数
        # --- 请求前处理 ---
        start_time = time.time()
        logger.info(f"MIDDLEWARE: Received {request.method} {request.url.path}")

        # 调用下一个中间件或路由处理函数
        response = await call_next(request)     

        # --- 响应后处理 ---
        process_time = time.time() - start_time
        logger.info(f"MIDDLEWARE: Response status {response.status_code} in {process_time:.4f}s for {request.url.path}")

        return response

# 注册中间件
app.add_middleware(LoggingMiddleware)

@app.get("/")
async def read_root():
    logger.info("ROUTE: Processing / route") #控制台输出路由信息
    return {"message": "Hello World"}

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    logger.info(f"ROUTE: Processing /users/{user_id} route") #控制台输出路由信息
    return {"user_id": user_id}

# 访问 / 或 /users/123 都会触发 LoggingMiddleware
# 控制台输出示例:
# INFO:    MIDDLEWARE: Received GET /users/123                         
# INFO:    ROUTE: Processing /users/123 route
# INFO:    MIDDLEWARE: Response status 200 in 0.0012s for /users/123

示例 2: 依赖注入 - 局部数据库会话获取

这个依赖项只为需要数据库会话的路径操作函数提供服务。

python 复制代码
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel

app = FastAPI()


# 路径操作函数 1:不需要数据库会话,不会触发db_connect()依赖
@app.get("/status")
async def get_status():
    logger.info("ROUTE: 没有使用依赖注入")
    return {"status": "ok"}

# 路径操作函数 2:需要数据库会话,触发db_connect()依赖

# 需要引入Depends
# 定义数据库链接函数,用于重复使用
#@app.post("/db")
async def db_connect(sqlText:str):
    # 1. 定义数据库连接参数
    config = {
        'host': 'localhost',  # MySQL 服务器地址,通常是 'localhost' 或 IP
        'port': 3307,  # MySQL 默认端口
        'user': 'root',  # 你的 MySQL 用户名
        'password': 'root',  # 你的 MySQL 密码
        'database': 'world',  # 你要连接的数据库名
        'charset': 'utf8mb4'  # 推荐使用 utf8mb4 支持完整的 UTF-8 字符集
    }

    try:
        # 2. 建立数据库连接
        connection = pymysql.connect(**config)
        print("成功连接到数据库!")

        # 3. 创建游标对象
        with connection.cursor() as cursor:  # 使用 with 确保游标在使用后关闭
            # 4. 执行 SQL 查询
            cursor.execute(sqlText)

            # 5. 获取查询结果
            result = cursor.fetchall()
            print(f"数据库版本: {result[0]}")   

    except pymysql.MySQLError as e:
        print(f"数据库错误: {e}")
    finally:
        # 6. 关闭数据库连接
        if connection:
            connection.close()
            print("数据库连接已关闭。")
    return result

#接口路由函数,使用依赖注入Depends(函数名称)
@app.post("/getBookList")
async def getBookList(con=Depends(db_connect)): # 注入数据库连接函数,并获取返回的数据
	print(type(con))    #获取注入函数返回的对象con
    num :int =0
    ls :list = []
    while num <10:
        num += 1
        print(con[num])
        ls.append(con[num])
    return  ls
相关推荐
二哈喇子!4 小时前
Java Web项目怎么创建 & 没有出现web.xml的解决方法
java·web·web.xml
一起养小猫4 小时前
LeetCode100天Day13-移除元素与多数元素
java·算法·leetcode
小北方城市网4 小时前
Spring Security 认证授权实战(JWT 版):从基础配置到权限精细化控制
java·运维·python·微服务·排序算法·数据库架构
青槿吖4 小时前
Java 集合操作:HashSet、LinkedHashSet 和 TreeSet
java·开发语言·jvm
刘联其4 小时前
Prism Region注册父子区域 子区域初始化导航没生效解决
java·开发语言
二哈喇子!4 小时前
controller & service & dao之间的关系
java·eclipse·intellij-idea
TT哇4 小时前
IDEA压缩空的软件包
java·ide·intellij-idea
Vv1997_4 小时前
基于java.awt 绘制 自定义图片算式验证码
java·开发语言·python
独自破碎E4 小时前
推送错了仓库的解决办法
java·开发语言