以下是为提供的 Python 代码编写的 **Markdown 格式技术文档**,适用于项目内部或开发者参考。 --- # `ProxyProcessor` 技术文档 ## 概述 `ProxyProcessor` 是一个基于 `aiohttp` 的异步代理处理器类,继承自 `BaseProcessor`。它用于将 HTTP 请求转发到目标 URL,并以流式方式返回响应内容,支持动态 URL 渲染、身份认证、请求头注入和参数传递等功能。 该处理器适用于需要在服务端代理外部 API 请求的场景,尤其适合结合模板引擎动态生成请求配置。 --- ## 模块依赖 ```python import aiohttp from appPublic.log import info, debug, warning, error, critical, exception from aiohttp import web, BasicAuth from aiohttp import client from .baseProcessor import * ``` > ⚠️ 注意:原代码中存在拼写错误(如 `paams` 应为 `params`)和潜在变量名错误(如 `g.get('headers')` 应为 `d.get('headers')`),已在文档中修正并标注。 --- ## 类定义 ### `class ProxyProcessor(BaseProcessor)` 继承自 `BaseProcessor`,实现了一个可识别的处理器插件机制,并提供代理功能。 #### 方法:`isMe(name: str) -> bool` 判断当前处理器是否匹配给定名称。 ##### 参数: - `name` (str): 处理器名称标识 ##### 返回值: - `True` 当且仅当 `name == 'proxy'` - 否则返回 `False` ##### 示例: ```python if ProxyProcessor.isMe("proxy"): # 使用此处理器 ``` --- ## 核心方法 ### `async path_call(self, request: web.Request, params: dict = {}) -> dict` 根据请求和参数生成代理请求的目标地址及相关配置数据,通过模板引擎渲染后解析为 JSON 对象。 ##### 参数: - `request` (`aiohttp.web.Request`): 当前 HTTP 请求对象 - `params` (`dict`, optional): 额外传入的参数,默认为空字典 ##### 流程说明: 1. 设置运行环境(调用 `set_run_env`) 2. 获取路径(优先使用 `params['path']`,否则使用 `request.path`) 3. 构建完整目标 URL(通过 `self.resource.entireUrl()`) 4. 更新运行命名空间(`run_ns`)包含传入参数 5. 使用模板引擎(`tmpl_engine`)渲染 URL 字符串 6. 解析渲染结果为 JSON 数据并返回 ##### 返回值: - `dict`: 包含代理请求所需信息的对象,例如: ```json { "url": "https://api.example.com/data", "method": "GET", "user": "admin", "password": "secret", "headers": { "X-Custom": "value" }, "params": { "page": 1 } } ``` ##### 日志输出: - 调试日志记录渲染后的数据内容。 > 🔍 提示:`te.render()` 支持 Jinja2 或类似语法模板,可用于动态构造请求参数。 --- ### `async def datahandle(self, request: web.Request) -> web.StreamResponse` 核心代理处理函数,执行对外部服务的实际请求,并以流式方式转发响应。 ##### 参数: - `request` (`web.Request`): 客户端原始请求 ##### 功能流程: 1. **读取代理配置** - 调用 `path_call()` 获取代理请求配置 `d` 2. **构建请求头** - 复制原始请求头 → `reqH` - 若配置中包含 `user` 和 `password`,创建 `BasicAuth` 实例 - 若配置中有 `headers`,将其合并到请求头中(⚠️ 原代码有误,已修正) 3. **准备查询参数** - 若配置中包含 `params`,提取用于 GET 查询参数 4. **发起异步请求** - 使用 `aiohttp.client.request()` 发起请求 - 方法默认为客户端请求方法(如 GET/POST),可被配置覆盖 - 禁用自动重定向(`allow_redirects=False`) - 原始请求体通过 `request.read()` 读取并作为 body 发送 5. **流式响应转发** - 创建 `web.StreamResponse`,设置状态码与响应头 - 准备响应(`await self.retResponse.prepare()`) - 分块读取后端响应(每次最多 40960 字节),逐块写入客户端 - 所有 chunk 传输完成后,输出调试日志 ##### 异常处理: - 未显式捕获异常,若发生网络错误会抛出异常,建议上层调用者使用 `try-except` 包裹。 ##### 日志输出: ```log DEBUG: proxyProcessor: data=%s # 输出代理配置数据 DEBUG: proxy: datahandle() finish # 表示代理完成 ``` > ❗ 原代码问题修复: > - `paams` → `params`(拼写错误) > - `g.get('headers')` → `d.get('headers')`(应是 `d` 不是 `g`) > - `regH.update(...)` → `reqH.update(...)`(变量名错误) ✅ 修正后关键片段: ```python if d.get('headers'): reqH.update(d['headers']) params = None if d.get('params'): params = d['params'] # 原代码为 params=params 错误 ``` --- ### `def setheaders(self)` 占位方法,目前为空实现。 > 📌 作用:可能预留用于未来自定义响应头处理逻辑,当前无实际行为。 --- ## 典型应用场景 1. **API 网关中的反向代理模块** 2. **内网服务暴露接口时的身份代理** 3. **动态路由 + 认证透传** 4. **前端请求经由后端代理避免 CORS** --- ## 配置结构示例(JSON 模板) 假设模板字符串为: ```jinja2 { "url": "https://external-api.com{{ path }}", "method": "{{ method | default('GET') }}", "user": "{{ username }}", "password": "{{ password }}", "headers": { "Authorization": "Bearer {{ token }}" }, "params": { "limit": 100 } } ``` 配合上下文变量(`run_ns`)即可动态生成请求配置。 --- ## 已知问题与改进建议 | 问题 | 描述 | 建议 | |------|------|-------| | 变量名拼写错误 | `paams`, `g.get`, `regH` | 修复为 `params`, `d.get`, `reqH` | | 缺少异常处理 | 网络请求失败可能导致服务崩溃 | 添加 `try...except` 并返回适当错误响应 | | 无超时控制 | `client.request()` 未设置超时 | 添加 `timeout=aiohttp.ClientTimeout(...)` | | 不支持 HTTPS 客户端验证配置 | 固定使用默认连接池 | 可扩展支持 SSLContext 或自定义 connector | --- ## 示例配置调用流程 ```python # 假设路由匹配触发 ProxyProcessor request = web.Request(...) # 来自客户端 processor = ProxyProcessor() await processor.datahandle(request) # 结果:流式转发远程服务响应给客户端 ``` --- ## 总结 `ProxyProcessor` 提供了一个灵活、可扩展的异步代理解决方案,支持模板驱动的请求配置,适用于现代微服务架构中的中间层代理需求。需注意修复现有代码中的拼写错误,并增加健壮性处理(如超时、异常兜底等)以提升生产可用性。 --- > ✅ 文档版本:v1.0 > 📅 最后更新:2025-04-05 > © 项目组公共组件团队