Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

181 lines
6.4KB

  1. import json
  2. import time
  3. from fastapi import FastAPI, Request,HTTPException
  4. from starlette.middleware.base import BaseHTTPMiddleware
  5. from starlette.responses import JSONResponse
  6. from pydantic import BaseModel
  7. from datetime import datetime
  8. import logging
  9. from common.log import logger
  10. class Result(BaseModel):
  11. code: int
  12. message: str
  13. status: str
  14. class ResponseData(BaseModel):
  15. data: dict|list|None
  16. result: Result
  17. timestamp: str
  18. async def http_context(request: Request, call_next):
  19. # 记录请求信息
  20. request_info = {
  21. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  22. "method": request.method,
  23. "url": str(request.url),
  24. "body": await request.body() if request.method in ["POST", "PUT", "PATCH"] else None,
  25. }
  26. logger.info(f"请求: {json.dumps(request_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  27. # 调用下一个中间件或路由处理程序
  28. response = await call_next(request)
  29. # 如果响应状态码为 200,则格式化响应为统一格式
  30. if response.status_code == 200:
  31. try:
  32. response_body = b""
  33. async for chunk in response.body_iterator:
  34. response_body += chunk
  35. response_body_str = response_body.decode("utf-8")
  36. business_data = json.loads(response_body_str)
  37. except Exception as e:
  38. business_data = {"error": f"Unable to decode response body: {str(e)}"}
  39. if "code" in business_data:
  40. message=business_data.get("message","请求失败!")
  41. result = ResponseData(
  42. data=None,
  43. result=Result(code=business_data.get("code",500), message=message, status="failed"),
  44. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  45. )
  46. else:
  47. # 构造统一格式的响应
  48. result = ResponseData(
  49. data=business_data,
  50. result=Result(code=200, message="请求成功!", status="succeed"),
  51. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  52. )
  53. response_info = {
  54. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  55. "status_code": response.status_code,
  56. "headers": dict(response.headers),
  57. "body": result.dict(),
  58. }
  59. logger.info(f"响应: {json.dumps(response_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  60. # 返回修改后的响应
  61. return JSONResponse(content=result.model_dump())
  62. else:
  63. print(response)
  64. print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
  65. message = "请求失败!"
  66. # 如果响应状态码不为 200,则记录响应信息
  67. try:
  68. response_body = b""
  69. async for chunk in response.body_iterator:
  70. response_body += chunk
  71. response_body_str = response_body.decode("utf-8")
  72. business_data = json.loads(response_body_str)
  73. except Exception as e:
  74. business_data = {"error": f"Unable to decode response body: {str(e)}"}
  75. # 根据不同状态码定制 message 字段
  76. if response.status_code == 404:
  77. message = e.detail
  78. elif response.status_code == 400:
  79. message = "请求参数错误"
  80. elif response.status_code == 500:
  81. message = "服务器内部错误"
  82. # 你可以根据不同的状态码设置更详细的错误消息
  83. # 构造统一格式的响应
  84. result = ResponseData(
  85. data={}, # 返回空数据
  86. result=Result(
  87. code=response.status_code,
  88. message=message, # 根据状态码返回详细信息
  89. status="failed"
  90. ),
  91. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  92. )
  93. response_info = {
  94. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  95. "status_code": response.status_code,
  96. "headers": dict(response.headers),
  97. "body": result.dict(),
  98. }
  99. logger.info(f"响应: {json.dumps(response_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  100. # 返回修改后的响应
  101. return JSONResponse(content=result.model_dump(), status_code=response.status_code)
  102. async def http_context_2(request: Request, call_next):
  103. # 记录请求信息
  104. request_body = None
  105. if request.method in ["POST", "PUT", "PATCH"]:
  106. try:
  107. request_body = await request.json() # 使用 .json(),避免影响 FastAPI 解析
  108. except Exception:
  109. request_body = "无法解析 JSON"
  110. request_info = {
  111. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  112. "method": request.method,
  113. "url": str(request.url),
  114. "body": request_body,
  115. }
  116. logger.info(f"请求: {json.dumps(request_info, separators=(',', ':'), ensure_ascii=False)}")
  117. # 继续处理请求
  118. response = await call_next(request)
  119. # 如果是 422 错误,直接返回,避免 Pydantic 解析错误
  120. if response.status_code == 422:
  121. return response
  122. # 处理正常请求
  123. try:
  124. response_body = b""
  125. async for chunk in response.body_iterator:
  126. response_body += chunk
  127. response_body_str = response_body.decode("utf-8")
  128. business_data = json.loads(response_body_str)
  129. except Exception as e:
  130. business_data = {"error": f"无法解析响应体: {str(e)}"}
  131. if "code" in business_data:
  132. message = business_data.get("message", "请求失败!")
  133. result = ResponseData(
  134. data=None,
  135. result=Result(code=business_data.get("code", 500), message=message, status="failed"),
  136. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  137. )
  138. else:
  139. result = ResponseData(
  140. data=business_data,
  141. result=Result(code=200, message="请求成功!", status="succeed"),
  142. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  143. )
  144. response_info = {
  145. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  146. "status_code": response.status_code,
  147. "headers": dict(response.headers),
  148. "body": result.dict(),
  149. }
  150. logger.info(f"响应: {json.dumps(response_info, separators=(',', ':'), ensure_ascii=False)}")
  151. return JSONResponse(content=result.model_dump())