#!/usr/bin/env python # -*- coding: utf-8 -*- """ 规则执行演示脚本 本脚本演示如何使用增强后的规则库执行API测试,包括性能规则、安全规则、RESTful设计规则和错误处理规则的执行。 脚本模拟了一系列API请求和响应场景,并使用规则执行引擎验证这些场景是否符合预定义的规则。 """ import time import json import logging import argparse from dataclasses import dataclass, asdict from typing import Dict, List, Any, Optional import requests from urllib.parse import urlparse # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # ------------------ 模拟 API 调用相关类 ------------------ @dataclass class APIRequest: """API请求类,用于构建和发送API请求""" method: str url: str headers: Optional[Dict[str, str]] = None params: Optional[Dict[str, Any]] = None json_data: Optional[Any] = None body: Optional[Any] = None data: Optional[Dict[str, Any]] = None @dataclass class APIResponse: """API响应类,用于存储API响应结果""" status_code: int headers: Dict[str, str] content: str elapsed_time: float json_content: Optional[Dict[str, Any]] = None request: Optional[APIRequest] = None def __post_init__(self): if self.content and (self.headers.get('Content-Type', '').startswith('application/json') or self.content.strip().startswith('{')): try: self.json_content = json.loads(self.content) except json.JSONDecodeError: self.json_content = None class APICaller: """API调用器,用于发送API请求并获取响应""" def call_api(self, request: APIRequest) -> APIResponse: """模拟调用API,返回响应结果""" logger.info(f"发送 {request.method} 请求到 {request.url}") # 这里只是模拟,实际项目中应使用真实的HTTP请求 # 根据URL创建不同的模拟响应 start_time = time.time() # 模拟不同的API响应场景 if "users" in request.url: response = self._mock_user_api_response(request) elif "slow" in request.url: # 模拟慢响应 time.sleep(0.6) # 模拟600ms响应时间 response = self._mock_slow_api_response(request) elif "error" in request.url: # 模拟错误响应 response = self._mock_error_api_response(request) elif "http://" in request.url: # 模拟HTTP响应(非HTTPS) response = self._mock_http_api_response(request) else: # 默认响应 response = self._mock_default_api_response(request) elapsed_time = time.time() - start_time response.elapsed_time = elapsed_time response.request = request return response def _mock_user_api_response(self, request: APIRequest) -> APIResponse: """模拟用户API响应""" headers = { 'Content-Type': 'application/json', 'X-Request-ID': '12345', 'X-Rate-Limit-Remaining': '1000' } content = json.dumps({ "id": 123, "name": "Test User", "email": "user@example.com", "created_at": "2023-01-01T00:00:00Z", "status": "active" }) return APIResponse( status_code=200, headers=headers, content=content, elapsed_time=0.1 ) def _mock_slow_api_response(self, request: APIRequest) -> APIResponse: """模拟慢响应API""" headers = { 'Content-Type': 'application/json', 'X-Request-ID': '54321', 'X-Rate-Limit-Remaining': '999' } content = json.dumps({ "id": 456, "name": "Test User", "email": "slow@example.com", "created_at": "2023-01-02T00:00:00Z", "status": "active", "data": [1, 2, 3, 4, 5] * 100 # 添加一些数据使响应大一些 }) return APIResponse( status_code=200, headers=headers, content=content, elapsed_time=0.6 # 模拟600ms响应时间 ) def _mock_error_api_response(self, request: APIRequest) -> APIResponse: """模拟错误响应API""" headers = { 'Content-Type': 'application/json', 'X-Request-ID': '67890' } # 标准错误响应格式 content = json.dumps({ "code": "user_not_found", "message": "User not found", "details": { "resource": "User", "id": request.params.get("id", "unknown") if request.params else "unknown" } }) return APIResponse( status_code=404, headers=headers, content=content, elapsed_time=0.05 ) def _mock_http_api_response(self, request: APIRequest) -> APIResponse: """模拟HTTP(非HTTPS)API响应""" headers = { 'Content-Type': 'application/json', 'X-Request-ID': '13579' } content = json.dumps({ "message": "This is an insecure HTTP response", "timestamp": time.time() }) return APIResponse( status_code=200, headers=headers, content=content, elapsed_time=0.08 ) def _mock_default_api_response(self, request: APIRequest) -> APIResponse: """模拟默认API响应""" headers = { 'Content-Type': 'application/json', 'X-Request-ID': '24680' } content = json.dumps({ "message": "Default response", "method": request.method, "path": urlparse(request.url).path, "timestamp": time.time() }) return APIResponse( status_code=200, headers=headers, content=content, elapsed_time=0.03 ) # ------------------ 规则模型类 ------------------ @dataclass class RuleResult: """规则执行结果类""" rule_id: str rule_name: str is_valid: bool message: str details: Optional[Dict[str, Any]] = None category: Optional[str] = None severity: Optional[str] = None class RuleCategory: """规则类别枚举""" PERFORMANCE = "Performance" SECURITY = "Security" API_DESIGN = "APIDesign" ERROR_HANDLING = "ErrorHandling" GENERAL = "General" class RuleLifecycle: """规则生命周期枚举""" REQUEST_PREPARATION = "RequestPreparation" REQUEST_EXECUTION = "RequestExecution" RESPONSE_VALIDATION = "ResponseValidation" POST_VALIDATION = "PostValidation" ANY_STAGE = "AnyStage" class RuleScope: """规则作用域枚举""" REQUEST_URL = "RequestURL" REQUEST_HEADERS = "RequestHeaders" REQUEST_PARAMS = "RequestParams" REQUEST_BODY = "RequestBody" RESPONSE_STATUS = "ResponseStatus" RESPONSE_HEADERS = "ResponseHeaders" RESPONSE_BODY = "ResponseBody" RESPONSE_TIME = "ResponseTime" SECURITY = "Security" PERFORMANCE = "Performance" ANY_SCOPE = "AnyScope" class TargetType: """规则目标类型枚举""" API_REQUEST = "APIRequest" API_RESPONSE = "APIResponse" ANY = "Any" @dataclass class BaseRule: """基础规则类""" id: str name: str description: str category: str = RuleCategory.GENERAL version: str = "1.0.0" severity: str = "warning" is_enabled: bool = True tags: List[str] = None target_type: str = TargetType.ANY lifecycle: str = RuleLifecycle.ANY_STAGE scope: str = RuleScope.ANY_SCOPE def __post_init__(self): if self.tags is None: self.tags = [] def to_dict(self) -> Dict[str, Any]: """将规则转换为字典""" return asdict(self) def validate(self, context: Dict[str, Any]) -> Dict[str, Any]: """验证逻辑,由子类实现""" raise NotImplementedError("子类必须实现validate方法") @dataclass class PerformanceRule(BaseRule): """性能规则类""" category: str = RuleCategory.PERFORMANCE target_type: str = TargetType.API_RESPONSE lifecycle: str = RuleLifecycle.RESPONSE_VALIDATION scope: str = RuleScope.RESPONSE_TIME threshold: float = 500.0 # 默认阈值500毫秒 metric: str = "response_time" unit: str = "ms" def validate(self, context: Dict[str, Any]) -> Dict[str, Any]: """验证API响应时间是否超过阈值""" response = context.get('api_response') if not response: return { 'is_valid': False, 'message': '缺少API响应对象', 'details': {} } response_time = response.elapsed_time * 1000 # 转换为毫秒 if response_time > self.threshold: return { 'is_valid': False, 'message': f'响应时间 {response_time:.2f}ms 超过阈值 {self.threshold}ms', 'details': { 'actual_time': response_time, 'threshold': self.threshold, 'unit': self.unit } } return { 'is_valid': True, 'message': f'响应时间 {response_time:.2f}ms 在阈值 {self.threshold}ms 内', 'details': { 'actual_time': response_time, 'threshold': self.threshold, 'unit': self.unit } } @dataclass class SecurityRule(BaseRule): """安全规则类""" category: str = RuleCategory.SECURITY target_type: str = TargetType.API_REQUEST lifecycle: str = RuleLifecycle.REQUEST_PREPARATION scope: str = RuleScope.SECURITY check_type: str = "transport_security" # 传输安全检查 expected_value: str = "https" # 期望值为https def validate(self, context: Dict[str, Any]) -> Dict[str, Any]: """验证API请求是否使用了HTTPS协议""" request = context.get('api_request') if not request: return { 'is_valid': False, 'message': '缺少API请求对象', 'details': {} } url = str(request.url) if not url.startswith('https://'): return { 'is_valid': False, 'message': 'API请求必须使用HTTPS协议', 'details': { 'current_url': url, 'expected_protocol': 'https' } } return { 'is_valid': True, 'message': 'API请求使用了HTTPS协议', 'details': { 'url': url } } @dataclass class RESTfulDesignRule(BaseRule): """RESTful设计规则类""" category: str = RuleCategory.API_DESIGN target_type: str = TargetType.API_REQUEST lifecycle: str = RuleLifecycle.REQUEST_PREPARATION scope: str = RuleScope.REQUEST_URL design_aspect: str = "URL设计" pattern: str = r"^/api/v\d+/[a-z0-9-]+(/[a-z0-9-]+)*$" def validate(self, context: Dict[str, Any]) -> Dict[str, Any]: """验证API URL是否符合RESTful设计规范""" import re request = context.get('api_request') if not request: return { 'is_valid': False, 'message': '缺少API请求对象', 'details': {} } url = str(request.url) # 解析URL,获取路径部分 parsed_url = urlparse(url) path = parsed_url.path # 使用正则表达式验证路径 if not re.match(self.pattern, path): return { 'is_valid': False, 'message': 'API URL不符合RESTful设计规范', 'details': { 'current_path': path, 'expected_pattern': self.pattern, 'suggestion': '路径应该遵循 /api/v{version}/{资源}[/{id}] 格式' } } return { 'is_valid': True, 'message': 'API URL符合RESTful设计规范', 'details': { 'path': path } } @dataclass class ErrorHandlingRule(BaseRule): """错误处理规则类""" category: str = RuleCategory.ERROR_HANDLING target_type: str = TargetType.API_RESPONSE lifecycle: str = RuleLifecycle.RESPONSE_VALIDATION scope: str = RuleScope.RESPONSE_BODY error_code: str = "*" # 匹配所有错误码 expected_status: int = -1 # 不验证状态码 def validate(self, context: Dict[str, Any]) -> Dict[str, Any]: """验证API错误响应是否符合标准格式""" response = context.get('api_response') if not response: return { 'is_valid': False, 'message': '缺少API响应对象', 'details': {} } # 只检查4xx和5xx状态码的响应 if response.status_code < 400: return { 'is_valid': True, 'message': '非错误响应,跳过验证', 'details': { 'status_code': response.status_code } } # 确保响应包含JSON内容 if not response.json_content: return { 'is_valid': False, 'message': '错误响应不是有效的JSON格式', 'details': { 'status_code': response.status_code, 'content_type': response.headers.get('Content-Type', '未知') } } # 检查错误响应的必要字段 required_fields = ['code', 'message'] missing_fields = [field for field in required_fields if field not in response.json_content] if missing_fields: return { 'is_valid': False, 'message': '错误响应缺少必要字段', 'details': { 'missing_fields': missing_fields, 'required_fields': required_fields, 'response': response.json_content } } return { 'is_valid': True, 'message': '错误响应符合标准格式', 'details': { 'status_code': response.status_code, 'error_code': response.json_content.get('code'), 'error_message': response.json_content.get('message') } } # ------------------ 规则执行引擎 ------------------ class RuleExecutor: """规则执行引擎""" def __init__(self, rules: List[BaseRule] = None): self.rules = rules or [] def add_rule(self, rule: BaseRule): """添加规则""" self.rules.append(rule) def execute_rule(self, rule: BaseRule, context: Dict[str, Any]) -> RuleResult: """执行单个规则""" logger.info(f"执行规则 '{rule.name}' (ID: {rule.id})") try: result = rule.validate(context) # 构建规则执行结果 rule_result = RuleResult( rule_id=rule.id, rule_name=rule.name, is_valid=result.get('is_valid', False), message=result.get('message', ''), details=result.get('details', {}), category=rule.category, severity=rule.severity ) # 记录执行结果 log_level = logging.INFO if rule_result.is_valid else logging.WARNING logger.log(log_level, f"规则 '{rule.name}' 结果: {'通过' if rule_result.is_valid else '失败'} - {rule_result.message}") return rule_result except Exception as e: # 记录执行异常 logger.error(f"执行规则 '{rule.name}' 时发生异常: {str(e)}", exc_info=True) # 返回异常结果 return RuleResult( rule_id=rule.id, rule_name=rule.name, is_valid=False, message=f"规则执行异常: {str(e)}", details={'exception': str(e)}, category=rule.category, severity=rule.severity ) def execute_rules_for_lifecycle(self, lifecycle: str, context: Dict[str, Any]) -> List[RuleResult]: """执行特定生命周期阶段的所有规则""" logger.info(f"执行 {lifecycle} 阶段的规则") # 筛选符合生命周期的规则 lifecycle_rules = [rule for rule in self.rules if rule.lifecycle == lifecycle or rule.lifecycle == RuleLifecycle.ANY_STAGE] # 执行筛选出的规则 results = [] for rule in lifecycle_rules: result = self.execute_rule(rule, context) results.append(result) # 统计执行结果 passed = sum(1 for result in results if result.is_valid) failed = len(results) - passed logger.info(f"{lifecycle} 阶段规则执行完成: {passed} 条通过, {failed} 条失败") return results def execute_all_rules(self, context: Dict[str, Any]) -> List[RuleResult]: """执行所有规则""" logger.info(f"执行所有规则") results = [] for rule in self.rules: result = self.execute_rule(rule, context) results.append(result) # 统计执行结果 passed = sum(1 for result in results if result.is_valid) failed = len(results) - passed logger.info(f"所有规则执行完成: {passed} 条通过, {failed} 条失败") return results # ------------------ 演示函数 ------------------ def create_demo_rules() -> List[BaseRule]: """创建演示用的规则""" rules = [] # 添加性能规则 performance_rule = PerformanceRule( id="response-time-max-500ms", name="响应时间不超过500毫秒", description="验证API响应时间不超过500毫秒", threshold=500, metric="response_time", unit="ms" ) rules.append(performance_rule) # 添加安全规则 security_rule = SecurityRule( id="https-only-rule", name="HTTPS强制使用规则", description="验证API是否只使用HTTPS协议", severity="error", check_type="transport_security", expected_value="https" ) rules.append(security_rule) # 添加RESTful设计规则 restful_rule = RESTfulDesignRule( id="restful-url-pattern", name="RESTful URL设计规则", description="验证API URL是否符合RESTful设计规范", pattern=r"^/api/v\d+/[a-z0-9-]+(/[a-z0-9-]+)*$" ) rules.append(restful_rule) # 添加错误处理规则 error_rule = ErrorHandlingRule( id="standard-error-response", name="标准错误响应格式规则", description="验证API错误响应是否符合标准格式", error_code="*" ) rules.append(error_rule) return rules def demo_rule_execution(): """演示规则执行过程""" logger.info("开始规则执行演示") # 创建规则 rules = create_demo_rules() # 创建规则执行引擎 executor = RuleExecutor(rules) # 创建API调用器 api_caller = APICaller() # === 场景1: 正常响应场景 === logger.info("\n=== 场景1: 正常响应场景 ===") # 创建API请求 request1 = APIRequest( method="GET", url="https://api.example.com/api/v1/users/123", headers={"Content-Type": "application/json"} ) # 执行请求准备阶段的规则 context1 = {"api_request": request1} prep_results1 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context1) # 发送API请求 response1 = api_caller.call_api(request1) # 执行响应验证阶段的规则 context1["api_response"] = response1 resp_results1 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context1) # === 场景2: 慢响应场景 === logger.info("\n=== 场景2: 慢响应场景 ===") # 创建API请求 request2 = APIRequest( method="GET", url="https://api.example.com/api/v1/users/slow", headers={"Content-Type": "application/json"} ) # 执行请求准备阶段的规则 context2 = {"api_request": request2} prep_results2 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context2) # 发送API请求 response2 = api_caller.call_api(request2) # 执行响应验证阶段的规则 context2["api_response"] = response2 resp_results2 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context2) # === 场景3: 错误响应场景 === logger.info("\n=== 场景3: 错误响应场景 ===") # 创建API请求 request3 = APIRequest( method="GET", url="https://api.example.com/api/v1/users/error", headers={"Content-Type": "application/json"}, params={"id": "999"} ) # 执行请求准备阶段的规则 context3 = {"api_request": request3} prep_results3 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context3) # 发送API请求 response3 = api_caller.call_api(request3) # 执行响应验证阶段的规则 context3["api_response"] = response3 resp_results3 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context3) # === 场景4: 非HTTPS场景 === logger.info("\n=== 场景4: 非HTTPS场景 ===") # 创建API请求 request4 = APIRequest( method="GET", url="http://api.example.com/api/v1/users/123", headers={"Content-Type": "application/json"} ) # 执行请求准备阶段的规则 context4 = {"api_request": request4} prep_results4 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context4) # 发送API请求 response4 = api_caller.call_api(request4) # 执行响应验证阶段的规则 context4["api_response"] = response4 resp_results4 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context4) # === 场景5: 不符合RESTful设计的URL === logger.info("\n=== 场景5: 不符合RESTful设计的URL ===") # 创建API请求 request5 = APIRequest( method="GET", url="https://api.example.com/getUserInfo?id=123", headers={"Content-Type": "application/json"} ) # 执行请求准备阶段的规则 context5 = {"api_request": request5} prep_results5 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context5) # 发送API请求 response5 = api_caller.call_api(request5) # 执行响应验证阶段的规则 context5["api_response"] = response5 resp_results5 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context5) logger.info("\n演示完成") def main(): """主函数""" parser = argparse.ArgumentParser(description='规则执行演示脚本') parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'], default='INFO', help='日志级别') args = parser.parse_args() # 设置日志级别 logging.getLogger().setLevel(getattr(logging, args.log_level)) # 执行演示 demo_rule_execution() if __name__ == "__main__": main()