8. FastApi 中间件

176 阅读3分钟

是什么

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

  • 它接收你的应用程序的每一个请求.
  • 然后它可以对这个请求做一些事情或者执行任何需要的代码.
  • 然后它将请求传递给应用程序的其他部分 (通过某种路径操作).
  • 然后它获取应用程序生产的响应 (通过某种路径操作).
  • 它可以对该响应做些什么或者执行任何需要的代码.
  • 然后它返回这个 响应.

一个完整的中间件是即包含了请求代码块也包含了相应代码块。

应用场景

希望有一种机制,无论什么路由方法执行之前和之后都需要执行的代码。 例如:任何请求来之前都需要校验当前token 是否生效?校验完成之后重新生成新的token? 如果不用上中间件的话需要在每个路由方法中都需要写上校验和重新生成的代码。

图例

image.png

客户端请求过来之后会依次走所有中间件的请求代码块最后走到路由方法中,方法返回后依次倒序走所有中间件的响应代码块

代码工作流程:

  1. 请求进入中间件:当请求到达 FastAPI 应用时,add_process_time_header 中间件会被触发,记录下当前的时间 start_time
  2. 调用下一中间件或路由:使用 await call_next(request) 调用请求的处理(即下一个中间件或目标路由),并等待它的响应。这个调用将会在请求处理过程中暂停,直到响应准备好。
  3. 计算处理时间:当响应被返回后,计算从 start_time 到当前时间的差值,得到请求处理的时间。
  4. 修改响应头:将计算出的处理时间 process_time 添加到响应头中,使用 response.headers["X-Process-Time"] = str(process_time)
  5. 返回响应:最后,返回修改后的响应。
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.perf_counter()  # 记录请求开始的时间
    response = await call_next(request)  # 执行请求处理
    process_time = time.perf_counter() - start_time  # 计算处理时间
    response.headers["X-Process-Time"] = str(process_time)  # 添加处理时间到响应头
    return response
    
@app.middleware("http")
async def add_process_time_header2(request: Request, call_next):
    start_time = time.perf_counter()  # 记录请求开始的时间
    response = await call_next(request)  # 执行请求处理
    process_time = time.perf_counter() - start_time  # 计算处理时间
    response.headers["X-Process-Time"] = str(process_time)  # 添加处理时间到响应头
    return response

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

在 FastAPI 中,中间件是按照你添加的顺序执行的,也就是说,第一个被添加的中间件会先执行,第二个中间件会在第一个中间件执行完后执行

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()

class Middleware1(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        print("Middleware1 before request")
        response = await call_next(request)
        print("Middleware1 after request")
        return response

class Middleware2(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        print("Middleware2 before request")
        response = await call_next(request)
        print("Middleware2 after request")
        return response

# 按顺序添加中间件
app.add_middleware(Middleware1)
app.add_middleware(Middleware2)

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

使用 app.add_middleware 添加的中间件会按照你调用 add_middleware 的顺序来执行。也就是说,第一个 add_middleware 被调用的中间件会先执行,第二个会在第一个之后执行,依此类推。