看用Tornado如何自定义实现表单验证

  我们知道,平时在登陆某个网站或软件时,网站对于你输入的内容是有要求的,并且会对你输入的错误内容有提示,对于Django这种大而全的web框架,是提供了form表单验证功能,但是对于Tornado而言,就没有这功能,所以就需要我们来自己自定义form表单验证,而且这种方法正是Django里的form表单验证的实质内容,也帮我们在后面学习Django理解相关的源码。

  写之前,我们必须知道form表单验证的实质是什么?

  实质就是正则匹配

  我们知道用户提交数据是通过post方式提交,所以我们重写post方法,并在post方法进行业务逻辑处理

  • 获取用户提交的数据
  • 将用户提交的数据和正则表达式匹配

第一阶段

  • 场景:我们知道后台是一个url对应一个类来处理客户请求的
  • 问题:那么在不同页面里会相同需求,比如登陆时用邮箱登陆,修改密码时又要用邮箱,那对于这样同样验证需求,在我们的后台是不是代码就重复了呢?
  • 解决:独立一个模块专门用于用户信息验证

我们先看一下下面这段代码

class MainForm(object):
    def __init__(self):
        # 各种信息正则匹配规则
        # 并且要求这里字段名和前端传来的name一致
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
        self.port = ‘(\d+)‘
        self.phone = ‘^1[3|4|5|8][0-9]\d{8}$‘

    def check_valid(self, request):
        flag = True
        form_dict = self.__dict__  #获取类的普通字段和值
        for key, regular in form_dict.items():
            post_value = request.get_argument(key)  #获取用户输入的值
            # 让提交的数据 和 定义的正则表达式进行匹配
            ret = re.match(regular, post_value)
            print key,ret,post_value
            if not ret:
                flag = False  #一旦有匹配不成功的,设置为False
        return flag  #post方法里根据这个返回值来决定给客户返回什么内容

从上面我们可以知道,这个模块只是简简单单的给了true  or false返回值的问题,我们还希望返回客户输入的值,看下怎么优化吧

  • 在check_vaild方法里定义一个局部变量-字典,然后接收客户的信息,并把这个字典一并返回到post方法里

第二阶段

  在上面,我们已经独立了一个模块来验证信息,但问题又来了,上面这个模块我定义的时候以index页面定制的,不同的页面处理的需要是不一样的,那如果解决这个问题,好像可以每个页面都独立一个模块,但这样,代码未免重复的太多了。

  仔细的人会发现,其实就是init里需要匹配的内容不一样,下面的check_vaild方法都是一样的

  • 类的继承,写一个BaseForm父类,让其他验证类继承

class BaseForm:
    def check_valid(self, handle):
        flag = True
        value_dict = {}
        for key, regular in self.__dict__.items():
            # host,ip port phone
            input_value = handle.get_argument(key)
            val = re.match(regular, input_value)
            print(key, input_value, val, regular)
            if not val:
                flag = False
            value_dict[key] = input_value
        return flag, value_dict

class IndexForm(BaseForm):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
        self.port = ‘(\d+)‘
        self.phone = ‘^1[3|4|5|8][0-9]\d{8}$‘

class HomeForm(BaseForm):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"

  在实际场景中,客户填写的信息有些是必填,有些是可不填,这样又增加了匹配的复杂度,我们可不可以把每种匹配规则独立成一个类,并达到后台传入True时,不为空,传入了False时可为空呢??并且我们让这个类处理时返回错误提示信息??

class IPFiled:
    REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"

    def __init__(self, error_dict=None, required=True):
        self.error_dict = {}  #用于自定制错误提醒信息
        if error_dict:
            self.error_dict.update(error_dict)
        self.required = required  #可空否
        self.error = None      #记录错误提醒信息
        self.is_valid = False  #匹配成功与否
        self.value = None       #用户发来进行匹配的值

    def validate(self, name, input_value):
        ‘‘‘
        :param name: 字段名:IP   方便在错误信息里提示
        :param input_value:用户输入的值
        :return:
        ‘‘‘
        if not self.required:
            #可以为空--》通过
            self.is_valid = True
            self.value = input_value
        else:
            if not input_value.strip():
                #输入为空
                if self.error_dict.get(‘required‘,None):
                    #使用自定义错误信息
                    self.error = self.error_dict[‘required‘]
                else:
                    #使用默认
                    self.error = "%s is required" % name
            else:
                ret = re.match(IPFiled.REGULAR, input_value)
                if ret:
                    #匹配成功--》通过
                    self.id_valid = True
                    self.value = ret.group()
                else:
                    if self.error_dict.get(‘valid‘, None):
                        #获取自定义错误信息,并使用
                        self.error = self.error_dict[‘valid‘]
                    else:
                        #使用默认
                        self.error = "%s is invalid" % name

class BaseForm:
    def check_valid(self, handle):
        flag = True
        success_value_dict = {}
        error_message_dict = {}
        for key, regular in self.__dict__.items():
            #key-->ip  handle-->homeindex对象  regular--》IPFiled对象
            input_value = handle.get_argument(key)  #用户输入的值
            #将验证放在了IPFiled对象里
            regular.validate(key,input_value)
            if regular.is_valid:
                #匹配成功就添加用户输入值
                success_value_dict[key] = regular.value
            else:
                #添加错误提示信息
                error_message_dict[key] = regular.error
                flag = False
        return flag, error_message_dict,success_value_dict

class HomeForm(BaseForm):
    def __init__(self):
        self.ip = IPFiled(required=True,error_dict={"required":"别闹","valid":"妹子,格式错了"})

class HomeHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(‘home.html‘)

    def post(self, *args, **kwargs):
        obj = HomeForm()
        # 获取用户输入的内容
        # 和正则表达式匹配
        is_valid,error_dict,success_dict = obj.check_valid(self)
        print(is_valid)
        if is_valid:
            print(success_dict)
        else:
            print(error_dict)

第三阶段

  在input类型有个叫checkbox的,在后台获取值不再是get_argument,而是get_arguments,不同于其他的input类型,所以在BaseForm的check_valid方法里获取值的方式就要改了,利用type进行判断

class BaseForm:
    def check_valid(self, handle):
        flag = True
        error_message_dict = {}
        success_value_dict = {}
        for key, regular in self.__dict__.items():
            # key: ip .....
            # handle: HomeIndex对象,self.get_... self.
            # regular: IPFiled(required=True)

            if type(regular) == ChechBoxFiled:
                input_value = handle.get_arguments(key)  #get_arguments是没有默认参数的
            else:
                input_value = handle.get_argument(key)
            # input_value = 用户输入的值
            # 将具体的验证,放在IPFiled对象中
            regular.validate(key, input_value)

            if regular.is_valid:
                success_value_dict[key] = regular.value
            else:
                error_message_dict[key] = regular.error
                flag = False

  另外对checkbox返回结果处理方式也稍微有些区别,不用对其合法性检测,只要判断是否为空即可,所以在CheckBoxFiled类的validate方法也要改

class CheckBoxFiled:

    def __init__(self, error_dict=None, required=True):
        # 封装了错误信息
        self.error_dict = {}
        if error_dict:
            self.error_dict.update(error_dict)

        self.required = required

        self.error = None # 错误信息
        self.value = None
        self.is_valid = False

    def validate(self, name, input_value):
        """
        :param name: 字段名 favor
        :param input_value: 用户表单中输入的内容,列表None or [1,2]
        :return:
        """

        if not self.required:
            # 用户输入可以为空
            self.is_valid = True
            self.value = input_value
        else:
            if not input_value:
                #不应为空,而空了,提示错误信息
                if self.error_dict.get(‘required‘,None):
                    #获取自定制信息
                    self.error = self.error_dict[‘required‘]
                else:
                    #取默认的
                    self.error = "%s is required" % name
            else:
                self.is_valid = True
                self.value = input_value

第四阶段

  除了checkbox这个特殊外,还有file----文件上传,在后台获取方式也不一样,不是self.get_argument,而是self.request.files.get(),得到的内容是一个列表,格式如 [{‘body‘:‘xx‘,‘filename‘:‘xx‘},{‘body‘:‘xx‘,‘filename‘:‘xx‘}],想要获得文件名,还要循环这个列表,通过filename的key取到,并且我们如果要把文件上传到服务端,就在FileFiled写入save方法,“body”里面就是文件内容

import tornado.ioloop
import tornado.web
import re
import os

class IPFiled:
    REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"

    def __init__(self, error_dict=None, required=True):
        # 封装了错误信息
        self.error_dict = {}
        if error_dict:
            self.error_dict.update(error_dict)

        self.required = required

        self.error = None # 错误信息
        self.value = None
        self.is_valid = False

    def validate(self, name, input_value):
        """
        :param name: 字段名
        :param input_value: 用户表单中输入的内容
        :return:
        """
        if not self.required:
            # 用户输入可以为空
            self.is_valid = True
            self.value = input_value
        else:
            if not input_value.strip():
                if self.error_dict.get(‘required‘,None):
                    self.error = self.error_dict[‘required‘]
                else:
                    self.error = "%s is required" % name
            else:
                ret = re.match(IPFiled.REGULAR, input_value)
                if ret:
                    self.is_valid = True
                    self.value = input_value
                else:
                    if self.error_dict.get(‘valid‘, None):
                        self.error = self.error_dict[‘valid‘]
                    else:
                        self.error = "%s is invalid" % name

class StringFiled:
    REGULAR = "^(.*)$"

    def __init__(self, error_dict=None, required=True):
        # 封装了错误信息
        self.error_dict = {}
        if error_dict:
            self.error_dict.update(error_dict)

        self.required = required

        self.error = None # 错误信息
        self.value = None
        self.is_valid = False

    def validate(self, name, input_value):
        """
        :param name: 字段名
        :param input_value: 用户表单中输入的内容
        :return:
        """
        if not self.required:
            # 用户输入可以为空
            self.is_valid = True
            self.value = input_value
        else:
            if not input_value.strip():
                if self.error_dict.get(‘required‘,None):
                    self.error = self.error_dict[‘required‘]
                else:
                    self.error = "%s is required" % name
            else:
                ret = re.match(IPFiled.REGULAR, input_value)
                if ret:
                    self.is_valid = True
                    self.value = input_value
                else:
                    if self.error_dict.get(‘valid‘, None):
                        self.error = self.error_dict[‘valid‘]
                    else:
                        self.error = "%s is invalid" % name

class ChechBoxFiled:

    def __init__(self, error_dict=None, required=True):
        # 封装了错误信息
        self.error_dict = {}
        if error_dict:
            self.error_dict.update(error_dict)

        self.required = required

        self.error = None # 错误信息
        self.value = None
        self.is_valid = False

    def validate(self, name, input_value):
        """
        :param name: 字段名 favor
        :param input_value: 用户表单中输入的内容,列表None or [1,2]
        :return:
        """

        if not self.required:
            # 用户输入可以为空
            self.is_valid = True
            self.value = input_value
        else:
            if not input_value:
                if self.error_dict.get(‘required‘,None):
                    self.error = self.error_dict[‘required‘]
                else:
                    self.error = "%s is required" % name
            else:
                self.is_valid = True
                self.value = input_value

class FileFiled:

    REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$"

    def __init__(self, error_dict=None, required=True):
        # 封装了错误信息
        self.error_dict = {}
        if error_dict:
            self.error_dict.update(error_dict)

        self.required = required

        self.error = None  # 错误信息
        self.value = []
        self.is_valid = True
        self.name = None
        self.success_file_name_list = []

    def validate(self, name, all_file_name_list):
        """
        :param name: 字段名
        :param all_file_name_list: 所有文件文件名
        :return:
        """
        self.name = name
        if not self.required:
            # 用户输入可以为空
            self.is_valid = True
            self.value = all_file_name_list
        else:
            #用户输入不能为空
            if not all_file_name_list:
                #输入为空
                self.is_valid = False
                if self.error_dict.get(‘required‘,None):
                    self.error = self.error_dict[‘required‘]
                else:
                    self.error = "%s is required" % name
            else:
                # 循环所有的文件名
                for file_name in all_file_name_list:
                    ret = re.match(FileFiled.REGULAR, file_name)
                    if not ret:
                        #有文件名匹配不成功
                        self.is_valid = False
                        if self.error_dict.get(‘valid‘, None):
                            self.error = self.error_dict[‘valid‘]
                        else:
                            self.error = "%s is invalid" % name
                        break
                    else:
                        self.value.append(file_name)  #都匹配成功的文件名列表

    def save(self, request, path=‘statics‘):
        # 所有文件列表
        # request = HomeHandler.request  self.name = fafafa(前端名字)
        file_metas = request.files.get(self.name)
        # 循环文件列表
        temp_list = []
        for meta in file_metas:
            # 每一个文件的文件名
            file_name = meta[‘filename‘]
            # self.value:[1.py, 2.py]  【statics/1.py  statics/2.py】
            new_file_name = os.path.join(path, file_name)
            #self.value--->success_file_name_list
            if file_name and file_name in self.value:
                temp_list.append(new_file_name)
                with open(new_file_name, ‘wb‘) as up:
                    up.write(meta[‘body‘])
        self.value = temp_list

class BaseForm:
    def check_valid(self, handle):
        flag = True
        error_message_dict = {}
        success_value_dict = {}
        for key, regular in self.__dict__.items():
            # key: ip .....
            # handle: HomeIndex对象,self.get_... self.
            # regular: IPFiled(required=True)

            if type(regular) == ChechBoxFiled:
                input_value = handle.get_arguments(key)
            elif type(regular) == FileFiled:
                # 获取文件名
                file_list = handle.request.files.get(key)
                # [{‘body‘:‘xx‘,‘filename‘:‘xx‘},{‘body‘:‘xx‘,‘filename‘:‘xx‘}]
                input_value = []  #file_name_list
                for item in file_list:
                    input_value.append(item[‘filename‘])
                # 所有文件名进行验证
            else:
                input_value = handle.get_argument(key)
            # input_value = 用户输入的值
            # 将具体的验证,放在IPFiled对象中
            regular.validate(key, input_value)

            if regular.is_valid:
                success_value_dict[key] = regular.value
            else:
                error_message_dict[key] = regular.error
                flag = False

        return flag, success_value_dict, error_message_dict

class HomeForm(BaseForm):
    def __init__(self):
        self.ip = IPFiled(required=True, error_dict={‘required‘: "别闹,别整空的..", "valid": "骚年,格式错误了"})
        self.host = StringFiled(required=False)
        self.favor = ChechBoxFiled(required=True)
        self.fafafa = FileFiled(required=True)  #前端name同名fafafa

class HomeHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(‘home.html‘, error_dict=None)

    def post(self, *args, **kwargs):

        # self.get_argument()
        # self.get_arguments()
        # files = self.request.files.get(‘fafafa‘,[])
        # # files = [ 文件一、文件二]
        # print(type(files),files)

        obj = HomeForm()
        is_valid, success_dict, error_dict = obj.check_valid(self)
        if is_valid:
            print(‘success‘,success_dict)
            #只有全部通过,就执行上传方法
            obj.fafafa.save(self.request)  #HomeForm().fafafa=FileFiled()
        else:
            print(‘error‘, error_dict)
            self.render(‘home.html‘, error_dict=error_dict)

settings = {
    ‘template_path‘: ‘views‘,
    ‘static_path‘: ‘statics‘,
    ‘static_url_prefix‘: ‘/statics/‘,
}

application = tornado.web.Application([
    (r"/home", HomeHandler),
], **settings)

if __name__ == "__main__":
    application.listen(8001)
    tornado.ioloop.IOLoop.instance().start()

  是不是有点乱,这么多类,好!这里画个小图,助于大家理解.....

  是不是还是不好理解,好把,我画图有限...

时间: 2024-10-25 19:37:59

看用Tornado如何自定义实现表单验证的相关文章

web前端框架之自定义form表单验证

h3 { background-color: palegreen } 自定义form验证初试 1 1.在后端创建一个类MainForm,并且在类中自定义host ip port phone等,然后写入方法,在post方法中创建MainForm对象,并且把post方法中的self(这里的self指代 的前端的ip,端口等)传入到这个MainForm方法中,在check_valid方法中定义一个参数handler代指传入的self,然后遍历MainForm对象中的属性,然后用handler.get_

自定义form表单验证

方式一:RegexValidator对象 from django.core.validators import RegexValidator phone = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')]) # 自定义验证规则进行验证 方式二:函数 from django.core.exceptions import

【jQuery基础学习】06 jQuery表单验证插件-Validation

jQuery的基础部分前面都讲完了,那么就看插件了. 关于jQuery表单验证插件-Validation validation特点: 内置验证规则:拥有必填.数字.E-Mail.URL和信用卡号码等19类内置验证规则 自定义验证规则:可以很方便地自定义验证规则 简单强大的验证信息提示:默认了验证信息提示,并提供了自定义覆盖默认提示信息的功能 实时验证:可以通过keyup或者blur事件触发验证,而不仅仅在表单提交的时候验证 使用方法: 引用jQuery库和Validation插件 <script

ThinkPhp框架:父类及表单验证

这个知识点,就可以通过"登录"和"注册"的页面来学习这个知识点了首先先做一个"登录"功能一.登录功能(父类)(1)登录的控制器在我的控制器文件夹中新建一个登陆控制器我的路径是这样的: 然后里面要书写正确才可以,其中要有命名空间,还有继承父类的控制器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php namespace Home\Controller; use Think\Controlle

jQuery基础(常用插件 表单验证,图片放大镜,自定义对象级,jQuery UI,面板折叠)

1.表单验证插件--validate   该插件自带包含必填.数字.URL在内容的验证规则,即时显示异常信息,此外,还允许自定义验证规则,插件调用方法如下: $(form).validate({options}) 其中form参数表示表单元素名称,options参数表示调用方法时的配置对象,所有的验证规则和异常信息显示的位置都在该对象中进行设置.     2.表单插件--form 通过表单form插件,调用ajaxForm()方法,实现ajax方式向服务器提交表单数据,并通过方法中的option

atittit.表单验证的实现方式以及原理本质以及选型以及自定义兼容easyui dsl规则的表单验证

atittit.表单验证的实现方式以及原理本质以及选型以及自定义兼容easyui dsl规则的表单验证 1. 需求,表单验证需要弹框式,但目前easyui ms绑定死了tooltip式样 1 2. 表单验证表现形式 1 2.1. 弹框 1 2.2. 浮动tooltip,推荐这个 1 3. 表单验证的实现原理 1 3.1. 定义reg 2 3.2. 解释 2 3.3. 调用提示... 2 4. 表单验证框架选型easyui>ligerui 2 4.1. ligerui的表单验证选型... 2 4.

jQuery Validate 表单验证插件----自定义校验结果样式

一.下载依赖包 网盘下载:https://yunpan.cn/cryvgGGAQ3DSW  访问密码 f224 二.引入依赖包 <script src="../../scripts/jquery-1.3.1.js" type="text/javascript"></script> <script src="lib/jquery.validate.js" type="text/javascript"

jQuery Validate 表单验证插件----自定义一个验证方法

一.下载依赖包 网盘下载:https://yunpan.cn/cryvgGGAQ3DSW  访问密码 f224 二.引入依赖包 <script src="../../scripts/jquery-1.3.1.js" type="text/javascript"></script> <script src="lib/jquery.validate.js" type="text/javascript"

表单验证插件,几乎可以自定义任何你想要的效果

上家公司的项目要用到大量的表单验证,且需求变化很大也多,实现ajax查询之类 的,还是以jquery插件的形式介绍吧.这个插件可以读文档自定义设置正则,很方便也很实用,社区活跃.