aiohttp服务GET请求返回500错误 loop参数异常修复方案
问题根因
你遇到的TypeError: BaseEventLoop.create_connection() got an unexpected keyword argument 'loop'是Python版本与旧版asyncio写法不兼容导致的:
Python 3.8 开始就将asyncio系列接口中显式传递
loop的参数标记为废弃,到Python 3.10版本正式移除了该参数。你当前运行环境是Python 3.10,代码中多处手动传递loop参数的写法已经不被支持。
除此之外原代码还存在三个隐藏bug:
asyncio.open_connection本身是协程对象,原代码没有正确处理连接成功后的资源释放,会造成连接泄漏- 端口扫描区间使用
range(begin_port, end_port)是左闭右开区间,传入1-10的参数实际只会扫描1-9端口,和预期不符 - 异常捕获范围不全,仅捕获了
TimeoutError,实际端口关闭时会抛出ConnectionRefusedError、OSError等异常,依然会触发500错误
修复步骤
- 删除所有手动获取、传递event loop的冗余代码,Python 3.7+版本asyncio会自动绑定当前运行的事件循环,不需要手动传参
- 修正端口检测逻辑,连接成功后主动关闭writer对象释放资源
- 调整端口遍历的区间范围,将结束端口+1,保证扫描区间和传入参数一致
- 补全异常捕获范围,移除不必要的
asyncio.ensure_future冗余包装 - 同步调整pytest用例适配新的代码逻辑
修复后完整代码
from aiohttp import web import logging import json import asyncio def catch_exception(func): async def decorated_function(ip, port): try: await func(ip, port) return {"port": str(port), "state": "open"} except (asyncio.TimeoutError, ConnectionRefusedError, OSError): return {"port": str(port), "state": "close"} return decorated_function @catch_exception async def check_port(ip, port): conn = asyncio.open_connection(ip, port) reader, writer = await asyncio.wait_for(conn, timeout=3) # 连接成功后主动关闭,避免资源泄漏 writer.close() await writer.wait_closed() async def run(ip, begin_port, end_port): tasks = [check_port(ip, p) for p in range(begin_port, end_port + 1)] responses = await asyncio.gather(*tasks) return responses async def handle(request): ip = request.match_info.get('ip') begin_port = int(request.match_info.get('begin_port')) end_port = int(request.match_info.get('end_port')) results = await run(ip, begin_port, end_port) response_obj = {'data': results} return web.Response(text=json.dumps(response_obj), status=200, content_type='application/json') def get_app(): new_app = web.Application() new_app.router.add_get('/scan/{ip}/{begin_port}/{end_port}', handle) return new_app # _________________________ pytest _____________________________________ def create_app(): test_app = get_app() return test_app async def test_hello(aiohttp_client): client = await aiohttp_client(create_app) resp = await client.get('/scan/127.0.0.1/1/10') assert resp.status == 200 data = await resp.json() assert type(data['data']) is list assert len(data['data']) == 10 # _________________________ pytest _____________________________________ logger = logging.getLogger('aiohttp.access') logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler()) if __name__ == '__main__': print('Starting!') web.run_app(get_app(), host='127.0.0.1', port=8080)
验证说明
修复后启动服务,访问http://127.0.0.1:8080/scan/127.0.0.1/1/10即可正常返回JSON格式的端口扫描结果,不会再抛出500错误,pytest测试用例也可正常跑通。
内容的提问来源于stack exchange,提问作者Parcurcik
相关产品推荐
相关产品推荐

