当前位置: 首页 > news >正文

【FastAPI】中间件

【FastAPI】中间件

  • 一、概述
  • 二、作用
    • 2.1 日志记录与监控
    • 2.2 身份验证与授权
    • 2.3 CORS(跨域资源共享)
    • 2.4 Gzip压缩
    • 2.5 会话管理
    • 2.6 自定义功能
    • 2.7 执行顺序
  • 三、 总结
  • 四、相关链接

一、概述

FastAPI的中间件提供了一种强大的机制,允许开发者在请求被处理之前以及响应返回给客户端之前对它们进行拦截和操作
这为实现诸如日志记录、身份验证、性能监控、错误处理等功能提供了极大的灵活性。

二、作用

2.1 日志记录与监控

中间件可以用来记录所有进入系统的HTTP请求及其对应的响应
例如,你可以创建一个中间件来测量每个请求的处理时间,并将这个信息作为自定义头部X-Process-Time添加到响应中。
这对于性能分析非常有用,因为它可以帮助你了解哪些请求消耗了较多的时间,从而优化系统性能。

import json
import logging
import os
import time
from fastapi import FastAPI, Request, Response
from starlette.middleware.base import BaseHTTPMiddleware# 设置日志格式和级别
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO').upper()
logging.basicConfig(level=LOG_LEVEL,format='%(asctime)s - %(levelname)s - %(message)s'
)class LoggingMiddleware(BaseHTTPMiddleware):async def dispatch(self, request: Request, call_next) -> Response:start_time = time.perf_counter()response = await call_next(request)process_time = time.perf_counter() - start_timelog_data = {"client_ip": request.client.host,"method": request.method,"path": request.url.path,"status_code": response.status_code,"process_time": f"{process_time:.4f}s","timestamp": datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')}# 根据环境变量决定是否打印详细的日志内容if LOG_LEVEL == 'DEBUG':log_data["headers"] = dict(request.headers)log_data["query_params"] = dict(request.query_params)logging.info(json.dumps(log_data))return responseapp = FastAPI()
app.add_middleware(LoggingMiddleware)@app.get("/")
async def root():return {"message": "Hello World"}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000, log_level=LOG_LEVEL.lower())

测试方法:

  • 启动应用:python main.py
  • 使用浏览器或Postman访问http://localhost:8000/
  • 查看命令行输出的日志信息,确认日志已正确记录。
2024-12-25 19:21:56,568 - INFO - {"client_ip": "127.0.0.1", "method": "GET", "path": "/", "status_code": 200, "process_time": "0.0003s", "timestamp": "2024-12-25 19:21:56"}
INFO:     127.0.0.1:53956 - "GET / HTTP/1.1" 200 OK
  1. 设置日志配置
    这里设置了日志记录器的基本配置。通过环境变量 LOG_LEVEL 来控制日志级别,默认为 INFO
    这意味着除非设置了更低的日志级别(如 DEBUG),否则不会输出调试信息。
    此外,还指定了日志格式,包括时间戳日志级别消息内容
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO').upper()
logging.basicConfig(level=LOG_LEVEL,format='%(asctime)s - %(levelname)s - %(message)s'
)
  1. 自定义中间件 LoggingMiddleware
    中间件是用来处理请求和响应之间逻辑的理想场所。
    在这个例子中,LoggingMiddleware 被用来捕获每次请求的时间点,然后在请求完成后计算处理所需的时间,并将相关信息以 JSON 格式记录下来。这有助于监控 API 性能并诊断问题。
  • 开始计时:使用 time.perf_counter() 获取当前时间。
  • 调用下一个处理器:通过 await call_next(request) 将请求传递给下一个中间件或最终的路由处理器。
  • 结束计时:再次调用 time.perf_counter() 计算总耗时。
  • 构建日志数据:创建一个字典 log_data 包含客户端 IP、HTTP 方法、路径、状态码及处理时间等信息。
  • 条件性添加更多信息:如果日志级别为 DEBUG,还会额外添加请求头和查询参数到日志中,以便于更详细的调试。
  • 记录日志:最后,将构造好的日志数据转换成 JSON 字符串并通过 logging.info() 输出。
  1. 定义 FastAPI 应用程序
    这里创建了一个 FastAPI 实例 app 并注册了之前定义的 LoggingMiddleware。随后定义了一个 GET 方法对应的路由 /,当访问根路径时会返回一个 JSON 响应,其中包含了一条简单的欢迎信息 “Hello World”。
app = FastAPI()
app.add_middleware(LoggingMiddleware)@app.get("/")
async def root():return {"message": "Hello World"}
  1. 启动 Uvicorn 服务器
    这段代码确保只有当脚本作为主模块运行时才会启动 Uvicorn 服务器。
    Uvicorn 是一个用于运行 ASGI 应用的异步服务器,它将托管 FastAPI 应用程序,并监听指定的主机地址 (0.0.0.0) 和端口 (8000)。
    同时,也将日志级别传递给 Uvicorn,以保证一致性。
if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000, log_level=LOG_LEVEL.lower())

2.2 身份验证与授权

通过中间件,可以在请求到达路由处理器之前检查用户的身份验证状态。

  • 如果请求未通过身份验证,则可以直接返回401 Unauthorized响应;
  • 如果通过了身份验证但没有足够的权限访问特定资源,则可以返回403 Forbidden响应。

这种方式确保了只有经过适当授权的用户才能访问受保护的API端点。

from datetime import datetime, timedelta
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from pydantic import BaseModel
from typing import Optional
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.base import BaseHTTPMiddlewareSECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")class Token(BaseModel):access_token: strtoken_type: strclass TokenData(BaseModel):username: Optional[str] = Nonefake_users_db = {"johndoe": {"username": "johndoe","full_name": "John Doe","email": "johndoe@example.com","hashed_password": "fakehashedsecret","disabled": False,}
}def verify_password(plain_password, hashed_password):return plain_password == hashed_passworddef get_user(db, username: str):if username in db:user_dict = db[username]return UserInDB(**user_dict)def authenticate_user(fake_db, username: str, password: str):user = get_user(fake_db, username)if not user:return Falseif not verify_password(password, user.hashed_password):return Falsereturn userdef create_access_token(data: dict, expires_delta: Optional[timedelta] = None):to_encode = data.copy()if expires_delta:expire = datetime.utcnow() + expires_deltaelse:expire = datetime.utcnow() + timedelta(minutes=15)to_encode.update({"exp": expire})encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)return encoded_jwtasync def get_current_user(token: str = Depends(oauth2_scheme)):credentials_exception = HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="Could not validate credentials",headers={"WWW-Authenticate": "Bearer"},)try:payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])username: str = payload.get("sub")exp: int = payload.get("exp")if username is None or exp is None:raise credentials_exceptionif datetime.utcfromtimestamp(exp) < datetime.utcnow():raise credentials_exceptiontoken_data = TokenData(username=username)except JWTError:raise credentials_exceptionuser = get_user(fake_users_db, username=token_data.username)if user is None:raise credentials_exceptionreturn userclass AuthMiddleware(BaseHTTPMiddleware):async def dispatch(self, request: Request, call_next) -> Response:token = request.headers.get('Authorization')if token:try:await get_current_user(token.split()[1])  # Assuming 'Bearer <token>'except HTTPException as e:return JSONResponse(status_code=e.status_code, content={"detail": e.detail})else:return JSONResponse(status_code=401, content={"detail": "Unauthorized"})return await call_next(request)app = FastAPI()
app.add_middleware(AuthMiddleware)@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):user = authenticate_user(fake_users_db, form_data.username, form_data.password)if not user:raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="Incorrect username or password",headers={"WWW-Authenticate": "Bearer"},)access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)access_token = create_access_token(data={"sub": user.username}, expires_delta=access_token_expires)return {"access_token": access_token, "token_type": "bearer"}@app.get("/users/me", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_user)):return current_userif __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)

2.3 CORS(跨域资源共享)

为了使Web应用程序能够从不同的域名发出AJAX请求,你需要配置CORS策略。
FastAPI内置了CORSMiddleware,它允许你轻松地设置允许的来源、方法、头等参数,从而实现安全且灵活的跨域通信2。
下面是一个支持CORS配置的应用程序示例,允许来自特定源的请求,并处理预检请求(Preflight Requests)。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddlewareapp = FastAPI()origins = ["http://localhost:3000",
]app.add_middleware(CORSMiddleware,allow_origins=origins,allow_credentials=True,allow_methods=["*"],allow_headers=["*"],expose_headers=["X-Total-Count"],max_age=86400
)@app.get("/")
async def root():return {"message": "Hello World"}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)

2.4 Gzip压缩

对于需要提高传输效率的情况,可以使用Gzip中间件来压缩响应内容。这样不仅可以减少网络带宽占用,还能加快页面加载速度,特别是在移动设备上效果尤为明显2。

from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddlewareapp = FastAPI()app.add_middleware(GZipMiddleware,minimum_size=1000,compresslevel=9
)@app.get("/")
async def root():large_text = "a" * 1024  # 创建一个较大的字符串用于测试压缩效果return {"message": large_text}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)

2.5 会话管理

借助于SessionMiddleware,你可以方便地管理用户的会话信息。
比如,在登录后存储用户的认证令牌或其他临时数据,这些数据可以通过cookie传递并在后续请求中读取2。

// A code block
var foo = 'bar';

2.6 自定义功能

除了上述提到的功能之外,中间件还可以用于执行其他任何你想要在每次请求或响应时运行的任务。例如,你可以编写一个中间件来解析并标准化查询字符串参数,或者根据某些条件修改请求体中的内容7。

// A code block
var foo = 'bar';

2.7 执行顺序

值得注意的是,当多个中间件同时存在于一个应用中时,它们按照“后进先出”的原则被执行。
也就是说,最后添加的那个中间件会在最前面被调用,并且最先完成其工作之后再交给下一个中间件继续处理。

// A code block
var foo = 'bar';

三、 总结

总之,FastAPI中的中间件是一个非常强大且灵活的工具,它使得开发者能够在不改变核心业务逻辑的前提下,轻松地向应用程序添加各种辅助性功能和服务
无论是为了增强安全性、改善用户体验还是简化开发流程,合理利用中间件都能带来显著的好处。

四、相关链接

【FastAPI】日志
【FastAPI】中间件
【FastAPI】简介
【FastAPI】BaseModel类

相关文章:

【FastAPI】中间件

【FastAPI】中间件 一、概述二、作用2.1 日志记录与监控2.2 身份验证与授权2.3 CORS&#xff08;跨域资源共享&#xff09;2.4 Gzip压缩2.5 会话管理2.6 自定义功能2.7 执行顺序 三、 总结四、相关链接 一、概述 FastAPI的中间件提供了一种强大的机制&#xff0c;允许开发者在…...

5个实用的设计相关的AI网站

在这个日新月异的数字时代&#xff0c;我们不断面临着新的挑战和机遇。随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;越来越多的AI工具开始融入到设计相关的工作流程中&#xff0c;极大地提升了工作效率和创作能力。今天&#xff0c;我非常兴奋地向大家介…...

STL 六大组件

C STL&#xff08;标准模板库&#xff09;主要由六大组件构成&#xff0c;它们相互协作&#xff0c;为C程序员提供了功能强大且高效的通用数据结构和算法工具&#xff0c;以下是对这六大组件的详细介绍&#xff1a; 1. 容器&#xff08;Containers&#xff09; 概述&#xff…...

Python选择题训练工具:高效学习、答题回顾与音频朗读一站式体验

一、引言 随着人工智能技术的不断进步&#xff0c;传统的教学方式已经逐渐向智能化、互动化转变。在众多英语测试题型中&#xff0c;选择题作为一种高效的方式被广泛应用于各类培训与考试中。为了帮助学生高效学习与自测&#xff0c;本篇文章将采用Python编写一款基于 Python …...

Python实现机器学习驱动的智能医疗预测模型系统的示例代码框架

以下是一个使用Python实现机器学习驱动的智能医疗预测模型系统的示例代码框架。这个框架涵盖了数据收集&#xff08;爬虫&#xff09;、数据清洗和预处理、模型构建&#xff08;决策树和神经网络&#xff09;以及模型评估的主要步骤。 1. 数据收集&#xff08;爬虫&#xff09…...

AWS Certified AI Practitioner 自学考试心得

学习目标&#xff1a; 考取 AWS Certified AI Practitioner 那什么是 AWS Certified AI Practitioner 认证 是基础级的认证 比较简单 — 学习内容&#xff1a; 1. AWS网站自学网站 极客时间免费课程&#xff1a;http://gk.link/a/12sJL 配合极客时间课程的章节测试检验自…...

JQ中的each()方法与$.each()函数的使用区别

介绍 jquery里的 each() 是一个强大的遍历工具&#xff0c;用于迭代集合中的元素&#xff0c;并为每个元素执行指定的函数‌。它既可以用于遍历 jQuery对象集合&#xff0c;也可以用于遍历普通的数组或对象。 each()对象遍历 语法&#xff1a; $(selector).each(function(in…...

滚珠丝杆与直线导轨的区别

滚珠丝杆和直线导轨是两种常见的精密机械传动装置&#xff0c;它们的作用是实现直线运动&#xff0c;在工业自动化和精密机械领域中扮演着重要的角色。尽管它们都用于实现直线运动&#xff0c;但它们在结构以及性能特点上还是存在一些区别&#xff1a; 一、工作原理 1、滚珠丝…...

【Ovis】Ovis1.6的本地部署及推理

Ovis简介 Ovis是阿里国际AI团队开源的多模态大模型&#xff0c;看新闻介绍效果不错&#xff0c;在多个场景的测试下都能达到SOTA&#xff0c;其中的Ovis1.6-Gemma2-9B在30B参数以下的模型中取得了综合排名第一&#xff0c;赶超MiniCPM-V-2.6等行业优秀大模型。所以我也部署一个…...

C语言结构体位定义(位段)的实际作用深入分析

1、结构体位段格式 struct struct_name {type [member_name] : width; };一般定义结构体&#xff0c;成员都是int、char等类型&#xff0c;占用的空间大小是固定的在成员名称后用冒号来指定位宽&#xff0c;可以指定每个成员所占用空间&#xff0c;并且也不用受结构体成员起始…...

儿童影楼管理系统:基于SSM的创新设计与功能实现

3.1系统的需求分析 需求分析阶段是设计系统功能模块的总方向&#xff0c;可以这样来说&#xff0c;系统的整个的开发流程以及设计进度&#xff0c;基本上都是以需求分析为基本依据的[10]。需求分析阶段可以确定系统的基本功能设计&#xff0c;以及在最后的系统验收阶段&#xf…...

青蛇人工智能学家

青蛇人工智能学家 青蛇&#xff0c;是蓝星上&#xff0c;最出名的人工智能学家。 在蓝星上&#xff0c;大家都知道&#xff0c;青蛇人工智能学家&#xff0c;最大的爱好&#xff0c;是美食。 青蛇人工智能学家&#xff0c;对自己的食物&#xff0c;非常在意&#xff0c;对自己的…...

uniapp+vue 前端防多次点击表单,防误触多次请求方法。

最近项目需求写了个uniappvue前端H5,有个页面提交表单的时候发现会有用户乱点导致数据库多条重复脏数据。故需要优化&#xff0c;多次点击表单只请求一次。 思路: 直接调用uni.showToast&#xff0c;点完按钮跳一个提交成功的提示。然后把防触摸穿透mask设置成true就行&#…...

【ES6复习笔记】rest参数(7)

什么是 rest 参数&#xff1f; rest 参数是 ES6 引入的一个特性&#xff0c;它允许我们将一个不定数量的参数表示为一个数组。使用 rest 参数可以更方便地处理函数的参数&#xff0c;尤其是在参数数量不确定的情况下。 如何使用 rest 参数&#xff1f; 在函数定义中&#xf…...

Hive SQL 窗口函数 `ROW_NUMBER() ` 案例分析

一文彻底搞懂 ROW_NUMBER() 和 PARTITION BY 1. 引言 在处理大规模数据集时&#xff0c;Hive SQL 提供了强大的窗口函数&#xff08;Window Function&#xff09;&#xff0c;如 ROW_NUMBER()&#xff0c;用于为结果集中的每一行分配唯一的行号。当与 PARTITION BY 和 ORDER …...

前端mock数据 —— 使用Apifox mock页面所需数据

前端mock数据 —— 使用Apifox 一、使用教程二、本地请求Apifox所mock的接口 一、使用教程 在首页进行新建项目&#xff1a; 新建项目名称&#xff1a; 新建接口&#xff1a; 创建json&#xff1a; 请求方法&#xff1a; GET。URL&#xff1a; api/basis。响应类型&#xf…...

车载U盘制作教程:轻松享受个性化音乐

车载U盘播放音乐相较于蓝牙播放具有一些明显的优势&#xff0c;这些优势主要体现在音质、稳定性、音乐管理以及兼容性等方面。以下是车载U盘播放音乐的一些优势&#xff1a; 音质更佳&#xff1a;车载U盘播放音乐时&#xff0c;音乐文件是直接被解码并播放的&#xff0c;这意味…...

springboot 3 websocket react 系统提示,选手实时数据更新监控

构建一个基于 Spring Boot 3 和 WebSocket 的实时数据监控系统&#xff0c;并在前端使用 React&#xff0c;可以实现选手实时数据的更新和展示功能。以下是该系统的核心设计和实现思路&#xff1a; 1. 系统架构 后端 (Spring Boot 3): 提供 WebSocket 服务端&#xff0c;处理…...

现代图形API综合比较:Vulkan DirectX Metal WebGPU

Vulkan、DirectX、Metal 和 WebGPU 等低级图形 API 正在融合为类似于当前 GPU 构建方式的模型。 图形处理单元 (GPU) 是异步计算单元&#xff0c;可以处理大量数据&#xff0c;例如复杂的网格几何形状、图像纹理、输出帧缓冲区、变换矩阵或你想要计算的任何数据。 NSDT工具推荐…...

【Hot100刷题计划】Day04 栈专题 1~3天回顾(持续更新)

LeetCode Hot 100 是最常被考察的题目集合&#xff0c;涵盖了面试中常见的算法和数据结构问题。刷 Hot100可以让你在有限的时间内集中精力解决最常考的问题。鼓励大家不仅要写出代码&#xff0c;最好理解问题的本质、优化解法和复杂度分析。遇到问题要多交流多求问多分享&#…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...