FastAPI中间件与路由

FastAPI的中间件与路由 -- pd的FastAPI笔记

文章目录

中间件基本结构

中间件是位于客户端和应用程序核心逻辑之间的软件层,用于:

  • 拦截请求和响应
  • 在请求到达路由处理程序之前进行处理
  • 在响应返回给客户端之前进行处理
python 复制代码
from fastapi import FastAPI
import time

app = FastAPI(debug=True)


@app.middleware("http")
async def custom_middleware(request, call_next):
    # 响应请求之前执行
    start_time = time.time()
    print(f'{start_time} custom_middleware called')
    response = await call_next(request)
    # 响应请求之后执行
    end_time = time.time()
    process_time = end_time - start_time
    print(f'{end_time} custom_middleware after called')
    response.headers["X-Process-Time"] = str(process_time)
    return response


@app.get('/user')
async def user():
    print(f'{time.time()} user called')
    return 'user'

if __name__ == '__main__':
    import uvicorn
    uvicorn.run("18中间件:app", host='127.0.0.1',
                port=8003, reload=True)

关键组件

  • request: Request :传入的请求对象
  • call_next :调用链中的下一个中间件/路由
  • 返回值:必须返回

中间件的执行顺序

  • 先注册的中间件在内层,后注册的中间件在外层。

请求阶段(从外到内)

  • 客户端发送 HTTP 请求。
  • 请求首先经过 最后注册的中间件(外层)。
  • 每个中间件调用 call_next(request) 后,请求进入 上一层中间件。
  • 最终请求到达 最先注册的中间件(内层),然后交给路由处理程序。

响应阶段(从内到外)

  • 路由处理程序生成响应。
  • 响应从 最先注册的中间件(内层)开始返回。
  • 每个中间件在 call_next(request) 之后执行后置逻辑。
  • 响应最终通过 最后注册的中间件(外层)返回给客户端。

中间件注册机制

在 FastAPI 中,除了使用 @app.middleware("http") 装饰器注册中间件外,还可以通过 app.add_middleware() 方法显式加载中间件。这两种方式的执行顺序规则是一致的:先注册的中间件在内层,后注册的中间件在外层

方式一:使用 @app.middleware("http") 装饰器

python 复制代码
from fastapi import FastAPI
import time

app = FastAPI(debug=True)

# 第一个注册的中间件(内层)
@app.middleware("http")
async def middleware_1(request, call_next):
    print("Middleware 1: 请求开始")
    response = await call_next(request)
    print("Middleware 1: 响应结束")
    return response

# 第二个注册的中间件(外层)
@app.middleware("http")
async def middleware_2(request, call_next):
    print("Middleware 2: 请求开始")
    response = await call_next(request)
    print("Middleware 2: 响应结束")
    return response

@app.get("/user")
async def user():
    print("Route Handler: 处理请求")
    return {"message": "Hello, User!"}

方式二:使用 app.add_middleware() 方法

python 复制代码
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
import time

app = FastAPI(debug=True)

# 定义中间件类
class Middleware1(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        print("Middleware 1: 请求开始")
        response = await call_next(request)
        print("Middleware 1: 响应结束")
        return response

class Middleware2(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        print("Middleware 2: 请求开始")
        response = await call_next(request)
        print("Middleware 2: 响应结束")
        return response

# 注册中间件(先注册的在内层,后注册的在外层)
app.add_middleware(Middleware1)  # 内层
app.add_middleware(Middleware2)  # 外层

@app.get("/user")
async def user():
    print("Route Handler: 处理请求")
    return {"message": "Hello, User!"}

🔁 执行流程对比

两种方式的执行顺序完全一致:

复制代码
客户端发送请求
       ↓
Middleware 2: 请求开始(外层)
       ↓
Middleware 1: 请求开始(内层)
       ↓
Route Handler: 处理请求
       ↓
Middleware 1: 响应结束(内层)
       ↓
Middleware 2: 响应结束(外层)
       ↓
客户端接收响应


⚠️ 注意事项

  1. 混合使用时的顺序

    • 如果同时使用 @app.middleware("http")app.add_middleware(),装饰器注册的中间件会排在 add_middleware() 注册的中间件之后。

    • 例如:

      python 复制代码
      app.add_middleware(Middleware1)  # 内层
      app.add_middleware(Middleware2)  # 外层
      
      @app.middleware("http")
      async def middleware_3(request, call_next):
          print("Middleware 3: 请求开始")
          response = await call_next(request)
          print("Middleware 3: 响应结束")
          return response

      执行顺序为:Middleware 3(最外层)→ Middleware 2Middleware 1(最内层)。

  2. 中间件类 vs 装饰器

    • BaseHTTPMiddleware 类更适合封装复杂逻辑。
    • 装饰器方式更简洁,适合轻量级中间件。

FastAPI跨域配置

同源策略(SOP)

同源策略是浏览器的一种安全机制,限制了一个源(origin)的网页如何与另一个源的资源进行交互。

同源策略防止恶意网站通过脚本(如JavaScript)未经授权访问其他网站的数据,例如窃取用户的敏感信息

但是:现代Web应用经常需要跨源请求!

CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种基于HTTP的机制,它允许服务器指示哪些其他源(域名、协议或端口)可以访问其资源,从而绕过浏览器的同源策略(Same-Origin Policy,SOP)限制

使用官方提供的CORS中间件

python 复制代码
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(debug=True)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"], # 或者指定前端域名'http://localhost:8080'
    allow_credentials=True, # 允许cookie
    allow_methods=["*"], # 允许的请求方法
    allow_headers=["*"], # 允许的请求头
)

@app.get("/info")
async def info():
    return {"message": "Hello World"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("19CORS中间件:app", host='127.0.0.1',
                port=8003, reload=True)

FastAPI路由划分

APIRouter 核心作用

  • 模块化架构:将大型应用拆分为独立功能模块
  • 路由分组:统一管理相关端点
  • 组织优化:解耦业务逻辑,提升可维护性
python 复制代码
from fastapi import APIRouter
app = FastAPI()

main_router = APIRouter(perfix="/api/v1")
user_router = APIRouter(tags=["用户应用"], perfix="/users")
item_router = APIRouter(tags=["商品应用"], perfix="/items")
# 将子路由器添加到主路由器
main_router.include_router(user_router)
main_router.include_router(item_router)
# 将主路由器添加到FastAPI实例中
app.include_router(main_router)

@user_router.get("/profile")
async def get_profile():
    pass

@item_router.get("/{item_id}")
async def get_item(item_id):
    pass

FastAPI项目结构

FastAPI 项目中,代码全部集中在单个文件中,虽然这种方式适合小型项目或原型开发,但在企业级应用中,将代码拆分为多个文件和目录是更优的实践,主要原因:

  • 职责分离:将应用的各个组成部分(如数据库配置、模型、模式、路由、中间件)分别放置在单独的文件或目录中,使代码结构更清晰,便于理解和维护
  • 提高代码可读性和可维护性:将代码拆分为小而专一的文件,开发者可以快速定位和修改特定功能,而无需在单一长文件中搜索
  • 增强可扩展性:模块化的结构便于扩展,当需要添加新功能(如新的模型或路由)时,只需在相应的目录中创建新文件,而不影响现有代码
  • 清晰的依赖管理:通过在每个文件中显式导入依赖,可以清楚地看到模块之间的关系,便于调试和理解代码逻辑
  • 便于团队协作:在企业开发中,多个开发者可能同时开发不同功能,拆分后的文件结构允许团队成员并行工作。例如,一个开发者修改用户路由,另一个开发者可以同时处理商品路由,互不干扰。
  • 符合企业级项目规范:企业级项目通常需要遵循标准化的代码组织规范,拆分文件是行
    业最佳实践之一。这种结构也便于集成自动化测试、CI/CD 流程和代码审查。

按软件功能目录结构示例

py 复制代码
project/
├── main.py       # 主入口文件,初始化 FastAPI 应用并集成路由、中间件
├── config/
│   └── database.py      # 数据库配置和 Tortoise-ORM 初始化
├── models/
│   └── user.py          # 数据模型定义(如 User 模型)
├── schemas/
│   └── user.py          # Pydantic 模式定义,用于输入输出验证
├── routers/
│   ├── __init__.py      # 标记 routers 为 Python 包
│   ├── user.py          # 用户相关的 API 路由
│   └── item.py          # 商品相关的 API 路由 
└── middleware/
    └── user_middleware.py  # 自定义中间件逻辑

按业务模块目录结构示例

py 复制代码
project/
├── main.py       # 主入口文件,初始化 FastAPI 应用并集成路由、中间件
├── config/
│   └── database.py      # 数据库配置和 Tortoise-ORM 初始化
├── modules/
│   ├── user/
│   │   ├── __init__.py    # 模块入口文件,标记 user 为包
│   │   ├── models.py      # 用户模型定义
│   │   ├── schemas.py     # 用户数据结构定义
│   │   └── routes.py      # 用户路由定义
│   │── item/
│   │   ├── __init__.py    # 模块入口文件,标记 item 为包
│   │   ├── models.py      # 商品模型定义
│   │   ├── schemas.py     # 商品数据结构定义
│   │   └── routes.py      # 商品路由定义
└── middleware/
    └── user_middleware.py  # 自定义中间件逻辑

其他类型的项目结构

例如采取标准FastAPI架构的设计:

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    API 路由层 (api/)                         │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  FastAPI 路由定义 │ 请求验证 │ 响应格式化 │ 文档生成   │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                   业务逻辑层 (services/)                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ 业务规则处理 │ 事务管理 │ 错误处理 │ 数据转换          │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                   数据访问层 (repositories/)                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ 数据库操作封装 │ 查询优化 │ 缓存管理 │ 事务控制         │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                   数据源层                                   │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ MySQL 数据库 │ Redis 缓存 │ 外部 API 服务 │ 文件存储   │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
py 复制代码
project/
├── src/                           # 源代码目录
│   └── project_name_api/          # 主应用包
│       ├── api/                   # API 路由层
│       │   └── endpoints/         # 各功能模块的API端点
│       │       ├── files.py       # 文件服务API
│       │       ├── ...
│       │       └── users.py       # 用户服务API
│       ├── core/                  # 核心组件层
│       │   ├── config.py          # 配置管理
│       │   ├── database.py        # 数据库连接
│       │   ├── ...                
│       │   ├── models.py          # 数据模型
│       │   └── redis.py           # Redis连接
│       ├── integrations/          # 外部服务集成层
│       │   ├── llm_api.py         # 大模型接口
│       │   └── ...
│       ├── middleware/            # 中间件层
│       │   ├── logging.py         # 日志中间件
│       │   └── ...
│       ├── repositories/          # 数据访问层
│       │   ├── file_repo.py       # 文件服务的CRUD操作
│       │   └── user_repo.py       # 用户服务的CRUD操作
│       ├── schemas/               # Pydantic 数据模型
│       │   ├── file_schema.py     # 文件服务的数据验证模型
│       │   └── user_schema.py     # 用户服务的数据验证模型
│       ├── services/              # 业务逻辑层
│       │   ├── file_service.py    # 文件服务的业务逻辑
│       │   └── user_service.py    # 用户服务的业务逻辑
│       ├── scheduler/             # 定时任务调度
│       │   └── scheduler_email.py # 定时邮件任务
│       ├── utils/                 # 工具函数
│       └── main.py                # 应用入口点
├── lib/                           # 本地依赖包
│   ├── service_a-0.1.0.tar.gz     # 服务A(身份认证相关)
│   └── sdk_b-0.1.1.tar.gz         # SDK B(文档处理相关)
├── sql/                           # 数据库脚本
│   ├── schema_full.sql            # 完整表结构
│   ├── seed_data.sql              # 初始数据
│   └── migration_v1.sql           # 表结构变更
├── scripts/                       # 脚本文件
│   └── utility_script.py          # 工具脚本示例
├── tests/                         # 测试代码
├── config.yaml                    # 应用配置文件
└── pyproject.toml                 # 项目配置和依赖管理
相关推荐
会算数的⑨9 小时前
Kafka知识点问题驱动式的回顾与复习——(一)
分布式·后端·中间件·kafka
小白不想白a19 小时前
消息队列--包括面试常考题/运维监控指标
中间件
金刚猿19 小时前
01_虚拟机中间件部署_root 用户安装 docker 容器,配置非root用户权限
docker·中间件·容器
闲人编程1 天前
使用FastAPI和WebSocket构建高性能实时聊天系统
websocket·网络协议·网络编程·fastapi·持久化·实时聊天·codecapsule
Hello.Reader1 天前
Rocket Fairings 实战把全局能力做成“结构化中间件”
中间件·rust·rocket
失忆爆表症1 天前
01_项目搭建指南:从零开始的 Windows 开发环境配置
windows·postgresql·fastapi·milvus
PD我是你的真爱粉1 天前
FastAPI使用tortoiseORM
数据库·fastapi
岁岁种桃花儿2 天前
Kafka从入门到上天系列第一篇:kafka的安装和启动
大数据·中间件·kafka
玄同7652 天前
Python 后端三剑客:FastAPI/Flask/Django 对比与 LLM 开发选型指南
人工智能·python·机器学习·自然语言处理·django·flask·fastapi