增加宽容的schema验证
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,38 @@
|
||||
from typing import Any, List
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def extract_data_for_validation(response_json: Any) -> List[Any]:
|
||||
"""
|
||||
从原始API响应JSON中智能提取需要被验证的核心业务数据列表。
|
||||
即使只有一个对象,也返回一个单元素的列表。
|
||||
|
||||
策略:
|
||||
1. 如果响应体是包含 'code' 和 'data' 的标准包装,则提取 'data' 的内容。
|
||||
2. 如果处理后的数据是列表,直接返回该列表。
|
||||
3. 如果处理后的数据是单个对象(字典),将其包装在单元素列表中返回。
|
||||
4. 如果数据为空或不适用,返回空列表。
|
||||
"""
|
||||
if not response_json:
|
||||
return []
|
||||
|
||||
data_to_process = response_json
|
||||
|
||||
# 策略 1: 解开标准包装
|
||||
if isinstance(response_json, dict) and 'code' in response_json and 'data' in response_json:
|
||||
logger.debug("检测到标准响应包装,提取 'data' 字段内容进行处理。")
|
||||
data_to_process = response_json['data']
|
||||
|
||||
# 策略 2: 统一返回列表
|
||||
if isinstance(data_to_process, list):
|
||||
logger.debug(f"数据本身为列表,包含 {len(data_to_process)} 个元素,直接返回。")
|
||||
return data_to_process
|
||||
|
||||
if isinstance(data_to_process, dict):
|
||||
logger.debug("数据为单个对象,将其包装在列表中返回。")
|
||||
return [data_to_process]
|
||||
|
||||
# 对于其他情况(如数据为None或非对象/列表类型),返回空列表
|
||||
logger.warning(f"待处理的数据既不是列表也不是对象,无法提取进行验证。数据: {str(data_to_process)[:100]}")
|
||||
return []
|
||||
@@ -0,0 +1,60 @@
|
||||
import logging
|
||||
import json
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
class SchemaProvider:
|
||||
def __init__(self, global_api_spec: Dict[str, Any]):
|
||||
self.global_api_spec = global_api_spec
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def get_schema(self, endpoint_spec: Dict[str, Any], status_code: int) -> Optional[Dict]:
|
||||
"""
|
||||
获取端点在特定状态码下的响应Schema。
|
||||
|
||||
当前实现:从API规范中查找。
|
||||
未来可扩展:优先从动态映射中获取,如果失败或未配置,则回退到当前实现。
|
||||
"""
|
||||
# --- 预留的动态获取逻辑扩展点 ---
|
||||
# if self._use_dynamic_provider(endpoint_spec):
|
||||
# schema = self._fetch_dynamic_schema(endpoint_spec)
|
||||
# if schema:
|
||||
# return schema
|
||||
# ---------------------------------
|
||||
|
||||
return self._get_schema_from_spec(endpoint_spec, status_code)
|
||||
|
||||
def _get_schema_from_spec(self, endpoint_spec: Dict[str, Any], status_code: int) -> Optional[Dict]:
|
||||
"""
|
||||
(私有方法) 从API规范中提取Schema,这是当前版本的主要实现。
|
||||
"""
|
||||
self.logger.debug(f"尝试从API规范中为状态码 {status_code} 查找Schema。")
|
||||
|
||||
expected_schema = None
|
||||
|
||||
# 兼容 OpenAPI/Swagger 格式
|
||||
if 'responses' in endpoint_spec:
|
||||
responses = endpoint_spec['responses']
|
||||
# 优先匹配精确的状态码
|
||||
if str(status_code) in responses:
|
||||
response_def = responses[str(status_code)]
|
||||
if 'content' in response_def and 'application/json' in response_def['content']:
|
||||
expected_schema = response_def['content']['application/json'].get('schema')
|
||||
# 回退到 'default'
|
||||
elif 'default' in responses:
|
||||
response_def = responses['default']
|
||||
if 'content' in response_def and 'application/json' in response_def['content']:
|
||||
expected_schema = response_def['content']['application/json'].get('schema')
|
||||
|
||||
# 兼容 YAPI 格式 (简化)
|
||||
elif 'res_body_type' in endpoint_spec and endpoint_spec['res_body_type'] == 'json':
|
||||
if endpoint_spec.get('res_body_is_json_schema') and endpoint_spec.get('res_body'):
|
||||
try:
|
||||
expected_schema = json.loads(endpoint_spec['res_body'])
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
self.logger.error(f"从YAPI res_body解析JSON schema失败。res_body: {endpoint_spec['res_body']}")
|
||||
return None # 解析失败
|
||||
|
||||
if not expected_schema:
|
||||
self.logger.info(f"在API规范中未找到针对状态码 {status_code} 的JSON schema。")
|
||||
|
||||
return expected_schema
|
||||
Reference in New Issue
Block a user