Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

middleware.py 8.6KB

il y a 1 mois
il y a 2 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 2 semaines
il y a 2 semaines
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 2 semaines
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 2 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 3 semaines
il y a 1 mois
il y a 2 semaines
il y a 1 mois
il y a 3 semaines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. import uuid
  10. from common.log import logger
  11. class Result(BaseModel):
  12. code: int
  13. message: str
  14. status: str
  15. class ResponseData(BaseModel):
  16. data: dict|list|None
  17. result: Result
  18. timestamp: str
  19. # 设置最大请求大小(100MB)
  20. MAX_REQUEST_SIZE = 100 * 1024 * 1024 # 100MB
  21. async def http_context_v1(request: Request, call_next):
  22. # 记录请求信息
  23. request_info = {
  24. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  25. "method": request.method,
  26. "url": str(request.url),
  27. "body": await request.body() if request.method in ["POST", "PUT", "PATCH"] else None,
  28. }
  29. logger.info(f"请求: {json.dumps(request_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  30. # 调用下一个中间件或路由处理程序
  31. response = await call_next(request)
  32. # 如果响应状态码为 200,则格式化响应为统一格式
  33. if response.status_code == 200:
  34. try:
  35. response_body = b""
  36. async for chunk in response.body_iterator:
  37. response_body += chunk
  38. response_body_str = response_body.decode("utf-8")
  39. business_data = json.loads(response_body_str)
  40. except Exception as e:
  41. business_data = {"error": f"Unable to decode response body: {str(e)}"}
  42. if "code" in business_data:
  43. message=business_data.get("message","请求失败!")
  44. result = ResponseData(
  45. data=None,
  46. result=Result(code=business_data.get("code",500), message=message, status="failed"),
  47. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  48. )
  49. else:
  50. # 构造统一格式的响应
  51. result = ResponseData(
  52. data=business_data,
  53. result=Result(code=200, message="请求成功!", status="succeed"),
  54. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  55. )
  56. response_info = {
  57. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  58. "status_code": response.status_code,
  59. "headers": dict(response.headers),
  60. "body": result.dict(),
  61. }
  62. logger.info(f"响应: {json.dumps(response_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  63. # 返回修改后的响应
  64. return JSONResponse(content=result.model_dump())
  65. else:
  66. message = "请求失败!"
  67. # 如果响应状态码不为 200,则记录响应信息
  68. try:
  69. response_body = b""
  70. async for chunk in response.body_iterator:
  71. response_body += chunk
  72. response_body_str = response_body.decode("utf-8")
  73. business_data = json.loads(response_body_str)
  74. except Exception as e:
  75. business_data = {"error": f"Unable to decode response body: {str(e)}"}
  76. # 根据不同状态码定制 message 字段
  77. if response.status_code == 404:
  78. message = e.detail
  79. elif response.status_code == 400:
  80. message = "请求参数错误"
  81. elif response.status_code == 500:
  82. message = "服务器内部错误"
  83. # 你可以根据不同的状态码设置更详细的错误消息
  84. # 构造统一格式的响应
  85. result = ResponseData(
  86. data={}, # 返回空数据
  87. result=Result(
  88. code=response.status_code,
  89. message=message, # 根据状态码返回详细信息
  90. status="failed"
  91. ),
  92. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  93. )
  94. response_info = {
  95. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  96. "status_code": response.status_code,
  97. "headers": dict(response.headers),
  98. "body": result.dict(),
  99. }
  100. logger.info(f"响应: {json.dumps(response_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  101. # 返回修改后的响应
  102. return JSONResponse(content=result.model_dump(), status_code=response.status_code)
  103. async def http_context(request: Request, call_next):
  104. request.state.max_request_size = MAX_REQUEST_SIZE
  105. # 生成唯一ID
  106. request_id = str(uuid.uuid4())
  107. # 检查请求大小
  108. content_length = request.headers.get("content-length")
  109. if content_length and int(content_length) > MAX_REQUEST_SIZE:
  110. return JSONResponse(
  111. content={
  112. "data": None,
  113. "result": {
  114. "code": 413,
  115. "message": "请求体过大,最大支持 100MB",
  116. "status": "failed",
  117. },
  118. "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
  119. },
  120. status_code=413, # 413 Payload Too Large
  121. )
  122. # 记录请求信息(只读取 body,防止重复读取导致错误)
  123. request_info = {
  124. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  125. "method": request.method,
  126. "url": str(request.url),
  127. "body": None, # 只在 POST/PUT/PATCH 时读取 body
  128. }
  129. if request.method in ["POST", "PUT", "PATCH"]:
  130. try:
  131. request_info["body"] = await request.body()
  132. except Exception as e:
  133. request_info["body"] = f"Error reading body: {str(e)}"
  134. logger.info(f"{request_id} 请求: {json.dumps(request_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  135. # 调用下一个中间件或路由
  136. response = await call_next(request)
  137. # 处理响应
  138. response_body = b""
  139. async for chunk in response.body_iterator:
  140. response_body += chunk
  141. response_body_str = response_body.decode("utf-8")
  142. try:
  143. business_data = json.loads(response_body_str)
  144. except Exception as e:
  145. business_data = {"error": f"无法解析响应体: {str(e)}"}
  146. # 构造统一格式的响应
  147. if response.status_code == 200:
  148. # result = ResponseData(
  149. # data=business_data,
  150. # result=Result(code=200, message="请求成功!", status="succeed"),
  151. # timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
  152. # )
  153. try:
  154. business_data = json.loads(response_body_str)
  155. except Exception as e:
  156. business_data = {"error": f"Unable to decode response body: {str(e)}"}
  157. if "code" in business_data:
  158. message=business_data.get("message","请求失败!")
  159. result = ResponseData(
  160. data=None,
  161. result=Result(code=business_data.get("code",500), message=message, status="failed"),
  162. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  163. )
  164. else:
  165. # 构造统一格式的响应
  166. result = ResponseData(
  167. data=business_data,
  168. result=Result(code=200, message="请求成功!", status="succeed"),
  169. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
  170. )
  171. response_info = {
  172. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  173. "status_code": response.status_code,
  174. "headers": dict(response.headers),
  175. "body": result.model_dump(),
  176. }
  177. logger.info(f"{request_id} 响应: {json.dumps(response_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  178. # 返回修改后的响应
  179. return JSONResponse(content=result.model_dump())
  180. else:
  181. message = "请求失败!"
  182. if response.status_code == 404:
  183. message = "资源未找到"
  184. elif response.status_code == 400:
  185. message = "请求参数错误"
  186. elif response.status_code == 500:
  187. message = "服务器内部错误"
  188. result = ResponseData(
  189. data=None,
  190. result=Result(code=response.status_code, message=message, status="failed"),
  191. timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
  192. )
  193. response_info = {
  194. "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
  195. "status_code": response.status_code,
  196. "headers": dict(response.headers),
  197. "body": result.model_dump(),
  198. }
  199. logger.info(f"{request_id} 响应: {json.dumps(response_info, separators=(',', ':'), default=str, ensure_ascii=False)}")
  200. return JSONResponse(content=result.model_dump(), status_code=response.status_code)