FastAPI中间件实战:实现请求日志与响应时间统计

在FastAPI中,中间件(Middleware)是一种非常实用的功能,它允许我们在请求到达视图函数之前和响应返回给客户端之后,统一处理一些逻辑,比如认证、日志记录、错误处理等。本文将通过一个实战案例,带你实现一个简单的中间件,用于记录请求日志和统计响应时间,让你的API开发更规范、更便于调试。

一、环境准备

首先,确保你已经安装了FastAPI和Uvicorn(用于运行FastAPI应用的服务器)。如果还没有安装,可以通过以下命令安装:

pip install fastapi uvicorn

二、中间件的作用

为什么要使用中间件来做请求日志和响应时间统计呢?想象一下,如果我们在每个视图函数里都手动记录这些信息,会产生大量重复代码,而且一旦需求变化(比如要新增字段),修改起来也会很麻烦。中间件可以帮我们统一处理这些逻辑,所有请求都会自动经过中间件,既简洁又易于维护。

三、编写中间件实现请求日志与响应时间统计

我们需要创建一个中间件类,继承Starlette提供的BaseHTTPMiddleware(FastAPI基于Starlette开发,所以中间件逻辑由Starlette管理),并在dispatch方法中实现日志记录和时间统计的逻辑。

1. 定义中间件类

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
import time

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # 记录请求开始时间(使用time模块获取当前时间戳)
        start_time = time.time()

        # 调用下一个中间件或视图函数,获取响应
        response = await call_next(request)

        # 计算响应耗时(结束时间 - 开始时间)
        duration = time.time() - start_time

        # 构造日志信息
        log_info = (
            f"[请求日志] 方法: {request.method} | "
            f"路径: {request.url.path} | "
            f"客户端IP: {request.client.host} | "
            f"耗时: {duration:.4f}秒 | "
            f"状态码: {response.status_code}"
        )

        # 打印日志(实际项目中可替换为logging模块输出到文件)
        print(log_info)

        return response

2. 解释中间件逻辑

  • 继承BaseHTTPMiddleware:这是Starlette提供的中间件基类,封装了中间件的基本处理流程,我们只需实现dispatch方法即可。
  • dispatch方法:这是中间件的核心方法,接收两个参数:
  • request:当前请求对象(包含请求方法、路径、客户端信息等)。
  • call_next:一个异步函数,用于调用下一个中间件或视图函数,返回处理后的响应。
  • 时间统计:在请求处理前记录开始时间,处理完成后记录结束时间,计算时间差(duration)。
  • 日志构造:将请求方法、路径、客户端IP、耗时、响应状态码等信息整合为一条日志,方便查看和调试。

四、添加中间件到FastAPI应用

创建FastAPI应用后,通过add_middleware方法将我们定义的中间件添加到应用中:

# 创建FastAPI应用实例
app = FastAPI()

# 添加中间件(全局生效)
app.add_middleware(LoggingMiddleware)

五、测试中间件效果

接下来,我们定义几个简单的视图函数,测试中间件是否能正确记录请求日志和响应时间。

1. 定义测试路由

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

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id, "item_name": f"Item {item_id}"}

@app.post("/users")
async def create_user(name: str):
    return {"user": {"name": name, "status": "created"}}

2. 运行应用并测试

在终端执行以下命令启动应用:

uvicorn main:app --reload

此时,访问不同的接口(例如http://localhost:8000/http://localhost:8000/items/123http://localhost:8000/users?name=test),观察控制台输出的日志。

3. 日志输出示例

访问http://localhost:8000/后,控制台可能输出类似以下内容:

[请求日志] 方法: GET | 路径: / | 客户端IP: 127.0.0.1 | 耗时: 0.0002秒 | 状态码: 200

六、扩展与优化

以上是最基础的中间件实现,你可以根据实际需求进行扩展:

  1. 使用logging模块:将print替换为Python的logging模块,支持日志分级(INFO/WARNING/ERROR)、输出到文件等。
  2. 记录请求体:对于POST/PUT请求,可以通过await request.json()获取请求数据(注意敏感信息脱敏)。
  3. 异常处理:在中间件中捕获视图函数抛出的异常,记录错误堆栈信息。
  4. 处理跨域:如果需要跨域支持,可以添加CORS中间件。

总结

通过本文,你学习了如何使用FastAPI中间件统一实现请求日志和响应时间统计,避免了重复代码,提高了代码的可维护性。中间件是FastAPI(以及Starlette)中处理请求/响应前置和后置逻辑的强大工具,后续可以尝试将更多通用逻辑(如认证、跨域处理)封装到中间件中,让API开发更高效、规范。

小夜