五、Python框架篇: FastApi-中间件

1.介绍

1.1 官网介绍

"中间件"是一个函数,它在每个请求被特定的路径操作处理之前,以及在每个响应返回之前工作.

  • 它接收你的应用程序的每一个请求.

  • 然后它可以对这个请求做一些事情或者执行任何需要的代码.

  • 然后它将请求传递给应用程序的其他部分 (通过某种路径操作).

  • 然后它获取应用程序生产的响应 (通过某种路径操作).

  • 它可以对该响应做些什么或者执行任何需要的代码.

  • 然后它返回这个 响应.

1.2 中间件工作示意图

 

1.3 官方使用示例

import time
from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response

 

2. 快速使用

从官方示例可以看出,中间件函数要和FastAPI实例在一个文件才能通过注解的方式,这种虽然使用起来比较简单,但是不太合适扩展和项目结构管理,下面是通过函数add_middleware来注册中间件。

2.1 创建中间件

在包 middleware下,并新增文件usetime_middleware.py,文件内容如下:

import time
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response

class UseTimeMiddleware(BaseHTTPMiddleware):
    """ 计算耗时中间件"""
    def __init__(self, app):
        super().__init__(app)

    async def dispatch(self, request: Request, call_next) -> Response:
        """ 请求耗时 """
        start_time = time.time()
        # 调用下一个中间件或路由处理函数
        result = await call_next(request)
        process_time = time.time() - start_time
        result.headers["X-Process-Time"] = str(process_time)
        return result

注意:我们定义的中间件类UseTimeMiddleware要继承基础类BaseHTTPMiddleware

2.2 封装注册函数

在包 middleware/__init__.py引用,并封装统一注册方法:

from fastapi import FastAPI
from .usetime_middleware import UseTimeMiddleware

def registerMiddlewareHandle(server: FastAPI):
    # 添加耗时请求中间件
    server.add_middleware(UseTimeMiddleware)

2.3 调用注册函数

修改main.py文件,修改内容如下:

from app import errors, middleware

# 实例化
server = FastAPI(redoc_url=None, docs_url="/apidoc", title="FastAPI学习")

# # 注册中间件
middleware.registerMiddlewareHandle(server)

2.4 添加路由

修改 router/demo_router.py文件,新增内容如下:

import time
import random

@router.get("/middle/useTime")
async def middleUseTime() -> response.HttpResponse:
    """
    中间件使用-演示
    """
    # 随机暂停时间
    seconds = random.randint(500, 5000) / 1000
    print("暂停时间:", seconds)
    time.sleep(seconds)
    return response.ResponseSuccess(seconds)

2.5 验证

URL:http://localhost:8000/middle/useTime

返回值:

{"code":200,"msg":"处理成功","data":4.738}

3.多中间件顺序

3.1 创建多个中间件

(1)中间件一

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
class TestMiddleware(BaseHTTPMiddleware):
    
    """ 测试顺序中间件"""
    def __init__(self, app):
        super().__init__(app)

    async def dispatch(self, request: Request, call_next) -> Response:
        print("调用-中间件-TestMiddleware---before")
        # 调用下一个中间件或路由处理函数
        result = await call_next(request)
        print("调用-中间件-TestMiddleware---after")
        return result

(2)中间件二

class TokenMiddleware(BaseHTTPMiddleware):

    """ token验证中间件 """
    def __init__(self, app):
        super().__init__(app)

    async def dispatch(self, request: Request, call_next) -> Response:
        token = request.headers.get("X-Token", "")
        print("调用-token验证中间件-TokenMiddleware---before", token)
        result = await call_next(request)
        print("调用-token验证中间件-TokenMiddleware---after", token)
        return result

3.2 注册

from fastapi import FastAPI
from .usetime_middleware import UseTimeMiddleware
from .test_middleware import TestMiddleware,TokenMiddleware

def registerMiddlewareHandle(server: FastAPI):
    # 添加token验证中间件
    server.add_middleware(TokenMiddleware)
    # 添加耗时请求中间件
    server.add_middleware(UseTimeMiddleware)
    # 测试
    server.add_middleware(TestMiddleware)

3.3 请求验证

URL:http://localhost:8000/middle/useTime

返回结果:

D:\devpinstall\anaconda3\envs\dbgpt_env\python.exe F:\pyproject\fastApiProject\main.py
<class 'pydantic.main.BaseModel'>
INFO:     Started server process [16372]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
调用-中间件-TestMiddleware---before
调用-token验证中间件-TokenMiddleware---before
暂停时间: 2.14
调用-token验证中间件-TokenMiddleware---after
调用-中间件-TestMiddleware---after
INFO:     127.0.0.1:52518 - "GET /middle/useTime HTTP/1.1" 200 OK
调用-中间件-TestMiddleware---before
调用-token验证中间件-TokenMiddleware---before
暂停时间: 4.841
调用-token验证中间件-TokenMiddleware---after
调用-中间件-TestMiddleware---after
INFO:     127.0.0.1:51024 - "GET /middle/useTime HTTP/1.1" 200 OK

总结: 中间件的执行顺序和注册顺序,正好是相反的;先注册的后执行

3.4 add_middleware函数

def add_middleware(self, middleware_class: type, **options: typing.Any) -> None:
        if self.middleware_stack is not None:  # pragma: no cover
            raise RuntimeError("Cannot add middleware after an application has started")
         # 发现每次调用,都会把最后一个中间件放在 索引=0的位置,即最前面
        self.user_middleware.insert(0, Middleware(middleware_class, **options))

3.5 优化注册函数

add_middleware函数通过栈的方式(后进先出)注册中间件,跟我们的思维相反,如果注册的中间件多的话,排查问题时,很容易被惯性思维误导,下面优化了下注册函数: 先注册的先执行

from fastapi import FastAPI
from .usetime_middleware import UseTimeMiddleware
from .token_middleware import TokenMiddleware
from .test_middleware import TestMiddleware

# 定义注册顺序
middlewareList = [
    UseTimeMiddleware,  # 添加耗时请求中间件
    TokenMiddleware,  # 添加token验证中间件
    TestMiddleware  # 测试中间件
]

def registerMiddlewareHandle(server: FastAPI):
    # 倒序中间件
    middlewareList.reverse()
    # 遍历注册
    for _middleware in middlewareList:
        server.add_middleware(_middleware)

4.内置中间件

4.1 常用中间件

框架也提供了一些常用的的内置中间件

HTTPSRedirectMiddleware:  将 HTTP 请求重定向到 HTTPS。这个中间件会检查请求的协议,如果是 HTTP,则自动将请求重定向到相应的 HTTPS 地址;

TrustedHostMiddleware: 强制所有传入请求都具有正确设置的 Host 标头,以防止 HTTP 主机标头攻击。

GZipMiddleware: 用于在响应中压缩内容,以减小传输大小。这有助于提高应用程序的性能,特别是在处理大量文本或数据时。

CORSMiddleware: 用于处理跨域资源共享(CORS)请求。CORS 是一种浏览器机制,允许 Web 页面从不同的域请求不同域的资源。

4.2 CORSMiddleware使用

跨域中间件应该是我们常用的一种中间件,具体使用示例如下:

# 导入
from fastapi.middleware.cors import CORSMiddleware

# 注册
server.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],  # 允许的来源,可以是字符串、字符串列表,或通配符 "*"
        allow_credentials=True,  # 是否允许携带凭证(例如,使用 HTTP 认证、Cookie 等)
        allow_methods=["*"],  # 允许的 HTTP 方法,可以是字符串、字符串列表,或通配符 "*"
        allow_headers=["*"],  # 允许的 HTTP 头信息,可以是字符串、字符串列表,或通配符 "*"
        expose_headers=["*"],  # 允许前端访问的额外响应头,可以是字符串、字符串列表
        max_age=600,  # 请求的缓存时间,以秒为单位
    )

以下是常用参数的详细说明:

  • allow_origins: 允许的来源。可以是字符串、字符串列表,或通配符 "*" 表示允许所有来源。

  • allow_credentials: 是否允许携带凭证(例如,使用 HTTP 认证、Cookie 等)。默认为 False,如果为 True,allow_origins 必须为具体的源,不可以是 ["*"]。

  • allow_methods: 允许的 HTTP 方法,可以是字符串、字符串列表,或通配符 "*" 表示允许所有方法。

  • allow_headers: 允许的 HTTP 头信息,可以是字符串、字符串列表,或通配符 "*" 表示允许所有头信息。

  • expose_headers: 允许前端访问的额外响应头,可以是字符串、字符串列表,一般很少指定。

  • max_age: 浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 600,一般使用默认值。

4.3 GZipMiddleware使用

# 导入
from fastapi.middleware.gzip import GZipMiddleware

# 注册
server.add_middleware(
    GZipMiddleware,
    minimum_size=500,   # 启用 Gzip 压缩的最小响应体大小,单位为字节
    compress_level=6,   # Gzip 压缩级别,范围为 0 到 9,级别越高,压缩率越高,但耗费的 CPU 越多
    exclude_mediatypes=["application/json"],  # 不进行 Gzip 压缩的媒体类型,可以是字符串或字符串列表
)

以下是常用参数的详细说明:

minimum_size: 启用 Gzip 压缩的最小响应体大小,单位为字节。只有当响应体的内容大于等于 minimum_size 时,才会进行 Gzip 压缩。默认为 500 字节。

compress_level: Gzip 压缩级别,范围为 0 到 9。级别越高,压缩率越高,但耗费的 CPU 越多。默认为 6。

exclude_mediatypes: 不进行 Gzip 压缩的媒体类型,可以是字符串或字符串列表。例如,可以配置不对 JSON 响应进行压缩。

5.JWT验证

FastAPI框架没有内置的JWT(JSON Web Token)中间件,我们可以使用第三方库( PyJWT)来验证和解码 JWT,下面来实现使用示例;

5.1 安装

pip install pyjwt

5.2 生成token

# 定义秘钥和算法
from datetime import timedelta, datetime

import jwt

secret_key = "abcd12345@abcdef"
algorithm = "HS256"

def jwtGenerator() -> str:
    # 构造 payload
    payload = {
        "uid": 1234567,  # 主题,通常是用户的唯一标识
        "iat": datetime.utcnow(),  # 签发时间
        "exp": datetime.utcnow() + timedelta(minutes=30),  # 过期时间
        "data": {"user_name": "张三", "uid": 1234567, "phone": "17600000000"}  # 自定义的数据
    }
    # 生成 JWT
    return jwt.encode(payload, secret_key, algorithm=algorithm)


if __name__ == '__main__':
    # 生成token
    token = jwtGenerator()
    print("Token:", token)

"""
Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEyMzQ1NjcsImlhdCI6MTcwMjUyODA1MSwiZXhwIjoxNzAyNTI5ODUxLCJkYXRhIjp7InVzZXJfbmFtZSI6Ilx1NWYyMFx1NGUwOSIsInVpZCI6MTIzNDU2NywicGhvbmUiOiIxNzYwMDAwMDAwMCJ9fQ.sV_k75YPdEtI2P7-HlDbGbXWdcdosf1cCImNkux7OGg
"""

<think>好的,用户遇到了Git合并时本地更改会被覆盖的问题,需要解决方案。根据之前的引用资料,解决方法主要有commit、stash和revert三种。首先,我得确认用户的具体情况,比如他们的本地更改是否已经完成,是否需要保留,或者是否可以丢弃。用户可能不太清楚这三种方法的适用场景,需要详细解释每个选项的步骤和优缺点。比如,commit适用于需要保留更改的情况,stash适合临时保存以便后续恢复,而revert则是直接撤销更改,适用于不需要保留的情况。需要结构清晰,分点说明,每个方法都给出操作步骤和适用场景,帮助用户理解哪种情况最适合他们。还要注意使用中文,避免技术术语过于复杂,让用户容易理解。同时,生成相关问题,可能涉及如何恢复stash的内容,或者如何避免频繁冲突等,扩展用户的知识点。另外,要确保LaTeX格式正确,但在这个问题中可能用不上数学公式,所以主要关注代码块和引用标识。检查引用是否正确,比如引用[^1]和[^2]的来源是否相关,用户提供的引用内容是否准确对应解决方案。 最后,确保回答真实可靠,操作步骤准确无误,避免误导用户。可能还需要提醒用户根据具体情况选择方法,比如如果有未完成的实验性代码,stash可能是更好的选择,而如果是确定要保留的修改,则应该commit。</think>要解决Git合并时本地更改被覆盖的问题,可根据不同场景选择以下三种方法: ### 一、提交本地更改(commit) **适用场景**:当本地更改已测试通过且需要保留到版本历史中。 **操作步骤**: 1. 将改动添加到暂存区: ```bash git add . # 添加所有文件 # 或指定文件 git add file1.txt file2.py ``` 2. 提交更改并添加描述: ```bash git commit -m "完成用户登录模块优化" ``` 3. 执行合并操作: ```bash git merge origin/main ``` **优点**:保留完整的开发记录,便于后续追溯[^1]。 ### 二、暂存更改(stash) **适用场景**:需要临时保存未完成的修改(如调试代码),优先处理合并操作。 **操作步骤**: 1. 保存当前工作状态: ```bash git stash # 可选添加备注 git stash save "用户登录模块调试代码" ``` 2. 查看暂存记录列表: ```bash git stash list ``` 3. 合并远程分支: ```bash git merge origin/main ``` 4. 恢复暂存内容: ```bash git stash pop # 恢复并删除最近一次暂存 # 或指定特定暂存 git stash apply stash@{1} ``` **优点**:保持工作目录整洁,特别适合处理紧急合并任务[^2]。 ### 三、撤销本地更改(revert) **适用场景**:本地修改可丢弃(如实验性代码或错误修改)。 **操作步骤**: 1. 撤销所有未提交的修改: ```bash git checkout -- . # 全部文件 # 或指定文件 git checkout -- file1.txt ``` 2. 执行合并操作: ```bash git merge origin/main ``` **风险提示**:此操作不可逆,需确认文件无需保留。 --- ### 对比决策表 | 方法 | 保留记录 | 可恢复性 | 适用阶段 | |---------|----------|----------|------------------| | commit | ✅ | ✅ | 功能开发完成时 | | stash | ❌ | ✅ | 开发中途临时切换 | | revert | ❌ | ❌ | 放弃无效修改 | ### 扩展应用场景 - 当存在多个暂存时,可用`git stash list`查看记录,通过`git stash apply stash@{n}`精准恢复 - 合并后出现冲突时,可使用`git mergetool`可视化工具解决冲突 - 长期分支开发推荐使用`git rebase`保持提交历史线性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值