任何从客户端发送来的数据,后端都是不信任的。
表单传送过程:客户端--->flask(request)--->取出
注册表单
<!--{{url_for()}}代表form表单将要发送到后端的路由-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<!--{{url_for()}}代表form表单将要发送到后端的路由-->
<form action="{{url_for('register')}}" method="post">
<label>用户名:</label><input name="name" type="text">
<br>
<label>手机号:</label><input name="phone" type="text">
<br>
<label>密码:</label><input name="pwd" type="password">
<br>
<label>确认密码:</label><input name="confirm_pwd" type="password">
<br>
<br>
<input type="submit" value="立即注册">
<br>
</form>
</body>
</html>
后端校验方式(1):
import re
from flask import Flask, request, render_template, Response
from werkzeug.exceptions import abort
app = Flask(__name__)
@app.route('/register',methods=["GET","POST"])
def register():
if request.method == 'GET':
return render_template('register.html')
name = request.form.get('name')
phone = request.form.get('phone')
pwd = request.form.get('pwd')
pwd_confirm = request.form.get('confirm_pwd')
# 表单验证
if not name:
res = Response(render_template('register.html', msg='用户名不能为空'), status='412', content_type='text/html;charset=utf-8')
abort(res)
if not re.match(r'^1[3,4,5,6,7,8,9]\d{9}$', phone):
res = Response(render_template('register.html', msg='手机号码格式错误'), status='412', content_type='text/html;charset=utf-8')
abort(res)
if len(pwd)<6:
res = Response(render_template('register.html', msg='密码必须大于6位'), status='412', content_type='text/html;charset=utf-8')
abort(res)
if pwd_confirm != pwd:
res = Response(render_template('register.html', msg='密码不一致'), status='412',content_type='text/html;charset=utf-8')
abort(res)
return Response(render_template('login.html', msg='注册成功'), status='200',content_type='text/html;charset=utf-8')
if __name__ == '__main__':
app.run(debug=True)
后端校验方式(2):
import re
from flask import Flask, request, render_template, Response
from werkzeug.exceptions import abort
app = Flask(__name__)
def form_check(form):
name = form.get('name')
phone = form.get('phone')
pwd = form.get('pwd')
pwd_confirm = form.get('confirm_pwd')
# 表单验证
if not name:
res = Response(render_template('register.html', msg='用户名不能为空'), status='412',
content_type='text/html;charset=utf-8')
abort(res)
if not re.match(r'^1[3,4,5,6,7,8,9]\d{9}$', phone):
res = Response(render_template('register.html', msg='手机号码格式错误'), status='412',
content_type='text/html;charset=utf-8')
abort(res)
if len(pwd) < 6:
res = Response(render_template('register.html', msg='密码必须大于6位'), status='412',
content_type='text/html;charset=utf-8')
abort(res)
if pwd_confirm != pwd:
res = Response(render_template('register.html', msg='密码不一致'), status='412',
content_type='text/html;charset=utf-8')
abort(res)
@app.route('/register',methods=["GET","POST"])
def register():
if request.method == 'GET':
return render_template('register.html')
form = request.form
form_check(form)
return Response(render_template('login.html', msg='注册成功'), status='200', content_type='text/html;charset=utf-8')
if __name__ == '__main__':
app.run(debug=True)
后端校验方式(3):
import re
from flask import Flask, request, render_template, Response
from werkzeug.exceptions import abort
app = Flask(__name__)
@app.route('/register',methods=["GET","POST"])
def register():
if request.method == 'GET':
return render_template('register.html')
name = request.form.get('name')
phone = request.form.get('phone')
pwd = request.form.get('pwd')
pwd_confirm = request.form.get('confirm_pwd')
# 表单验证
if not name:
return render_template('register.html', msg='用户名不能为空'),412, {"content_type":'text/html;charset=utf-8'}
if not re.match(r'^1[3,4,5,6,7,8,9]\d{9}$', phone):
return render_template('register.html', msg='手机号码格式错误'), 412, {"content_type": 'text/html;charset=utf-8'}
if len(pwd)<6:
return render_template('register.html', msg='密码应大于6位'), 412, {"content_type": 'text/html;charset=utf-8'}
if pwd_confirm != pwd:
return render_template('register.html', msg='密码不一致'), 412, {"content_type": 'text/html;charset=utf-8'}
return Response(render_template('login.html', msg='注册成功'), status='200',content_type='text/html;charset=utf-8')
if __name__ == '__main__':
app.run(debug=True)
效果
后端验证方式(4):
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import Regexp, DataRequired, Length, EqualTo
class RegisterForm(FlaskForm): # 继承自FlaskForm
"""
表单属性要和前端传递过来的表单name属性一致,最好也要和数据库里面的字段一致
"""
name = StringField(label='用户名',
validators=[Length(3, 12, message='用户名格式错误'),
DataRequired('用户名不能为空')])
phone = StringField(label='手机号码',
validators=[Regexp(r'^1[3,4,5,6,7,8,9]\d{9}$', message='手机号码格式错误'),
DataRequired('手机号码不能为空')])
pwd = PasswordField(label='密码',
validators=[Length(6, 18, message='密码格式错误'),
DataRequired('密码不能为空')])
confire_pwd = PasswordField('确认密码', validators=[EqualTo("pwd", message='确认密码格式错误')])
import os
from src.form_check.helpers.forms import RegisterForm
from flask import Flask, request, render_template, Response
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24) # 随机生产字符串,flask需要传给前端,不然报错
@app.route('/')
def hello():
return 'hello world'
@app.route('/register', methods=["GET", "POST"])
def register():
form = RegisterForm(formdata=request.form) # RegisterForm需要一个form表单的参数。
# form = RegisterForm(data=request.json) # 如果form表单数据是通过ajax请求或者postman之类工具发送过来的json格式数据,通过data=request.json来接收。
if request.method == 'GET':
return render_template('register.html', form=form) # 此处将通过form=form,将csrf_token数据传回前端
# 如果是post,开始验证
# form.validate():验证主函数
# form.data:获取所有数据
# form.errors:获取错误信息
if form.validate():
return 'success'
return f'error:{form.errors}'
if __name__ == '__main__':
app.run(debug=True)
后端必须要返回一个SECRET_KYEY给前端(csrf_token密钥),不然报错。
form = RegisterForm(data=request.json) # 如果form表单数据是通过ajax请求或者postman之类工具发送过来的json格式数据,通过data=request.json来接收,不可再用formdata=request.json接收。
前端:添加form.hidden_tag()后,前端的cookies里就会多一个crsf_token数据,保证安全。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<form action="{{url_for('register')}}" method="post">
<!--添加form.hidden_tag()后,前端的cookies里就会多一个crsf_token数据,保证安全-->
{{ form.hidden_tag() }}
<label>用户名:</label><input name="name" type="text">
<br>
<label>手机号:</label><input name="phone" type="text">
<br>
<label>密码:</label><input name="pwd" type="password">
<br>
<label>确认密码:</label><input name="confirm_pwd" type="password">
<br>
<br>
<input type="submit" value="立即注册">
{{msg}}
<br>
</form>
</body>
</html>
效果:
FlaskForm中其他可校验字段类型:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, IntegerField, DecimalField, DateField, RadioField, SelectField, \
SelectMultipleField, TextAreaField, BooleanField, SubmitField
from wtforms.validators import Regexp, DataRequired, Length, EqualTo, Email, NumberRange
class RegisterForm(FlaskForm): # 继承自FlaskForm
"""
表单属性要和前端传递过来的表单name属性一致,最好也要和数据库里面的字段一致
"""
name = StringField(label='用户名',
validators=[Length(3, 12, message='用户名格式错误'),
DataRequired('用户名不能为空')])
phone = StringField(label='手机号码',
validators=[Regexp(r'^1[3,4,5,6,7,8,9]\d{9}$', message='手机号码格式错误'),
DataRequired('手机号码不能为空')])
pwd = PasswordField(label='密码',
validators=[Length(6, 18, message='密码格式错误'),
DataRequired('密码不能为空')])
confire_pwd = PasswordField('确认密码', validators=[EqualTo("pwd", message='确认密码格式错误')])
# Text Field类型,文本输入框,Email格式
email = StringField('email address', validators=[Email()])
# IntegerField类型,必须输入整数数值,范围16~70
age = IntegerField('age', validators=[NumberRange(min=16, max=70)])
# DecimalField类型,必须输入数值,显示时保留1位小数
height = DecimalField('height(Centimeter)', places=1)
# DateField类型,必须输入'年-月-日'格式日期
birthday = DateField('birthday', format='%Y-%m-%d')
# RadioField类型,单选框,choices里面的内容会在ul标签里,里面每1项(值,显示名)是一对
gender = RadioField('gender', choices=[('m', 'Male'), ('f', 'Female')], validators=[DataRequired()])
# SelectField类型,下拉单选框,choices里面内容会在option里,里面每1项(值,显示名)是一对
job = SelectField('job', choices=[('teacher', 'Teacher'), ('doctor', 'Doctor'), ('engineer', 'Engineer')])
# SelectMultipleField类型,下拉多选框,choices里面内容会在option里,里面每1项(值,显示名)是一对
hobby = SelectMultipleField('hobby', choices=[('swim', 'swimming'), ('skate', 'skating'), ('hike', 'hiking')])
# TextAreaField类型,段落输入框
description = TextAreaField('introduction of someone')
# BooleanField类型,可做CheckBox类型使用,单选框,default='checked'即默认是选上的
accept_terms = BooleanField('我接收服务条款', default='checked', validators=[DataRequired()])
# SubmitField按钮
submit = SubmitField('Register')
wtf自定义验证器validators:
import re
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired, ValidationError
class Mobile:
"""
自定义手机号码验证器
"""
regex = re.compile(r'^1[3,4,5,6,7,8,9]]\d{9}$')
def __init__(self, message=None):
if message is None:
self.message = '输入的不是手机号码'
self.message = message
def __call__(self, form, field):
match = self.regex.match(field.data)
if not match:
message = self.message
raise ValidationError(message)
return match
class RegisterForm(FlaskForm): # 继承自FlaskForm
"""
表单属性要和前端传递过来的表单name属性一致,最好也要和数据库里面的字段一致
"""
phone = StringField(label='手机号码',
validators=[
Mobile('手机号码格式错误'),
DataRequired('手机号码不能为空')])