113 lines
6.5 KiB
Markdown
113 lines
6.5 KiB
Markdown
# 设计文档:响应适配层 (Response Adaptation Layer)
|
||
|
||
## 1. 问题背景
|
||
|
||
在对企业内部大量存量的API进行合规性测试时,我们遇到了一个常见的挑战:API的设计风格存在不一致性。具体表现为:
|
||
|
||
1. **响应体结构不统一**:
|
||
* 部分API遵循标准的包装格式,如 `{"code": 0, "message": "success", "data": {...}}`,真正的业务数据位于 `data` 字段内。
|
||
* 另一部分API则直接返回业务数据对象,如 `{...}`。
|
||
* 当返回列表时,业务数据可能是 `data: [...]`,也可能是直接返回 `[...]`。
|
||
|
||
2. **数据模型(Schema)来源不统一**:
|
||
* 标准的API会在其OpenAPI/Swagger规范中直接定义响应体的Schema。
|
||
* 但存在一类特殊的"核心模型"API,其响应数据对应的Schema需要通过调用另一个独立的API端点来动态获取。
|
||
|
||
这些不一致性给自动化的Schema验证带来了极大的困难。如果为每种情况都编写一个独立的测试用例,会导致代码大量冗余,且难以维护。
|
||
|
||
## 2. 核心理念与设计目标
|
||
|
||
为了优雅地解决上述问题,我们引入 **"响应适配层" (Response Adaptation Layer)** 的概念。
|
||
|
||
其核心理念是将 **"适配"** 过程(即,如何找到真正需要验证的数据和其对应的Schema)与核心的 **"验证"** 逻辑(即,数据是否符合Schema的规范)完全解耦。
|
||
|
||
**设计目标**:
|
||
* **灵活性与可扩展性**:能够轻松适应未来可能出现的更多样的API风格,只需增加新的适配策略,而无需修改核心验证框架。
|
||
* **高内聚,低耦合**:将适配逻辑集中处理,保持核心验证测试用例的纯净和通用。
|
||
* **配置驱动**:适配规则应通过外部配置来定义,而非硬编码在代码中。
|
||
|
||
## 3. 架构设计
|
||
|
||
响应适配层位于 "API调用" 和 "测试断言" 之间。它接收原始的HTTP响应和API端点信息,输出归一化后的数据和Schema,供下游的验证引擎使用。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
subgraph 测试执行流程
|
||
A[调用API获取响应] --> B{响应适配层};
|
||
|
||
subgraph B
|
||
C[响应数据提取器]
|
||
D[动态Schema提供者]
|
||
end
|
||
|
||
B --> E[归一化的数据和Schema];
|
||
E --> F[执行Schema验证];
|
||
end
|
||
```
|
||
|
||
## 4. 核心组件详述
|
||
|
||
### 4.1. 响应数据提取器 (Response Data Extractor)
|
||
|
||
这是一个工具模块,提供一个核心函数 `extract_data_for_validation(response_json)`,负责从原始API响应中智能地提取出需要被验证的核心业务数据。
|
||
|
||
**提取策略**:
|
||
1. **检查标准包装**:检查响应体 `response_json` 是否同时包含 `code` 和 `data` 字段。
|
||
* **是**:返回 `response_json['data']` 的内容作为待处理数据。
|
||
* **否**:返回整个 `response_json` 作为待处理数据。
|
||
2. **处理列表与对象**:对上一步获取的待处理数据进行检查。
|
||
* **是列表 (List)**:
|
||
* 如果列表为空 `[]`,则认为无法进行Schema验证,直接返回 `None`,跳过验证。
|
||
* 如果列表非空,则返回列表的第一个元素 `list[0]` 作为最终需要验证的数据。这是基于"一个列表内的所有元素都应遵循相同Schema"的假设。
|
||
* **是对象 (Dict)**:直接返回该对象作为最终需要验证的数据。
|
||
|
||
### 4.2. 动态Schema提供者 (Dynamic Schema Provider)
|
||
|
||
这是一个独立的模块,负责根据API端点信息,为其提供正确的JSON Schema,特别是在Schema需要从外部动态获取的场景下。
|
||
|
||
**工作机制**:
|
||
1. **配置驱动**:在测试项目的配置文件中,增加一个映射关系表 `dynamic_schema_mapping`,用于定义哪个API端点(或哪种模式的API)的Schema需要从哪个URL获取。
|
||
|
||
*示例配置 (config.yaml)*:
|
||
```yaml
|
||
dynamic_schema_mapping:
|
||
# key可以是精确路径,也可以是正则表达式
|
||
"/api/v1/core-models/get/{model_id}": "https://api.internal/schemas/{model_id}"
|
||
"/api/v2/special-data/.*": "https://api.internal/schemas/special-data.json"
|
||
```
|
||
|
||
2. **按需获取**: 当测试用例需要Schema时,提供者会检查当前测试的API端点是否在 `dynamic_schema_mapping` 中有匹配项。
|
||
* **有匹配**: 根据配置的URL模板(例如,从路径中提取 `model_id`)构造完整的Schema URL,然后发起HTTP GET请求获取Schema内容。
|
||
* **无匹配**: 回退到默认行为,即从当前API的OpenAPI规范中查找其定义的响应Schema。
|
||
|
||
3. **缓存机制**: 为了避免对同一个Schema URL的重复网络请求,提供者内部会维护一个缓存(如一个简单的字典)。在发起请求前,会先检查缓存中是否已有该URL的Schema。
|
||
|
||
## 5. 整合与实现
|
||
|
||
我们将创建一个新的、高度灵活的测试用例 `FlexibleSchemaValidationCase` 来整合上述两个组件。
|
||
|
||
**实现步骤**:
|
||
1. **测试用例识别**: 在 `applies_to` 方法中,定义此测试用例适用于哪些API。我们可以通过在OpenAPI规范中使用一个自定义扩展字段 `x-flexible-validation: true` 来标记目标API。
|
||
|
||
*示例 (openapi.yaml)*:
|
||
```yaml
|
||
paths:
|
||
/api/v1/core-models/get/{model_id}:
|
||
get:
|
||
summary: "获取核心模型数据"
|
||
x-flexible-validation: true # 标记适用此测试用例
|
||
...
|
||
```
|
||
2. **执行逻辑 (`execute` 方法)**:
|
||
a. 调用 **动态Schema提供者**,传入当前端点信息,获取对应的JSON Schema。如果获取失败,则测试用例断言失败。
|
||
b. 正常调用该API,获得HTTP响应。
|
||
c. 将响应体JSON传递给 **响应数据提取器**,获取归一化后的核心业务数据。如果返回`None`(例如空列表),则该测试用例标记为 `SKIPPED`。
|
||
d. 使用标准的 `jsonschema.validate` 函数,用步骤a获取的Schema来验证步骤c提取的数据。
|
||
e. 根据验证结果,断言测试成功或失败。
|
||
|
||
## 6. 方案优势总结
|
||
|
||
* **关注点分离**:将复杂的适配逻辑与核心的验证逻辑解耦,代码更清晰,可维护性更高。
|
||
* **高度可扩展**:未来若有新的API风格,只需在"提取器"或"提供者"中增加新策略,而无需改动大量测试用例。
|
||
* **配置灵活**:通过外部配置文件和API规范中的自定义字段来驱动,适应性强,无需硬编码。
|
||
* **代码复用**:将通用的适配逻辑沉淀到工具库中,避免在不同测试用例中重复实现。 |