64 lines
2.9 KiB
Python
64 lines
2.9 KiB
Python
# ============================================================
|
|
# 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,
|
|
) |