@app.route('/', methods=['GET', 'POST'])def login(): form = LoginForm() print('form.validate_on_submit() =', form.validate_on_submit()) print('form.email.label =', form.email.label) print('form.email() = ', form.email) print('form.email.errors =', form.email.errors) return render_template('login.html', form=form)app.run(debug=True)在第 1 行,设置以 GET 方法或者 POST 方法访问路径 / 时,使用函数 login() 进行处理;在第 3 行,创建一个实例 form,表示用户登录的表单;在第 8 行,调用 render_template 渲染 login.html。form 对象提供了如下方法和属性:属性说明form.validate_on_submit()表单验证函数,返回值表示验证是否正确form.email()显示 email 字段对应的 HTML 代码form.email.labelemail 字段的 labelform.email.errors验证 email 字段的失败提示信息在程序中打印 form 的属性,当用户提交表单时,在控制台中显示如下信息:form.validate_on_submit() = Falseform.email.label = <label for="email">邮箱</label>form.email() = <input id="email" name="email" required type="text" value="tom">form.email.errors = ['请输入正确的邮箱']当表单验证失败时,form.validate_on_submit() 返回为 False。form.email.errors 是一个列表,记录了所有可能的错误信息。
在 Web 页面中,表单是一种常见的元素,表单包含有多个字段,通常字段的取值需要在一定的范围内,例如:QQ 注册时,名称不可以为空,密码的长度至少是 8 个字符,如下图所示:将表单提交给服务端处理时,服务端需要验证表单中的字段的取值是否符合要求。本节学习 Flask 中提供表单验证的功能,学习如何对表单中的字段的取值进行有效性检查。
本篇主要介绍使用 JavaScript 进行表单验证。表单验证并不是 JavaScript 提供的某种特性,而是结合各种特性达到的一种目的,是需求的产物。所有线上产品的表单几乎都有验证,如注册时要求“用户名 6-16 位”,验证会由 JavaScript 来完成,通常为了安全性和准确性,服务端会再次做一遍验证。
下面列出 ThinkPHP 提供的内置验证规则:验证规则名含义require验证字段为必须number验证字段是否为数字类型integer验证字段是否为整数float验证字段是否为浮点型boolean 或 bool验证字段是否为布尔值email验证字段是否符合邮箱格式array验证字段是否为数组date验证字段是否为有效的日期alpha验证字段是否为纯字母alphaNum验证字段是否为字母+数字混合alphaDash验证字段是否为字母+数字+_+-混合chs验证字段是否为 汉字chsAlpha验证字段是否为 汉字+字母混合chsAlphaNum验证字段是否为 汉字+字母+数字混合chsDash验证字段是否为 汉字+字母+数字+_+-混合cntrl验证字段是否控制字符(换行、缩进、空格)graph验证字段是否为可打印字符(空格除外)print验证字段是否为可打印字符(包括空格)lower验证字段是否为小写字母upper验证字段是否为大写字母space验证字段是否为空白字符(包括缩进,垂直制表符,换行符,回车和换页字符)xdigit验证字段是否为十六进制字符串url验证字段是否为有效的URL地址ip验证字段是否为有效的IP地址,支持验证ipv4和ipv6格式的IP地址dateFormat:format验证字段是否为指定格式的日期,如 dateFormat:y-m-d 表示年月日mobile验证字段是否为有效的手机格式idCard验证字段是否为有效的身份证格式macAddr验证字段是否为有效的MAC地址zip验证字段是否为有效的邮政编码in验证字段是否为是否在某个范围,如 in:1,2,3 表示取值在 1,2,3中notIn验证字段是否为不在某个范围between验证字段是否为在某个区间,如 between:1,10 表示 1-10 之间notBetween验证字段是否为不在某个范围length:num1,num2验证字段是否为长度是否在某个范围,如 length:4,25 长度在 4-25,length:4 表示指定长度,如果验证的数据是数组,则判断数组的长度。如果验证的数据是File对象,则判断文件的大小。max:number验证字段的 最大长度,如 max:10 表示最大长度 10,注意不包含字段不存在的情况,如果验证的数据是数组,则判断数组的长度。如果验证的数据是File对象,则判断文件的大小。min:number验证字段的 最小长度,如min:5 表示最小长度 5,注意不包含字段不存在的情况,如果验证的数据是数组,则判断数组的长度。如果验证的数据是File对象,则判断文件的大小。after:日期验证字段是否为在某个日期之后,如 after:2020-11-18 表示 2020-11-18 之后before:日期验证字段是否为在某个日期之前,如 after:2020-11-18 表示 2020-11-18 之前expire:开始时间,结束时间验证当前操作(注意不是某个值)是否在某个有效日期之内,如expire:2020-1-21,2020-5-1表示 2020-01-21 至 2020-05-01 之间allowIp:allow1,allow2,…验证当前请求的IP是否允许访问,如 'allowIp:114.45.4.55' 表示ip为 114.45.4.55denyIp:allow1,allow2,…验证当前请求的IP是否禁止访问,如 denyIp:114.45.4.55confirm验证字段是否为和另外一个字段值一致,如 confirm:new_value 表示当前判断字段值是否和 new_value 字段值一致eq 或者 = 或者 same验证字段是否为等于某个值,如eq:100 表示等于 100,=:10 表示等于 10,same:20 表示等于 20egt 或者 >=验证字段是否为大于等于某个值,如 egt:30表示大于等于30,>=50 表示大于等于50gt 或者 >验证字段是否为大于某个值,同上elt 或者 <=验证字段是否为小于等于某个值,同上lt 或者 <验证字段是否为小于某个值file验证字段是否为一个上传文件image:width,height,type验证字段是否为一个图像文件,其中 width,height和type都是可选,width和height必须同时定义fileExt:允许的文件后缀验证字段是否为上传文件后缀fileMime:允许的文件类型验证字段是否为上传文件类型fileSize:允许的文件字节大小验证字段是否为上传文件大小token:表单令牌名称表单令牌验证unique:table,field,except,pk验证当前请求的字段值是否为唯一的requireIf:field,value验证某个字段的值等于某个值的时候必须requireWith:field验证某个字段有值的时候必须requireWithout:field验证某个字段没有值的时候必须requireCallback:callable验证当某个callable为真的时候字段必须
WTForms 支持如下类型的表单验证:验证类型说明Email验证电子邮件地址EqualTo比较两个字段的值;常用于要求输入两次秘钥进行确认的情况Length验证输入字符串的长度NumberRange验证输入的值在数字范围内DateRequired确保字段中有数据
这里以之前添加学生接口为例,可以添加如下代码:$captcha = $this->request->param('captcha');if(!captcha_check($captcha)){// 验证失败 throw new HttpException(401, "验证码验证失败");}else{ echo "验证成功";}如下图所示:下面演示验证失败的情况:下面演示验证成功的情况:
2.2.1 表单认证的过程说明Spring Security 支持从 HTML 的 Form 表单形式提交登录用户信息。表单认证可分为以下步骤:用户请求受保护资源;Spring Security 的 FilterSecurityInterceptor 对象,检测到当前用户认证未通过,应予以拒绝,并抛出 AccessDeniedException;当 AccessDeniedException 被 ExceptionTranslationFilter 接收后,其认定需要发起认证流程,此时用户被要求登录,认证服务器将登录地址(默认由 LoginUrlAuthenticationEntryPoint)返回给客户端;客户端浏览重定向到登录页面;登录页面有服务端渲染生成。图 2 表单登录流程当用户提交登录信息,认证服务器端的 UsernamePasswordAuthenticationFilter 就会被执行。此过程的具体执行过程如下:UsernamePasswordAuthenticationFilter 产生 UsernamePasswordAuthenticationToken,并存入从请求中获取的用户名、密码等信息;创建出的 Token 被传递给 AuthenticationManager 用于认证;认证成功或失败的后续流程同上一小节中关于 AbstractAuthenticationProcessingFilter 的执行过程一致。2.2.2 表单认证的开启默认情况下,Spring Security 开启了表单认证功能。如果我们需要显式配置,可用如下方式实现。创建 Security 配置文件: src/main/java/imooc/springsecurity/usernamepassword/config/WebSecurityConfig.java,并在其中添加 http.formLogin(withDefaults()) 的配置,完整代码如下:package imooc.springsecurity.usernamepassword.config;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.Customizer;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http.formLogin(Customizer.withDefaults()); }}访问 http://localhost:8080/user/me ,网页会自动跳转到登录页面。登录页面输入默认生成的用户名 「user」, 默认生成密码可在控制台日志中找到。如下图:提交登录后,通过认证,我们将在浏览器看到当前登录的用户名。当前登录用户为:「user」2.2.3 表单认证的配置默认情况下,表单登录的跳转地址是 /login,登录参数中用户名变量名为 username,密码变量名为 password。如果我们需要修改这些配置信息,可以通过如下方式实现:在 configure(HttpSecurity http) 方法中,为 http 的 formLogin 项修改配置。 protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login").permitAll() // 表单认证页面不需要权限 .anyRequest().authenticated(); // 其他页面需要登录用户才能访问 http.formLogin() .loginPage("/login") // 自定义表单认证页面地址 .usernameParameter("user") .passwordParameter("pass"); http.csrf().disable(); // 关闭 csrf 以通过认证,注意,这不是最好的做法,后续章节会有介绍。 }当然这一步中配置 /login 页面需要我们自己去实现。这里有几个需要注意的地方:自定义表单提交地址为 /login ,提交方法仅支持 POST;表单需要支持 CSRF 票据,即附带 _csrf 参数;用户名字段需要命名为 user;密码字段需要命名为 pass;当认证失败时,表单页面会收到 error 参数;当用户退出成功时,表单页面会收到 logout 参数。为了测试上述配置,我们创建一个测试登录页:新建 src/main/java/imooc/springsecurity/usernamepassword/controller/LoginController.java。package imooc.springsecurity.usernamepassword.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controllerpublic class LoginController { @RequestMapping("/login") public String viewLogin2() { return "/login.html"; }}新建 src/main/resources/templates/login.html<form method="post" action="/login"> <input type="text" name="user"> <input type="password" name="pass"> <input type="submit" value="登录"></form>访问测试:http://localhost:8080/user/me ,此时跳转到我们新建的登录页面。登录页面输入用户名密码后可看到用户信息。
表单用于收集信息,从 HTML 上讲,表单内容使用 form 标签进行包裹。<form action="/login"> <label> 用户名:<input type="text"> </label> <label> 密码:<input type="text"> </label> <div> <button type="submit">登入</button> </div></form>这就是一个相对简单的表单,其中包含文本框(input标签)与按钮(button标签),并使用 form 标签进行包裹。利用 form 标签,再触发其 submit 事件时,会将表单内容收集后提交个体 action 属性配置的路径。单其实把 form 标签去掉,在页面上展示的效果几乎是一样的。<label> 用户名:<input type="text"></label><label> 密码:<input type="text"></label><div> <button type="submit">登入</button></div>·所以自出现 AJAX 技术后,很多开发者都不再书写 form 标签,直接使用其他元素对表单内容进行包裹,因为业务上可能不需要使用 form 标签的特性来提交表单。其实不论是使用表单,还是不使用表单,表单的验证都是针对所有表单项的,即输入框、单选项、多选项等。在表单提交之前,需要对写着表单项的内容做校验,然后拦截提交操作。
class LoginForm(FlaskForm): name = StringField( label = '姓名', validators = [ DataRequired(message = '姓名不能为空') ] ) password = PasswordField( label = '密码', validators =[ DataRequired(message = '密码不能为空'), Length(min = 3, message = '密码至少包括 3 个字符') ] ) submit = SubmitField('登录')使用 WTForms 表单实现登录表单,LoginForm 继承于 FlaskForm,它包含 2 个字段 name 和 password。name 字段的验证器 DataRequired 要求字段不能为空;password 字段的验证器 DataRequired 要求字段不能为空,验证器 Length 要求密码至少包括 3 个字符。
class RegisterForm(FlaskForm): name = StringField( label = '姓名', validators = [ DataRequired(message = '姓名不能为空') ] ) password = PasswordField( label = '密码', validators =[ DataRequired(message = '密码不能为空'), Length(min = 3, message = '密码至少包括 3 个字符') ] ) submit = SubmitField('注册')使用 WTForms 表单实现注册表单,RegisterForm 继承于 FlaskForm,它包含 2 个字段 name 和 password。name 字段的验证器 DataRequired 要求字段不能为空;password 字段的验证器 DataRequired 要求字段不能为空,验证器 Length 要求密码至少包括 3 个字符。
定义:验证是连接阶段的第一步,这一阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机的自身安全。验证过程的主要验证信息:验证过程中,主要对三种类型的数据进行验证,分别是“元数据验证,字节码验证和符号引用验证”。具体内容请看下边的讲解。元数据验证:验证这个类是否有父类(除了 java.lang.Object 之外,所有类都应当有父类);验证这个类是否继承了不允许被继承的类(被 final 修饰的类);如果这个类不是抽象类,验证该类是否实现了其父类或接口之中所要求实现的所有方法;验证类中的字段、方法是否与父类产生矛盾(例如覆盖了父类的 final 字段,或者出现不符合规则的方法重载,例如方法参数都一致,但返回值类型却不同等等)。字节码验证:字节码验证主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。这个阶段将对类的方法体进行校验分析,保证被校验类的方法在运行时不会产生危害虚拟机安全的事件,例如:保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作。例如不会出现类似这样的情况:在操作数栈放置了一个int类型的数据,使用时却按long类型来加载入本地变量表中;保证跳转指令不会跳转到方法体以外的字节码指令上;保证方法体中的类型转换是有效的,例如可以把一个子类对象赋值给父类数据类型,但是把父类对象赋值给子类数据类型,甚至把对象赋值给与它毫无继承关系、完全不相干的一个数据类型,则是危险不合法的。符号引用验证:符号引用验证可以看作是类对自身以外(常量池中的各种符号引用)的信息进行匹配性校验,通常需要校验以下内容:符号引用中通过字符串描述的全限定名是否能够找到对应的类;在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段;符号引用中的类、字段、方法的访问性(private、default、protected、public)是否可被当前类访问。
对前面小节的例子程序进行如下的局部修改,修改类 LoginForm,增加一个成员函数 validate_password,如下:class LoginForm(FlaskForm): def validate_password(self, field): for char in field.data: print('!', char) if '0123456789'.find(char) < 0: return raise ValidationError('密码不能全部是数字')在第 2 行,定义成员函数 validate_password,验证函数的形式为 validate_字段名,在验证字段数据时会调用这个方法来验证对应的字段,在这里调用函数 validate_password 验证字段 password。在第 3 行到第 6 行,遍历密码字段 field 的每个字符,如果发现存在一个非数字的字符,则正常返回;如果所有的字符都是数字,则抛出异常 ValidationError。当用户输入的密码全部都是数字时,表单验证失败,提示错误信息为:‘密码不能全部是数字’。
表单我们在前面介绍 HTML 基础的时候介绍过。下面是之前完成的一个简单的表单示例,模仿普通网站的登录表单:(django-manual) [root@server first_django_app]# cat templates/test_form1.html{% load staticfiles %}<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}" />{% if not success %}<form action="/hello/test_form_view1/" method="POST">{% csrf_token %}<div><span>账号:</span><input class="input-text" type="text" placeholder="请输入登录手机号/邮箱" name="name" required/></div><div><span>密码:</span><input class="input-text" type="password" placeholder="请输入密码" name="password" required/></div><div><label style="font-size: 10px; color: grey"> <input type="checkbox" checked="checked" name="save_login"/>7天自动登录</label></div><div><input class="input-text input-red" type="submit" value="登录" style="width: 214px"/></div>{% if err_msg %}<div><label class="color-red">{{ err_msg }}</label</div>{% endif %}</form>{% else %} <p>登录成功</p>{% endif %}准备好视图函数:class TestFormView1(TemplateView): template_name = 'test_form1.html' # template_name = 'register.html' def get(self, requests, *args, **kwargs): return self.render_to_response(context={'success': False}) def post(self, requests, *args, **kwargs): success = True err_msg = "" name = requests.POST.get('name') password = requests.POST.get('password') if name != 'spyinx' or password != '123456': success = False err_msg = "用户名密码不正确" return self.render_to_response(context={'success': success, 'err_msg': err_msg})最后编写 URLConf,要和表单中的 action 属性值保持一致:urlpatterns = [ # ... # 表单测试 path('test_form_view1/', views.TestFormView1.as_view(), name='test_form_view1'),]接下来启动服务然后放对应的登录页面。操作如下:15这是一个简单的手写表单提交功能。但是实际上,我们不用写前端的那么 input 之类的,这些可以有 Django 的 Form 表单帮我们完成这些,不过基本的页面还是要有的。我们现在用 Django 的 Form 表单模块来实现和上面相同的功能,同时还能对表单中的元素进行校验,这能极大的简化我们的 Django 代码,不用在视图函数中进行 if-else 校验。首先准备好静态资源,包括模板文件以及 css 样式文件:/* 代码位置:static/css/main.css *//* 忽略部分无关样式 */.input-text { margin-top: 5px; margin-bottom: 5px; height: 30px;}.input-red { background-color: red}.color-red { color: red; font-size: 12px;}.checkbox { font-size: 10px; color: grey;}{# 代码位置:template/test_form2.html #}{% load staticfiles %}<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}" />{% if not success %}<form action="/hello/test_form_view2/" method="POST">{% csrf_token %}<div><span>{{ form.name.label }}:</span>{{ form.name }}<div><span>{{ form.password.label }}:</span>{{ form.password }}<div>{{ form.save_login }}{{ form.save_login.label }}</div><div><input class="input-text input-red" type="submit" value="登录" style="width: 214px"/></div>{% if err_msg %}<div><label class="color-red">{{ err_msg }}</label</div>{% endif %}</form>{% else %} <p>登录成功</p>{% endif %}注意:这个时候,我们用 form 表单对象中定义的属性来帮我们生成对应的 input 或者 checkbox 等元素。同样继续视图函数的编写。此时,我们需要使用 Django 的 Form 表单功能,先看代码,后面会慢慢介绍代码中的类、函数以及相关的参数含义:# 源码位置:hello_app/view.py# ...# 自定义密码校验def password_validate(value): """ 密码校验器 """ pattern = re.compile(r'^(?=.*[0-9].*)(?=.*[A-Z].*)(?=.*[a-z].*).{6,20}$') if not pattern.match(value): raise ValidationError('密码需要包含大写、小写和数字') # 定义的表单,会关联到前端页面,生成表单中的元素class LoginForm(forms.Form): name = forms.CharField( label="账号", min_length=4, required=True, error_messages={'required': '账号不能为空', "min_length": "账号名最短4位"}, widget=forms.TextInput(attrs={'class': "input-text", 'placeholder': '请输入登录账号'}) ) password = forms.CharField( label="密码", validators=[password_validate, ], min_length=6, max_length=20, required=True, # invalid时对应的错误信息 error_messages={'required': '密码不能为空', "invalid": "密码需要包含大写、小写和数字", "min_length": "密码最短8位", "max_length": "密码最>长20位"}, widget=forms.TextInput(attrs={'class': "input-text",'placeholder': '请输入密码', 'type': 'password'}) ) save_login = forms.BooleanField( required=False, label="7天自动登录", initial="checked", widget=forms.widgets.CheckboxInput(attrs={'class': "checkbox"}) )class TestFormView2(TemplateView): template_name = 'test_form2.html' def get(self, request, *args, **kwargs): form = LoginForm() return self.render_to_response(context={'success': False, 'form': form}) def post(self, request, *args, **kwargs): # 将数据绑定到表单,这样子才能使用is_valid()方法校验表单数据的合法性 form = LoginForm(request.POST) success = True err_msg = "" if form.is_valid(): login_data = form.clean() name = login_data['name'] password = login_data['password'] if name != 'spyinx' or password != 'SPYinx123456': success = False err_msg = "用户名密码不正确" else: success = False err_msg = form.errors['password'][0] print(success, err_msg, form.errors) return self.render_to_response(context={'success': success, 'err_msg': err_msg, 'form': form})最后,添加相应的 URLConf 配置,如下:# 代码位置:hello_app/urls.pyurlpatterns = [ # ... # 表单2测试 path('test_form_view2/', views.TestFormView2.as_view(), name='test_form_view2'),]最后,继续启动 first_django_app 工程,然后访问此 form 表单接口,可以发现效果和原来的是一样的。此外,我们通过直接在 form 表单中设置好相应的校验规则,Django 会自动帮我们处理校验问题,并通过 is_valid()方法来帮我们验证表单参数的合法性。比如上面我们自定义了一个密码校验器,输入的密码必须包含大写字母、小写字母和数字,三者缺一不可。我们只需要添加校验器,放到定义的 Form 的对应属性字段中即可,使用起来非常方便。参见下面的演示:16
在爬虫开发中我们经常会遇到一种反爬虫的手段就是验证码,那么如何才能绕过验证码拿到我们想要的数据呢?这节课我给大家介绍一个破验证码的利器–Pytesseract。Pytesseract 是 Python 中专门用来识别验证码和字符的常用第三方模块,它是一个根据 Google 开发的 Tesseract 包进行独立封装的产物。由于它在识别验证码方面具有得天独厚的优势,所以经常被爬虫开发程序员用来进行识别验证码。本节课我们就来使用 pytesseract 进行简单的验证码的识别。
class LoginForm(FlaskForm): email = StringField( label = '邮箱', validators = [ DataRequired(message = '邮箱不能为空'), Email(message = '请输入正确的邮箱') ] ) password = PasswordField( label = '密码', validators =[ DataRequired(message = '密码不能为空'), Length(min = 6, message = '密码至少包括 6 个字符') ] ) submit = SubmitField('登录')定义类 LoginForm,它继承于 FlaskForm,用于描述登录界面,登录界面是一个表单,包含有 3 个字段:email,显示 label 为 ‘邮箱’,包括 2 个验证器:DataRequired 和 Email,message 参数为验证失败的提示信息;password,显示 label 为 ‘密码’,包括 2 个验证器:DataRequired 和 Length,message 参数为验证失败的提示信息,min = 6 表示密码的最小长度;submit,提交按钮,提交表单给服务端。
大部分情况下,网页中展示的数据或者图片或者多媒体效果都是静态的数据,但是有时用户需要通过网页跟服务器进行动态的数据交互,例如登录、注册等操作,这时需要用到表单。表单其实是由一些输入框、单选、复选框等元素组成,用户点击提交按钮,浏览器会将这些数据发送给表单设置的 URL ,URL 对应的服务器接收到数据并处理之后,再将数据返回给浏览器。
许多做的非常好的网站都会有验证码校验,比如京东、淘宝的登录。更为复杂的还有12306网站那个让人头晕的识图验证等等。目前而言,验证码技术从原来的简单数字、字母识别,到滑块拖动、拼图认证以及最新的图片识别、汉字倒立等,已经越来越复杂和难辨。很多基于机器学习以及深度学习的高难度识别算法应运而生,但这些对于普通程序员而言,难以企及。我们唯有两方面突破:花钱买服务:网上有不少专门的验证码识别服务提供商,比如几年前比较流行的若快平台 (目前官网无法访问,似乎已经凉了)等;开源项目:如果舍不得花钱买服务的,我们只能寄希望于部分开源工具。好在还是有不少大神愿意将他们的研究代码、工具进行开源,这也使得我们能有机会去学习和使用这些工具去突破验证码的限制;京东的拼图验证12306的识图验证
接下来,我们来尝试使用它来验证验证码。验证码图片如下所示:代码如下:import pytesseractfrom PIL import Imageim=Image.open('test.jpeg')#灰度处理im=im.convert('L')#设置二值化的阈值threshold=170t=[]for i in range(256): if i<threshold: t.append(0) else: t.append(1)#通过表格转换成二进制图片,1的作用是白色,0就是黑色im=im.point(t,"1")im.show()print(pytesseract.image_to_string(im))#删除冗余字符print(pytesseract.image_to_string(im)[0:-1])
如果想要创建一个可重用的通用验证器,可以通过定义一个全局函数来实现。对前面小节的例子程序进行如下的局部修改,增加一个全局函数 validate_password,如下:def can_not_be_all_digits(form, field): for char in field.data: print('!', char) if '0123456789'.find(char) < 0: return raise ValidationError('密码不能全部是数字')函数 can_not_be_all_digits 验证字段 field 是否全部是数字,代码与上一个小节中的函数 validate_password 完全相同。同时修改类 LoginForm 的 PasswordField,如下:class LoginForm(FlaskForm): password = PasswordField( label = '密码', validators =[ DataRequired(message = '密码不能为空'), Length(min = 6, message = '密码至少包括 6 个字符'), can_not_be_all_digits ] )在第 7 行,validatros 中增加一个新的 validator——can_not_be_all_digits。当用户输入的密码全部都是数字时,表单验证失败,提示错误信息为:‘密码不能全部是数字’。
序列化是将数据库中的数据进行解析,返回前端需要的数据形式(如 json),而前端发来的数据,经过验证和处理,变为数据库需要的数据形式,这个过程称为反序列化。为了保证数据符合数据模型的要求,对前端发来的数据,根据字段类别、字段限制首先进行验证。在序列化器中,可以调用 is_valid() 方法进行验证,验证成功返回 True,否则返回 False。# serializers.pyfrom rest_framework import serializersclass StudentsSerializer(serializers.Serializer): id = serializers.IntegerField(label='ID', read_only=True) s_name = serializers.CharField(label='姓名', max_length=8) s_age = serializers.IntegerField(label='年龄', required=True) s_number = serializers.CharField(label='学号', max_length=16)通过构造序列化器对象,并将要反序列化的数据传递给 data 构造参数,进而进行验证:from AppDemo.serializers import StudentsSerializerdata = {'s_name': '小黄', 's_age': 15, 's_number': '004'}serializer = StudentsSerializer(data=data)serializer.is_valid() # 返回Trueserializer.errors# {}serializer.validated_data # 验证通过的数据# OrderedDict([('s_name', '小黄'), ('s_age', 15), ('s_number', '004')])
本小节主要介绍如何在路由验证请求的合法性,主要包括请求的变量规则、url后缀、路由中间件。
本小节讲解了 Flask 的表单验证的概念和使用,总结如下:使用 Flask 的表单验证功能,可以方便的验证表单中的字段的有效性,并渲染输出表单的 HTML 代码。
若想要在不同场景下验证不同的字段,可在 protected $scene 总设置如下内容: /** * 设置验证场景 * @var string[][] */ protected $scene = [ 'add-student' => ['name', 'age', 'id_number'], 'edit-student' => ['name', 'age'] ];如下图所示:
命令选项说明–print-certs显示有关 APK 签名证书的信息。–min-sdk-version用来确认 APK 签名将通过验证的最低 Android 框架 API 级别。–max-sdk-version用来确认 APK 签名将通过验证的最高 Android 框架 API 级别。–verbose使用详细输出模式。-Werr将警告视为错误。
若想要单应用 app\Models 目录下快速生成模型,可以使用如下命令:php think make:validate Test如下图所示:生成的验证器文件内容如下:<?phpdeclare (strict_types = 1);namespace app\validate;use think\Validate;class Test extends Validate{ /** * 定义验证规则 * 格式:'字段名' => ['规则1','规则2'...] * * @var array */ protected $rule = []; /** * 定义错误信息 * 格式:'字段名.规则名' => '错误信息' * * @var array */ protected $message = [];}
2.2.1 验证前的准备打开项目中的 pom.xml 文件,添加 validation-api 依赖包,大家需要注意一下,包名是以 javax 开头的。因为 Spring MVC 并没有实现 JSR 接口规范,这里选择 hibernate-validator ;Tips: hibernate-validator 是 Hibernate 提供的 JSR 具体实现模块。<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version></dependency><dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.0.Final</version></dependency>打开 WebConfig 配置类,通知 Spring MVC 创建 LocalValidatorFactoryBean 对象。可以使用这个工厂对象创建具体的实现了 JSR 规范的验证器。@Beanpublic LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); localValidatorFactoryBean.setProviderClass(HibernateValidator.class); return localValidatorFactoryBean;}2.2.2 JSR 验证流程先设定一个需求:添加老师信息。构建一个 Teacher 类,并在 Teacher 类的相关属性上添加对应注解;public class Teacher {@NotNull(message = "姓名不能为空")private String name; @Min(value = 22,message = "年龄不能小于 22 岁")private Integer age;Tips: JSR 注解有一个 message 属性,用来保存错误提示信息。编写 teacher.html 页面;<form action="teacher/save" method="post"> 老师姓名:<input type="text" value="" name="name"/> <br/> 老师年龄:<input type="text" value="" name="age"/> <br/> <input type="submit" value="添加" name="btnSave"/> <input type="reset" value="重置" name="btnReset"/></form>编写响应控制器;@Controller@RequestMapping("/teacher")public class TeacherAction { @RequestMapping(value = "/save",method = RequestMethod.POST) public String register(@Valid Teacher teacher,BindingResult result) { if (result.hasErrors()) { return "fail"; } return "success"; }}Tips: @Valid 注解表示在绑定数据之后对数据进行验证。BindingResult 组件用来保存验证过程中的错误信息。除了可以使用 BindingResult ,此处还可以使用 Errors 替代。在浏览器中访问到 teacher.html 页面,输入不符合规则的数据后提交(年龄小于 22 岁);最后会在浏览器中看到。如此,整个验证过程完毕。
完成了上述一系列配置,我们如何验证刚刚的配置是否成功呢?首先,在 Windows 搜索栏处输入cmd找到命令提示符应用,再点击打开:输入java -version命令。显示如下输出,即证明你已经成功完成了环境变量的配置。
那么如何验证上述一系列操作是否成功呢?打开终端,键入 java -version 命令, 看到如下输入,即证明你已经成功配置好了环境变量。
字段的验证规则可以在 protected $rule 中设置如下规则: /** * 设置字段验证规则 * @var string[] */ protected $rule = [ 'name' => 'require|max:5', 'age' => 'number|between:1,120', 'id_number' => 'require|in:18', ];如下图所示: