From 11c5e1b74e9a72a718d3dfbb7d033d2bf1b03395 Mon Sep 17 00:00:00 2001 From: han0 Date: Thu, 19 Nov 2020 08:41:57 +0800 Subject: [PATCH] feat: validate --- nc_http/tools/validate.py | 357 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 nc_http/tools/validate.py diff --git a/nc_http/tools/validate.py b/nc_http/tools/validate.py new file mode 100644 index 0000000..a206476 --- /dev/null +++ b/nc_http/tools/validate.py @@ -0,0 +1,357 @@ +import re +import time + + +class DataFormatValidator: + """ + 参数验证类 + + 示例: + request_data = {'name': '51zan', 'type': 3,'desc':''} + validate_rule = { + 'name': 'required|str|max:20', # 必须、字符串类型、最大20个字符 + 'type': 'required|choices:0,1,2' # 必须、 取值范围【0,1,2】 + 'desc': 'str' # 非必须,字符串 + } + + """ + + def __init__(self, rules=None): + """ + :param rules: 验证规则 + """ + rules = rules or [] + self.rules = rules + self.error = '' + self.valid_data = {} + + def check(self, data): + """ + 验证数据格式是否正确 + :param data: dict + :return: boolean + """ + is_valid = True + valid_data = {} + for key, rule_str in self.rules.items(): + rules = rule_str.split("|") + field_value = data.get(key) + + # 如果字段不是必需的,没这个参数就不验证其它规则 + if 'required' not in rules and key not in data: + continue + + item_valid = True + for rule in rules: + if not rule: + continue + + combination_rule = rule.split(":", 1) + if len(combination_rule) == 2: + func = getattr(self, combination_rule[0].strip()) + args = combination_rule[1].split(',') + if not func(field_value, *args): + self.set_error(key, rule) + item_valid = is_valid = False + else: + func = getattr(self, rule.strip()) + if not func(field_value): + self.set_error(key, rule) + item_valid = is_valid = False + + # 如果当前项所有规则都验证通过,加入有效数据 + if item_valid: + valid_data[key] = field_value + + self.valid_data = valid_data + return is_valid + + def get_valid_data(self): + return self.valid_data + + def get_error(self): + return self.error + + def set_error(self, key, rule): + self.error = self.error or "parameter {} is invalid, verified failed by method [{}]".format(key, rule) + + @staticmethod + def required(value): + """ + 必需,不能为空 + :param value: + :return: + """ + if value in (None, ''): + return False + return True + + @staticmethod + def str(value): + """ + 字符串 + :param value: + :return: + """ + if type(value) in (str,): + return True + return False + + @staticmethod + def int(value): + if isinstance(value, int): + return True + return False + + @staticmethod + def number(value): + """ + 数字 + :param value: + :return: + """ + try: + int(value) + except ValueError: + try: + float(value) + except ValueError: + return False + return True + return True + + @staticmethod + def phrase(value): + """ + 英文单词,短语 + :param value: + :return: + """ + if re.match(r'[a-zA-Z \']+$', value): + return True + return False + + @staticmethod + def float(value): + """ + 浮点型 + :param value: + :return: + """ + if isinstance(value, float): + return True + return False + + @staticmethod + def bool(value): + """ + 布尔型 + :param value: + :return: + """ + if isinstance(value, bool): + return True + return False + + @staticmethod + def dict(value): + """ + 字典 + :param value: + :return: + """ + if type(value) == dict: + return True + return False + + @staticmethod + def ip(value): + """ + IP v4地址 + :param value: + :return: + """ + if re.match(r'^[\d]{1,3}(\.[\d]{1,3}){3}$', value): + return True + return False + + def list(self, value, item_type=None): + """ + 列表 + 规则 list|list:str|list:int|list:ip|list:url + :param value: + :param item_type: + :return: + """ + if type(value) not in (list, tuple): + return False + + if item_type: + for item in value: + func = getattr(self, item_type) + if not func(item): + return False + return True + + @staticmethod + def max(value, max_value): + """ + 验证字符串最大长度 + 规则 max:2|max:10086 + :param value: + :param max_value: + :return: + """ + if len(value) > int(max_value): + return False + return True + + @staticmethod + def min(value, min_length): + """ + 验证字符串最小长度 + 规则 min:1|min:1024 + :param value: + :param min_length: + :return: + """ + if len(value) < int(min_length): + return False + return True + + @staticmethod + def length(value, valid_length): + """ + 验证字符串长度 + 规则 length:100 + :param value: + :param valid_length: + :return: + """ + if len(value) == int(valid_length): + return True + return False + + @staticmethod + def choices(value, *args): + """ + 选项 + 规则 choice:1,2,3|choice:a,b,c + :param value: + :param args: + :return: + """ + if str(value) in args: + return True + return False + + @staticmethod + def excepts(value, *args): + """ + 排除选项 + 规则 choice:1,2,3|choice:a,b,c + :param value: + :param args: + :return: + """ + if str(value) not in args: + return True + return False + + @staticmethod + def range(value, min_length, max_length): + """ + 数值区间 + 规则 range:1,9|range:1,|range:,9 + :param value: + :param min_length: + :param max_length: + :return: + """ + if min_length and value < int(min_length): + return False + if max_length and value > int(max_length): + return False + return True + + @staticmethod + def url(value): + """ + URL + :param value: + :return: + """ + if re.match(r'^https?:/{2}[\w\-_]+(\.[\w\-_]+)+.*$', value): + return True + return False + + @staticmethod + def email(value): + """ + 邮箱 + :param value: + :return: + """ + if re.match(r'^[\w\-_]+@[\w\-_]+(\.[\w\-_]+)+$', value): + return True + return False + + @staticmethod + def mobile(value): + """ + 手机号 + :param value: + :return: + """ + if re.match(r'^1\d{10}$', value): + return True + return False + + @staticmethod + def datetime(value, datetime_format): + """ + 验证日期或时间格式 + 规则 datetime:%Y-%m-%d %H:%M:%S | datetime:%Y-%m-%d + :param value: + :param datetime_format: + :return: + """ + try: + time.strptime(value, datetime_format) + except ValueError: + return False + return True + + +if __name__ == '__main__': + test_rules = { + "id": "int|choices:2,3,4,5", + "name": "required", + "create_time": "int|range:,1000000", + "remark": "required|str|max:5|min:1", + "tags": "list:url", + "avatar": "url", + "email": "email", + "mobile": "mobile", + "birthday": "datetime:%Y-%m-%d %H:%M:%S", + "ip": "ip", + "department": "dict", + "code": "number" + } + + test_data = { + "id": 4, + "name": "aaa", + "create_time": 111111, + "remark": "中国123", + "tags": (u'http://baidu.com', 'http://baidu.com'), + "avatar": "https://aaa111.com/a.php?a=b", + "email": "ruyi111@sina.com", + "mobile": "15880445400", + "birthday": "2018-10-12 12:00:00", + "ip": "111.1.1.1", + "department": {"a": "b"}, + "code": '11.111' + } + + print(DataFormatValidator(test_rules).check(test_data))