以下是为提供的 `OAuthClient` 类编写的 **Markdown 格式技术文档**,涵盖了类的功能、设计思想、使用方式、参数说明及示例等内容。 --- # `OAuthClient` 技术文档 `OAuthClient` 是一个基于 HTTP(S) 的异步客户端工具类,用于调用由第三方服务发布的 RESTful API 接口。它通过预定义的接口描述(`desc`)动态构建请求,并解析响应数据,支持自定义数据转换、错误判断逻辑和返回结构处理。 该类广泛应用于需要与 OAuth 认证或标准 JSON API 交互的服务中,具有良好的可配置性和扩展性。 --- ## 📦 模块依赖 ```python import json from appPublic.httpclient import HttpClient, RESPONSE_TEXT, RESPONSE_JSON, RESPONSE_BIN, RESPONSE_FILE, RESPONSE_STREAM, HttpError from appPublic.argsConvert import ArgsConvert from appPublic.dictObject import DictObject ``` - `HttpClient`: 异步 HTTP 客户端,支持多种响应类型。 - `ArgsConvert`: 用于模板字符串替换(如 `${key}`)。 - `DictObject`: 支持通过属性访问字典键值的对象封装。 --- ## 🧱 类定义 ```python class OAuthClient: def __init__(self, desc, converters={}) ``` ### 构造函数参数 | 参数 | 类型 | 说明 | |------|------|------| | `desc` | `dict` | 接口描述对象,必须包含 `data` 字段,定义了基础数据和各 API 方法的配置。详见下方“接口描述格式”章节。 | | `converters` | `dict[str, callable]` | 可选的数据转换函数映射表,键为转换器名称,值为函数引用。 | > ⚠️ 注意:`desc['data']` 必须存在,否则会抛出断言错误。 --- ## 🔧 接口描述格式 (`desc`) `desc` 是一个字典,其结构如下: ```json { "data": { /* 基础共享数据,可在请求时被引用 */ }, "method_name": { "path": "/api/v1/users/${userId}", "method": "GET", // 可选,默认 GET "headers": [ ... ], "params": [ ... ], "data": [ ... ], // 请求体中的数据 "resp": [ { "name": "result", "resp_keys": ["user", "name"], "converter": "to_upper" // 可选 } ], "error_if": { "error_keys": ["status"], "op": "==", // 比较操作符:'==' 或 '!=' "value": "fail", "code_keys": ["code"], // 错误码路径 "msg_keys": ["message"] // 错误信息路径 } } } ``` ### 字段说明 | 字段 | 类型 | 说明 | |------|------|------| | `data` | `dict` | 全局上下文数据,可用于模板替换。 | | `method_name` | `dict` | 每个 API 接口的配置项,方法名作为 key。 | |   `path` | `str` | 接口路径,支持 `${}` 占位符变量。 | |   `method` | `str` | HTTP 方法(GET/POST/PUT/DELETE 等),默认为 `GET`。 | |   `headers` | `list[dict]` | 请求头列表,每个元素形如 `{name: "Header-Key", value: "HeaderValue", converter?: "funcName"}` | |   `params` | `list[dict]` | URL 查询参数,格式同上。 | |   `data` | `list[dict]` | 请求体数据(JSON 格式),格式同上。 | |   `resp` | `list[dict]` | 响应数据提取规则。 | |     `name` | `str` | 返回结果中字段的名称。 | |     `resp_keys` | `list[str]` | 在响应 JSON 中获取数据的路径(嵌套键)。 | |     `converter` | `str` | 转换函数名(需在 `converters` 中注册)。 | |   `error_if` | `dict` | 自定义错误判断条件。 | |     `error_keys` | `list[str]` | 提取用于比较的响应字段路径。 | |     `op` | `str` | 比较操作符:`==` 或 `!=`。 | |     `value` | any | 期望值,用于对比。 | |     `code_keys` | `list[str]` | 错误码字段路径(可选)。 | |     `msg_keys` | `list[str]` | 错误消息字段路径(可选)。 | --- ## 🧩 核心方法 ### `__call__(self, host, mapi, params) -> dict` 发起一次 API 调用。 #### 参数 | 参数 | 类型 | 说明 | |------|------|------| | `host` | `str` | 服务主机地址,例如 `"https://api.example.com"` | | `mapi` | `str` | 接口方法名,对应 `desc` 中的键名。 | | `params` | `dict` | 动态传入的参数,用于替换模板和构造请求。 | #### 返回值 成功时: ```json { "status": "ok", "data": { /* 根据 resp 配置提取的数据 */ } } ``` 失败时: ```json { "status": "error", "code": "400", "message": "Bad Request" } ``` #### 流程说明 1. 查找 `desc[mapi]` 是否存在; 2. 使用 `datalize()` 替换路径中的 `${}` 变量; 3. 构造完整 URL; 4. 处理 headers、params、data(应用转换器); 5. 发起异步 HTTP 请求; 6. 解析响应为 `DictObject`; 7. 检查是否符合 `error_if` 条件; 8. 按照 `resp` 规则提取并转换返回数据。 > 📌 日志输出:请求详情(URL、method、params 等)将打印到控制台。 --- ### `setup_req_data(data, ns) -> dict or None` 将一组 `{name, value, converter}` 结构的数据根据命名空间 `ns` 进行模板替换和转换。 #### 参数 - `data`: 数据项列表(如 headers、params) - `ns`: 命名空间(通常是 `params`) #### 内部调用 - `setup_req_kv()`: 处理单个键值对,执行模板替换和转换。 --- ### `datalize(dic, data={}) -> dict` 执行模板替换(`${key}` → 实际值)。 #### 说明 - 合并 `self.data`(全局数据)与传入的 `data`; - 使用 `ArgsConvert('${', '}')` 执行替换; - 支持嵌套字典和字符串。 #### 示例 ```python self.data = {'token': 'abc123'} dic = {'url': '/api?tk=${token}'} result = self.datalize(dic) # {'url': '/api?tk=abc123'} ``` --- ### `get_resp_data(resp, keys, converter=None)` 从响应对象中按 `keys` 路径提取数据,并可选地进行转换。 #### 参数 - `resp`: `DictObject` 类型的响应体; - `keys`: 键路径列表,如 `['data', 'user', 'id']`; - `converter`: 转换函数名。 #### 示例 ```python resp = DictObject(data={"user": {"name": "alice"}}) value = get_resp_data(resp, ['data', 'user', 'name']) # "alice" ``` --- ### `setup_return_data(resp) -> dict` 根据 `api.resp` 配置,构建最终返回的 `data` 对象。 #### 示例配置 ```json "resp": [ { "name": "username", "resp_keys": ["data", "user", "name"], "converter": "to_upper" } ] ``` #### 输出 ```json { "status": "ok", "data": { "username": "ALICE" } } ``` --- ### `check_if_error(resp) -> dict or None` 检查响应是否满足预设的错误条件。 #### 判断逻辑 ```python v = resp.get_data_by_keys(ei.error_keys) if ei.op == '==' and v == ei.value → 触发错误 if ei.op == '!=' and v != ei.value → 触发错误 ``` #### 成功返回 - `None` 表示无错误。 #### 失败返回 ```json { "status": "error", "code": "...", "message": "..." } ``` > 💡 支持从响应中提取 `code` 和 `message`。 --- ### `set_data(resp_data, data_desc)` 将响应中的某些字段保存回 `self.data`,用于后续请求共享状态(如 token 刷新)。 #### 参数 - `resp_data`: 原始响应数据(dict) - `data_desc`: 描述如何映射字段 ```python data_desc = [ {'field': 'access_token', 'name': 'token'}, {'field': 'expires_in', 'name': 'expires'} ] ``` 执行后: ```python self.data['token'] = resp_data['access_token'] self.data['expires'] = resp_data['expires_in'] ``` --- ## ✅ 使用示例 ### 1. 定义接口描述 ```python desc = { "data": { "client_id": "your_client_id", "client_secret": "your_secret" }, "get_user": { "path": "/users/${user_id}", "method": "GET", "headers": [ {"name": "Authorization", "value": "Bearer ${token}"} ], "resp": [ { "name": "userInfo", "resp_keys": ["data"], "converter": "strip_data" } ], "error_if": { "error_keys": ["status"], "op": "==", "value": "error", "code_keys": ["error_code"], "msg_keys": ["error_msg"] } } } ``` ### 2. 定义转换器 ```python def to_upper(s): return s.upper() if isinstance(s, str) else s converters = { "to_upper": to_upper, "strip_data": lambda x: x.get('data') if isinstance(x, dict) else x } ``` ### 3. 创建并调用客户端 ```python client = OAuthClient(desc, converters=converters) result = await client( host="https://api.example.com", mapi="get_user", params={ "user_id": "123", "token": "xyz987" } ) print(result) # 输出: # { # "status": "ok", # "data": { # "userInfo": { ... } # } # } ``` --- ## 🛠️ 调试信息 所有请求细节将通过 `print()` 输出: ```python print(f'{url=}, {method=}, {myparams=}, {mydata=}, {myheaders=}') ``` 建议在生产环境中移除或替换为日志记录。 --- ## ❗ 异常处理 - 若指定的 `mapi` 未在 `desc` 中定义,抛出 `Exception`; - HTTP 请求异常捕获为 `HttpError`,返回标准错误结构; - 网络级错误(如连接超时)也会被捕获并返回 `"https error"`。 --- ## 🧩 扩展建议 - 添加日志模块替代 `print()`; - 支持更多响应类型(如文件下载); - 增加中间件钩子(before_request / after_response); - 支持分页自动迭代。 --- ## 📚 总结 `OAuthClient` 是一个灵活、声明式的 API 客户端封装,适用于微服务间通信、第三方接口集成等场景。通过配置驱动的方式,实现了解耦与复用,适合构建统一的外部服务网关层。 --- > 📎 版本:1.0 > 📅 最后更新:2025年4月5日 > © 2025 Your Company. All rights reserved.