#数据验证:确保用户输入的邮箱、密码符合安全规范
📂 所属阶段:第二阶段 — 交互与数据(核心篇)
🔗 相关章节:Flask-WTF 插件 · 密码安全加密
#1. WTForms 验证器概述
#1.1 验证器一览
| 验证器 | 说明 |
|---|---|
DataRequired | 字段不能为空 |
Email | 邮箱格式验证 |
Length | 长度限制 |
Range | 数字范围限制 |
EqualTo | 两字段相等(如确认密码) |
Regexp | 正则表达式 |
URL | URL 格式验证 |
NumberRange | 数字范围(配合 IntegerField) |
AnyOf | 枚举值 |
NoneOf | 排除值 |
InputRequired | 原始数据存在(非空字符串) |
Optional | 允许为空 |
#2. 内置验证器详解
#2.1 常用验证组合
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, IntegerField, TextAreaField
from wtforms.validators import (
DataRequired, Email, Length, EqualTo,
Regexp, URL, NumberRange, Optional
)
class ArticleForm(FlaskForm):
title = StringField("标题", validators=[
DataRequired(message="标题不能为空"),
Length(min=5, max=100, message="标题长度需在 5-100 个字符之间"),
# 自定义正则:只允许中文、英文、数字
Regexp(r"^[\u4e00-\u9fa5a-zA-Z0-9]+$", message="标题只能包含中文、英文字母和数字"),
])
url_slug = StringField("URL别名", validators=[
DataRequired(),
Length(max=50),
# 小写字母、数字、连字符
Regexp(r"^[a-z0-9-]+$", message="URL别名只能包含小写字母、数字和连字符"),
])
price = IntegerField("价格(元)", validators=[
DataRequired(message="价格不能为空"),
NumberRange(min=0, max=999999, message="价格必须在 0-999999 之间"),
])
website = StringField("个人网站", validators=[
Optional(),
URL(message="请输入有效的 URL,以 http:// 或 https:// 开头"),
])
content = TextAreaField("正文", validators=[
DataRequired(message="正文不能为空"),
Length(min=10, message="正文至少 10 个字符"),
])#2.2 正则表达式验证
# 手机号验证(中国)
phone = StringField("手机号", validators=[
Regexp(
r"^1[3-9]\d{9}$", # 13x-9x 开头的 11 位手机号
message="请输入有效的手机号"
)
])
# 用户名验证(字母开头,字母数字下划线,3-20位)
username = StringField("用户名", validators=[
Regexp(
r"^[a-zA-Z]\w{2,19}$",
message="用户名需以字母开头,3-20个字符,只能包含字母、数字和下划线"
)
])
# 身份证号验证
id_card = StringField("身份证号", validators=[
Regexp(
r"^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$",
message="请输入有效的身份证号"
)
])#3. 自定义验证器
#3.1 表单类中的自定义验证
class ProfileForm(FlaskForm):
username = StringField("用户名", validators=[
DataRequired(), Length(3, 20)
])
bio = StringField("个人简介", validators=[
Length(max=200, message="简介最多 200 个字符")
])
# 字段级验证(针对特定字段)
def validate_username(self, field):
# 检查用户名是否包含敏感词
if any(word in field.data for word in ["admin", "root", "system"]):
raise ValidationError("该用户名不可用")
# 检查用户名是否已被占用
if User.query.filter_by(username=field.data).first():
raise ValidationError("该用户名已被注册")
# 字段级验证:个人简介禁止特定词汇
def validate_bio(self, field):
if field.data and "http" in field.data.lower():
raise ValidationError("简介中禁止包含链接")#3.2 独立的自定义验证器
# app/validators.py
from wtforms.validators import ValidationError
def contains_no_links(form, field):
"""验证字段中不包含链接"""
import re
url_pattern = r"https?://[^\s]+"
if re.search(url_pattern, field.data, re.IGNORECASE):
raise ValidationError("禁止包含 URL 链接")
def starts_with_uppercase(form, field):
"""验证字段以大写字母开头"""
if not field.data[0].isupper():
raise ValidationError("必须以大写字母开头")
# 在表单中使用
class PostForm(FlaskForm):
title = StringField("标题", validators=[
DataRequired(), contains_no_links, starts_with_uppercase
])#3.3 异步验证(AJAX)
# app/routes/api.py
@api_bp.route("/check/username/<username>")
def check_username(username):
"""检查用户名是否可用(供前端 AJAX 调用)"""
exists = User.query.filter_by(username=username).first() is not None
return jsonify({"available": not exists, "username": username})
@api_bp.route("/check/email/<email>")
def check_email(email):
"""检查邮箱是否可用"""
import re
if not re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", email):
return jsonify({"valid": False})
exists = User.query.filter_by(email=email).first() is not None
return jsonify({"valid": True, "available": not exists})前端实时验证:
// 前端实时验证
document.getElementById("username").addEventListener("blur", async function() {
const response = await fetch(`/api/check/username/${this.value}`);
const data = await response.json();
const errorEl = document.getElementById("username-error");
if (!data.available) {
errorEl.textContent = "用户名已被注册";
errorEl.style.display = "block";
} else {
errorEl.style.display = "none";
}
});#4. 客户端验证 + 服务器端验证
#4.1 HTML5 原生验证
<input type="email"
name="email"
required
minlength="5"
maxlength="100"
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
placeholder="your@email.com">#4.2 完整验证体系
双重验证 = 客户端(用户体验)+ 服务器端(安全保障)
1. 客户端验证:
→ HTML5 原生属性(required, minlength)
→ JavaScript 实时反馈(无需等待服务器)
2. 服务器端验证(必须):
→ WTForms 验证器(防止绕过客户端直接请求)
→ 数据库唯一性检查
→ 业务逻辑验证#5. 小结
# 验证器速查
# 必填
DataRequired(message="...")
# 邮箱
Email(message="...")
# 长度
Length(min=3, max=50, message="...")
# 正则
Regexp(r"^\w+$", message="...")
# 相等
EqualTo("password", message="两次密码不一致")
# 范围
NumberRange(min=0, max=100, message="...")
# URL
URL(message="...")
# 可选(允许空)
Optional()
# 自定义
def validate_field(self, field):
if bad_condition:
raise ValidationError("错误信息")💡 记住:客户端验证只是提升用户体验,服务器端验证才是真正的安全防线。永远不要只依赖客户端验证。
🔗 扩展阅读

