ahserver/aidocs/functionProcessor.md
2025-10-05 12:07:12 +08:00

294 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

以下是为提供的 `FunctionProcessor` 类编写的 **Markdown 格式技术文档**,适用于项目开发文档或 API 文档场景。
---
# `FunctionProcessor` 技术文档
## 概述
`FunctionProcessor` 是一个基于 `aiohttp` 的异步请求处理器类,继承自 `BaseProcessor`。它用于将 HTTP 请求动态映射到通过 `RegisterFunction` 注册的函数上,并支持路径参数解析和运行时环境注入。该处理器特别适用于实现基于注册函数的路由分发机制。
主要功能包括:
- 解析 URL 路径中的参数
- 调用已注册的同步或异步函数
- 支持运行时命名空间(`run_ns`)注入上下文变量
- 返回 `Response``FileResponse` 或普通内容数据
---
## 导入依赖
```python
import inspect
from appPublic.dictObject import DictObject
from appPublic.registerfunction import RegisterFunction
from appPublic.log import info, debug, warning, error, exception, critical
from aiohttp import web
from aiohttp.web_response import Response, StreamResponse
from .baseProcessor import BaseProcessor
```
### 依赖说明
| 模块 | 用途 |
|------|------|
| `inspect` | 判断目标函数是否为协程函数 |
| `DictObject` | 将字典转换为属性可访问的对象 |
| `RegisterFunction` | 获取全局注册的函数 |
| `appPublic.log` | 日志输出 |
| `aiohttp.web` | Web 服务核心组件 |
| `BaseProcessor` | 基础处理器基类 |
---
## 类定义
```python
class FunctionProcessor(BaseProcessor):
```
继承自 `BaseProcessor`,用于处理特定类型的 HTTP 请求并调用注册函数。
---
## 类方法
### `isMe(name)`
```python
@classmethod
def isMe(cls, name):
return False
```
#### 说明
此方法用于判断当前处理器是否适用于给定名称的资源。在本实现中始终返回 `False`,表示此类不会自动匹配任何资源名 —— 其使用需显式配置。
> ⚠️ 注意:若需启用自动识别,请重写此方法以根据条件返回 `True`。
#### 参数
- `name` (`str`) - 资源名称
#### 返回值
- `bool`: 始终返回 `False`
---
## 实例方法
### `__init__(path, resource, opts)`
构造函数,初始化处理器实例。
```python
def __init__(self, path, resource, opts):
self.config_opts = opts
BaseProcessor.__init__(self, path, resource)
```
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `path` | `str` | 请求路径模板 |
| `resource` | `Any` | 关联资源对象(未具体使用) |
| `opts` | `dict` | 配置选项,必须包含以下键:<br>`leading`: 前缀路径,用于截断<br>`registerfunction`: 注册函数名 |
#### 属性设置
- `self.config_opts`: 存储传入的配置项
- 调用父类初始化逻辑
---
### `path_call(request, params={})`
执行注册函数的核心方法,负责解析路径、获取函数并调用。
```python
async def path_call(self, request, params={}):
```
#### 流程说明
1. 设置运行时环境(通过 `set_run_env`
2. 提取 `params_kw` 和实际请求路径
3. 截去配置中指定的前缀(`leading`),拆分剩余路径作为位置参数
4.`RegisterFunction` 中获取目标函数
5. 若函数未注册,记录错误日志并返回 `None`
6. 构造调用环境(排除敏感键)
7. 异步或同步调用函数并返回结果
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `request` | `aiohttp.web.Request` | 当前 HTTP 请求对象 |
| `params` | `dict` | 可选参数字典,可覆盖默认路径等信息 |
#### 返回值
- 函数调用结果(可以是 `Response`, `str`, `dict`, 或其他类型)
- 如果函数未注册,返回 `None`
#### 内部逻辑细节
##### 路径处理
```python
path1 = path[len(self.config_opts['leading']):]
if path1[0] == '/': path1 = path1[1:]
args = path1.split('/')
```
例如:
`request.path = "/api/v1/user/123"`,且 `leading="/api/v1"`,则 `args = ['user', '123']`
##### 函数调用环境构建
```python
env = {k:v for k,v in self.run_ns.items() if k not in ['params_kw', 'request']}
```
过滤掉 `params_kw``request`,避免重复传参冲突。
##### 协程支持
```python
if inspect.iscoroutinefunction(f):
return await f(request, params_kw, *args, **env)
return f(request, params_kw, *args, **env)
```
自动识别函数类型,确保正确调用同步或异步函数。
##### 错误处理
```python
if f is None:
error(f'{rfname=} is not registered, {rf.registKW=}')
return None
```
若函数未注册,输出调试信息并返回 `None`
---
### `datahandle(request)`
处理请求并生成响应内容的方法。
```python
async def datahandle(self, request):
x = await self.path_call(request)
if isinstance(x, web.FileResponse):
self.retResponse = x
elif isinstance(x, Response):
self.retResponse = x
else:
self.content = x
```
#### 功能
调用 `path_call` 执行业务逻辑,并根据返回值类型决定如何封装响应:
| 返回类型 | 处理方式 |
|---------|----------|
| `web.FileResponse` | 直接赋值给 `self.retResponse` |
| `Response`(含子类) | 赋值给 `self.retResponse` |
| 其他类型(如 `str`, `dict` 等) | 赋值给 `self.content`,由后续中间件或父类序列化 |
#### 参数
- `request`: `aiohttp.web.Request` 对象
#### 影响属性
- `self.retResponse`: 成功时保存响应对象
- `self.content`: 成功时保存原始内容数据
---
## 配置要求(`opts`
`FunctionProcessor` 必须接收如下配置项:
| 键名 | 类型 | 说明 |
|------|------|------|
| `leading` | `str` | 需要从路径中去除的前缀,如 `/api/v1` |
| `registerfunction` | `str` | 在 `RegisterFunction` 中注册的函数名称 |
示例配置:
```python
opts = {
"leading": "/api/v1",
"registerfunction": "my_registered_handler"
}
```
---
## 使用示例
### 注册函数示例
```python
from appPublic.registerfunction import RegisterFunction
def my_handler(request, params, *args, **env):
return web.json_response({"args": args, "env": env})
RegisterFunction().register("my_registered_handler", my_handler)
```
### 创建处理器并处理请求
```python
processor = FunctionProcessor("/api/v1/data/*", None, {
"leading": "/api/v1/data",
"registerfunction": "my_registered_handler"
})
# 在 aiohttp 路由中调用
async def handle_request(request):
await processor.datahandle(request)
if processor.retResponse:
return processor.retResponse
return web.json_response(processor.content)
```
---
## 注意事项与限制
1. **函数注册必需**:目标函数必须提前通过 `RegisterFunction().register(name, func)` 注册。
2. **函数签名要求**:被调用函数应接受 `(request, params_kw, *args, **kwargs)` 形式的参数。
3. **路径前缀必须匹配**`leading` 必须准确匹配请求路径开头,否则可能导致参数解析异常。
4. **非标准返回值需手动处理**:若返回非 `Response` 类型,需确保上游能正确序列化 `self.content`
---
## 日志输出
该类使用 `appPublic.log` 模块进行日志记录:
- `error`: 函数未注册时输出详细错误信息
- (其他日志级别暂未使用)
建议开启调试日志以便追踪调用过程。
---
## 总结
`FunctionProcessor` 提供了一种灵活的函数驱动式 Web 接口设计模式,适合微服务、插件化系统或需要动态绑定逻辑的场景。结合 `RegisterFunction` 机制,实现了低耦合、高扩展性的请求处理架构。
---
> ✅ 文档版本1.0
> 📅 最后更新2025-04-05