python 开发时一些问题 Python

2025-10-10 约 900 字 阅读时长2 分钟

python 开发时一些问题

一些特性记录

fastapi中间件

中间件 执行顺序类似于洋葱,最后添加的中间件在最外层

middleware.py

python
 1import time
 2
 3from fastapi import Request
 4from fastapi.encoders import jsonable_encoder
 5from starlette.middleware.base import BaseHTTPMiddleware
 6from starlette.responses import JSONResponse as StarletteJSONResponse
 7
 8import app.common.log_factory as log_factory
 9from app.common.exception import AppException
10from app.common.utils.response_util import WrapperResponse
11
12log = log_factory.get_logger(__name__)
13
14
15# 全局异常处理中间件
16class HandleExceptionMiddleware(BaseHTTPMiddleware):
17    async def dispatch(self, request: Request, call_next):
18        try:
19            response = await call_next(request)
20            # 检查非200状态码
21            if response.status_code != 200:
22                content_str = ""
23                # 处理不同类型的响应
24                if hasattr(response, "body"):
25                    try:
26                        content = await response.body()
27                        content_str = content.decode(errors='replace')[:500]
28                    except Exception as e:
29                        content_str = f"[读取body失败: {str(e)}]"
30                elif hasattr(response, "body_iterator"):
31                    content_str = "[流式响应内容,无法直接读取]"
32                else:
33                    content_str = f"[未知响应类型: {type(response)}]"
34
35                log.warning(
36                    f"非200响应 - 路径: {request.url.path}, "
37                    f"状态码: {response.status_code}, "
38                    f"内容: {content_str}"
39                )
40        except AppException as e:
41            log.error('%s', e)
42            response = StarletteJSONResponse(
43                jsonable_encoder(WrapperResponse.error(msg=e.msg)),
44                status_code=e.code if isinstance(e.code, int) else 500
45            )
46        except Exception as e:
47            log.error('%s', e)
48            response = StarletteJSONResponse(
49                jsonable_encoder(WrapperResponse.error(msg="服务器内部异常")),
50                status_code=500
51            )
52        return response
53
54
55class RateLimiterMiddleware(BaseHTTPMiddleware):
56    def __init__(
57            self,
58            app,
59            limit: int = 60,  # 单位时间内允许的最大请求数
60            window: int = 60  # 时间窗口,单位为秒
61    ):
62        super().__init__(app)
63        self.limit = limit
64        self.window = window
65        self.client_requests = {}
66
67    async def dispatch(self, request: Request, call_next):
68        log.info(f"请求进入:{request.url.path}")
69        try:
70            client_ip = request.client.host
71            current_time = time.time()
72
73            # 如果客户端 IP 不在记录中,初始化记录
74            if client_ip not in self.client_requests:
75                self.client_requests[client_ip] = {
76                    "count": 1,
77                    "start_time": current_time
78                }
79            else:
80                client_info = self.client_requests[client_ip]
81                elapsed_time = current_time - client_info["start_time"]
82
83                # 如果时间窗口已过,重置计数和开始时间
84                if elapsed_time > self.window:
85                    client_info["count"] = 1
86                    client_info["start_time"] = current_time
87                else:
88                    # 如果未超过时间窗口,增加计数
89                    client_info["count"] += 1
90
91                    # 检查是否超过请求限制
92                    if client_info["count"] > self.limit:
93                        raise AppException(msg="请求频率过高,请稍后再试", code=429)
94
95            response = await call_next(request)
96        finally:
97            log.info(f"请求结束:{request.url.path}")
98        return response

一些错误记录

pyCharm Dubeg启动报错

12025-10-24 10:34:17 - asyncio - ERROR - Exception in callback <Task pending name='Task-1' coro=<Server.serve() running at C:\Users\lei\Desktop\fastapi-demo\.venv\lib\site-packages\uvicorn\server.py:69> cb=[_run_until_complete_cb() at C:\Users\lei\AppData\Roaming\uv\python\cpython-3.10.18-windows-x86_64-none\lib\asyncio\base_events.py:184]>()
2handle: <Handle <Task pending name='Task-1' coro=<Server.serve() running at C:\Users\lei\Desktop\fastapi-demo\.venv\lib\site-packages\uvicorn\server.py:69> cb=[_run_until_complete_cb() at C:\Users\lei\AppData\Roaming\uv\python\cpython-3.10.18-windows-x86_64-none\lib\asyncio\base_events.py:184]>()>
3Traceback (most recent call last):
4  File "C:\Users\lei\AppData\Roaming\uv\python\cpython-3.10.18-windows-x86_64-none\lib\asyncio\events.py", line 80, in _run
5    self._context.run(self._callback, *self._args)
6TypeError: 'Task' object is not callable

解决办法:禁用异步调试模式

进入 Help -> Find Action,搜索 Registry,找到并取消勾选 python.debug.asyncio.repl 选项。此操作可禁用PyCharm对异步代码的特殊处理,避免事件循环冲突

使用滚轮缩放
按住拖动