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

458 lines
12 KiB
Markdown
Raw Permalink 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.

# 技术文档Web 应用核心工具模块
```markdown
# Web 核心工具模块技术文档
> **文件编码**UTF-8
> **语言**Python 3.7+(基于 `aiohttp` 异步框架)
> **用途**:为异步 Web 服务提供通用工具函数、配置管理、安全处理、文件操作及数据库上下文支持。
---
## 模块概览
该模块是 Web 后端系统的核心基础组件,封装了以下功能:
- HTTP 错误响应生成
- 文件读写与下载控制
- 数据导出为 Excel
- 配置读取与动态变量替换
- 密码加解密
- 全局环境初始化
- 数据库连接池集成
- 流式响应支持
- 安全路径校验机制
适用于基于 `aiohttp` 构建的异步 Web 服务架构。
---
## 依赖说明
### 第三方库
| 包名 | 用途 |
|------|------|
| `aiohttp` | 异步 Web 服务框架 |
| `aiohttp-session` | Session 管理 |
| `openpyxl` | Excel 文件生成 |
| `asyncio` | 异步编程支持 |
### 内部模块
| 模块路径 | 功能 |
|--------|------|
| `appPublic.*` | 公共工具类(配置、日志、编码、时间等) |
| `sqlor.*` | 数据库抽象层ORM/查询构造器) |
| `.xlsxData` | Excel 数据解析器 |
| `.uriop` | URI 操作接口 |
| `.error` | 自定义错误类型封装 |
| `.filetest`, `.filedownload`, `.filestorage` | 文件相关操作 |
| `.serverenv` | 服务器运行环境单例 |
---
## 函数与类详解
### `server_error(errcode)`
根据 HTTP 状态码抛出对应的 `aiohttp.web.HTTPException`
#### 参数:
- `errcode` (int): HTTP 状态码(如 404, 500
#### 支持状态码列表:
| 状态码 | 异常类 |
|-------|--------|
| 400 | `HTTPBadRequest` |
| 401 | `HTTPUnauthorized` |
| 403 | `HTTPForbidden` |
| 404 | `HTTPNotFound` |
| 405 | `HTTPMethodNotAllowed` |
| 408 | `HTTPRequestTimeout` |
| 409 | `HTTPConflict` |
| 410 | `HTTPGone` |
| 415 | `HTTPUnsupportedMediaType` |
| 429 | `HTTPTooManyRequests` |
| 500 | `HTTPInternalServerError` |
| 502 | `HTTPBadGateway` |
| 503 | `HTTPServiceUnavailable` |
> 默认返回 `HTTPException`,其他未列状态码也映射为此类。
---
### `basic_auth_headers(user, passwd)`
生成用于 Basic Auth 的请求头。
#### 参数:
- `user` (str): 用户名
- `passwd` (str): 密码
#### 返回值:
```python
{
"Authorization": "Basic base64encoded"
}
```
---
### `stream_response(request, async_data_generator, content_type='text/html')`
异步流式响应处理器,用于大内容或实时数据传输。
#### 参数:
- `request`: aiohttp 请求对象
- `async_data_generator`: 异步生成器函数(`async def()`),产出 `bytes`, `str` 或 JSON 对象
- `content_type` (str): 响应内容类型,默认 `'text/html'`
#### 行为:
- 自动判断输出类型并编码为 UTF-8
- 出错时记录异常并通过 `write_eof()` 结束流
- 支持 JSON 直接序列化(非 ASCII 不转义)
> ⚠️ 注意:若生成器内部报错会中断流并抛出异常。
---
### `data2xlsx(rows, headers=None)`
将数据行列表导出为临时 `.xlsx` 文件。
#### 参数:
- `rows`: 字典列表,例如 `[{'name': 'Alice', 'age': 30}]`
- `headers`: 列定义列表,每个元素可含 `.name``.title` 属性
#### 返回值:
- 生成的 `.xlsx` 文件绝对路径(使用 `tempfile.mktemp` 创建)
> 使用 `openpyxl` 写入,自动关闭工作簿。建议后续由调用方清理临时文件。
---
### `save_file(str_or_bytes, filename)`
异步保存字符串或字节到文件存储系统。
#### 参数:
- `str_or_bytes`: 要保存的内容(`str``bytes`
- `filename`: 文件名(带扩展名)
#### 返回值:
- 存储结果信息(由 `FileStorage.save()` 定义)
> 封装了异步 IO 操作,适合上传场景。
---
### `webpath(path)` / `realpath(path)`
获取文件在 Web 中的访问路径和实际物理路径。
#### 参数:
- `path` (str): 相对路径
#### 返回值:
- `webpath`: 可通过浏览器访问的 URL 路径
- `realpath`: 服务器上的真实文件系统路径
> 基于 `FileStorage` 实现路径映射。
---
### `FileOutZone(fp)`
自定义异常类,防止越权访问文件系统目录。
#### 触发条件:
当尝试打开的文件不在允许目录范围内时抛出。
#### 属性:
- `openfilename`: 被拒绝访问的文件路径
---
### `get_config_value(kstr)`
从全局配置中按点分键获取嵌套值。
#### 示例:
```python
get_config_value("database.host")
# => getConfig().get('database').get('host')
```
#### 返回值:
- 成功找到则返回对应值
- 找不到任一级键则返回 `None`
---
### `get_definition(k)`
快捷方式获取配置中的 `definitions.{k}` 节点。
#### 示例:
```python
get_definition("userSchema")
# 等价于 get_config_value("definitions.userSchema")
```
---
### `abspath(path)`
根据配置中的网站根路径查找文件的实际路径。
#### 配置要求:
- `config.website.paths`: 字符串路径列表(相对或绝对)
- 尝试拼接每个根路径 + 输入 path检查是否存在
#### 返回值:
- 找到存在的文件则返回其完整路径
- 否则返回 `None`
---
### `openfile(url, m)`
安全地打开一个本地文件,具备路径白名单校验。
#### 参数:
- `url` (str): 相对路径(相对于 `website.paths``allow_folders`
- `m` (str): 文件打开模式(如 `'r'`, `'rb'`
#### 安全校验流程:
1. 解析为绝对路径(通过 `abspath`
2. 获取所有允许的根目录(包括 `website.paths``allow_folders`
3. 检查目标路径是否以任意允许目录开头
4. 若不满足,抛出 `FileOutZone`
> 防止路径穿越攻击(如 `../../../etc/passwd`
---
### `isNone(a)`
辅助函数,判断变量是否为 `None`
#### 返回值:
- `True` if `a is None`
- `False` otherwise
> 主要用于模板引擎或表达式中避免语法限制。
---
### `appname()`
获取当前应用名称。
#### 来源:
- `config.license.app`
- 失败时返回默认 `"test app"`
---
### `configValue(ks)`
执行类似 `eval('config' + ks)` 的安全配置提取。
#### 示例:
```python
configValue(".database.port")
# => getConfig().database.port
```
> ⚠️ 警告:存在潜在代码注入风险,请确保输入可信!
---
### `visualcoding()`
获取配置项 `config.website.visualcoding` 的值。
通常用于前端可视化开发开关。
---
### `file_download(request, path, name, coding='utf8')`
【已弃用】同步方式发送文件给客户端(兼容旧版 Twisted 风格 API
#### 参数:
- `request`: 请求对象(需有 `setHeader`, `write`, `finish` 方法)
- `path`: 文件相对路径
- `name`: 下载显示名称
- `coding`: 名称编码格式(默认 UTF-8
#### 设置响应头:
- `Content-Disposition: attachment; filename=...`
- 缓存控制、内容长度、二进制传输标识等
> ❌ 不推荐新代码使用,建议改用 `path_download` 或流式方案。
---
### `paramify(data, ns)`
使用模板语法 `${key}$` 替换数据中的占位符。
#### 参数:
- `data`: 包含占位符的字符串或结构化数据dict/list
- `ns`: 命名空间dict提供替换值
#### 示例:
```python
paramify("Hello ${name}$!", {"name": "World"})
# => "Hello World!"
```
> 支持嵌套结构递归替换。
---
### `password_encode(s)` / `password_decode(c)`
RC4 加密/解密封装。
#### 密钥来源:
- `config.password_key`
- 默认密钥:`QRIVSRHrthhwyjy176556332`
#### 用途:
- 敏感字段加密存储如密码、token
- 安全参数传递
---
### `@asynccontextmanager sqlorContext(module)`
异步上下文管理器,获取指定模块关联数据库的 `SqlOR` 实例。
#### 工作流程:
1. 获取 `DBPools` 连接池
2. 查询 `ServerEnv` 中模块对应的数据库名
3. 获取该库的 ORM 上下文(`sor`
#### 使用示例:
```python
async with sqlorContext('user') as sor:
users = await sor.select('users', cond={'active': True})
```
---
### `initEnv()`
初始化全局运行环境(`ServerEnv` 单例),注入大量工具函数与常量。
#### 注入内容分类:
| 类别 | 示例 |
|------|------|
| 工具函数 | `paramify`, `data2xlsx`, `uuid` |
| 时间处理 | `curDateString`, `str2date`, `timestampstr` |
| 数据结构 | `DictObject`, `uObject` |
| 文件操作 | `abspath`, `openfile`, `webpath` |
| 数据库 | `DBPools`, `DBFilter` |
| 错误类 | `Success`, `Error`, `NeedLogin` |
| HTTP 工具 | `HttpClient`, `StreamHttpClient`, `basic_auth_headers` |
| 异步支持 | `async_sleep`, `stream_response` |
| 其他 | `rfexe`(注册函数执行器) |
> 此函数应在应用启动时调用一次。
---
### `set_builtins()`
将 Python 内置函数(如 `print`, `len`, `isinstance`)注入到 `ServerEnv()` 全局命名空间。
#### 实现原理:
- 遍历 `builtins` 模块公开符号
- 使用 `exec()` 动态绑定至 `g[key] = builtin_func`
#### 示例效果:
```python
g = ServerEnv()
g.print("Hello") # 实际调用内置 print
g.len([1,2,3]) # 调用内置 len
```
> 便于在模板或 DSL 中统一访问内置函数。
---
## 使用建议
### ✅ 推荐实践
- 使用 `initEnv()` 初始化全局环境后,可通过 `ServerEnv()` 统一获取工具集
- 文件下载优先使用 `path_download``file_download`(新版本)
- 敏感数据加解密务必使用 `password_encode/decode`
- 大数据响应使用 `stream_response` 避免内存溢出
### ⚠️ 注意事项
- `configValue()` 使用 `eval`,请严格验证输入
- `mktemp()` 在高并发下可能有命名冲突风险,建议升级为 `NamedTemporaryFile(delete=False)`
- `file_download` 当前实现依赖非标准接口(`setHeader`),仅适配特定框架
---
## 示例:导出用户数据为 Excel 并下载
```python
async def export_users(request):
env = ServerEnv()
# 查询数据
async with env.sqlorContext('user') as sor:
rows = await sor.select('users', fields=['id', 'name', 'email'])
# 定义表头
headers = [
{'name': 'id', 'title': '编号'},
{'name': 'name', 'title': '姓名'},
{'name': 'email', 'title': '邮箱'}
]
# 生成 Excel
xlsx_path = env.data2xlsx(rows, headers)
# 返回文件下载
return await env.path_download(request, xlsx_path, '用户列表.xlsx')
```
---
## 版本信息
- **创建日期**:未知(根据代码风格推测为 2020~2022
- **维护状态**:活跃使用中
- **作者**:内部团队开发(依赖 `appPublic`, `sqlor` 私有库)
---
## 附录 AHTTP 状态码映射表
| Code | Meaning | Exception Class |
|------|---------|------------------|
| 400 | Bad Request | HTTPBadRequest |
| 401 | Unauthorized | HTTPUnauthorized |
| 403 | Forbidden | HTTPForbidden |
| 404 | Not Found | HTTPNotFound |
| 405 | Method Not Allowed | HTTPMethodNotAllowed |
| 408 | Timeout | HTTPRequestTimeout |
| 409 | Conflict | HTTPConflict |
| 410 | Gone | HTTPGone |
| 415 | Unsupported Media Type | HTTPUnsupportedMediaType |
| 429 | Too Many Requests | HTTPTooManyRequests |
| 500 | Internal Error | HTTPInternalServerError |
| 502 | Bad Gateway | HTTPBadGateway |
| 503 | Service Unavailable | HTTPServiceUnavailable |
---
## 附录 B全局环境注入清单部分
| 名称 | 类型 | 来源 |
|------|------|------|
| `json` | module | built-in |
| `time` | module | built-in |
| `random` | module | built-in |
| `datetime` | module | built-in |
| `paramify` | function | local |
| `curDateString` | function | timeUtils |
| `getID` | function | uniqueID |
| `Error`, `Success` | class | .error |
| `HttpClient` | class | httpclient |
| `stream_response` | coroutine | local |
| `DBPools` | class | sqlor.dbpools |
> 完整列表见 `initEnv()` 函数体。
---
```
> 📝 文档结束。此文档可用于团队 Wiki、API 手册或项目交接资料。