# ============================================================ # HTTP 请求日志中间件 # 功能:拦截每一次 API 请求,记录请求方信息、请求参数、 # 响应状态、响应数据以及接口耗时,统一写入 log.txt # 工作流程: # 请求进入 → 记录 method / url → 调用真实接口 → 记录耗时 # → 记录响应状态码 & 响应体 → 返回重新组装的 Response # ============================================================ import time from fastapi import Request from fastapi.responses import Response from Log import Log async def m1(req: Request, call_next): """ FastAPI HTTP 中间件入口函数。 参数: req: FastAPI 自动注入的 Request 对象,包含 method / url / body 等 call_next: 由中间件机制注入的回调函数,调用它会执行下一个中间件或真正的接口处理器 注意: 中间件里读取响应体后,必须手动重新构造一个 Response 返回; 否则 FastAPI 流式响应的 body_iterator 被读完就没了,前端会收到空内容。 """ # 初始化日志器:日志名 ai0824,默认 INFO 级别 log = Log('ai0824', 'INFO') # 将日志输出到 log.txt(会自动追加) log.logFile('log.txt') # —— 1. 请求前置记录 —— log.message('Info', '请求方式:' + req.method) # GET / POST / PUT / DELETE ... log.message('Info', '请求地址:' + str(req.url)) # 例如 http://127.0.0.1:12345/basic-information # —— 2. 执行真正的接口,记录耗时 —— beginTime = time.time() # 请求开始时间戳(秒) r = await call_next(req) # 调用后续链:下一个中间件或路由处理器 endTime = time.time() # 响应返回时间戳 # —— 3. 读取原始请求体(JSON 参数等)并记录 —— body = await req.body() # body() 是异步方法,必须 await;返回 bytes log.message('Info', '请求参数:' + body.decode('utf-8', 'ignore')) # —— 4. 读取流式响应体并记录 —— # call_next 返回的是 StreamingResponse,响应体藏在 body_iterator 里,且只能遍历一次 resp_bytes = b'' async for chunk in r.body_iterator: resp_bytes += chunk if isinstance(chunk, bytes) else chunk.encode() log.message('Info', '响应状态码:' + str(r.status_code)) log.message('Info', '响应数据:' + resp_bytes.decode('utf-8', 'ignore')) log.message('Info', '请求耗时:%.2f ms' % ((endTime - beginTime) * 1000)) # —— 5. 重新构造 Response 返回给前端 —— # 原因:body_iterator 已经被遍历过一次,原始 Response 再返回会导致响应体为空 return Response( content=resp_bytes, status_code=r.status_code, headers=dict(r.headers), media_type=r.media_type, )