Flask表单基础 #
一、表单概述 #
1.1 什么是表单 #
表单是Web应用中用户与服务器交互的主要方式,用于收集用户输入的数据。
1.2 表单处理流程 #
text
用户填写表单
↓
提交到服务器
↓
Flask接收请求
↓
验证数据
↓
处理业务逻辑
↓
返回响应
二、HTML表单 #
2.1 基本表单 #
templates/login.html:
html
<!DOCTYPE html>
<html>
<head>
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<form method="POST" action="/login">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
</div>
<div>
<label>
<input type="checkbox" name="remember"> 记住我
</label>
</div>
<button type="submit">登录</button>
</form>
</body>
</html>
2.2 表单属性 #
| 属性 | 说明 |
|---|---|
| method | 提交方法(GET/POST) |
| action | 提交URL |
| enctype | 编码类型(multipart/form-data用于文件上传) |
2.3 表单控件 #
html
<!-- 文本输入 -->
<input type="text" name="username">
<!-- 密码输入 -->
<input type="password" name="password">
<!-- 邮箱 -->
<input type="email" name="email">
<!-- 数字 -->
<input type="number" name="age" min="0" max="150">
<!-- 日期 -->
<input type="date" name="birthday">
<!-- 单选框 -->
<input type="radio" name="gender" value="male"> 男
<input type="radio" name="gender" value="female"> 女
<!-- 复选框 -->
<input type="checkbox" name="hobby" value="reading"> 阅读
<input type="checkbox" name="hobby" value="music"> 音乐
<!-- 下拉选择 -->
<select name="country">
<option value="cn">中国</option>
<option value="us">美国</option>
</select>
<!-- 多行文本 -->
<textarea name="content" rows="5"></textarea>
<!-- 文件上传 -->
<input type="file" name="avatar">
<!-- 隐藏字段 -->
<input type="hidden" name="token" value="xxx">
三、处理表单请求 #
3.1 接收表单数据 #
python
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# 获取表单数据
username = request.form.get('username')
password = request.form.get('password')
remember = request.form.get('remember')
# 处理登录逻辑
if username and password:
return f'登录成功: {username}'
else:
return '请填写完整信息'
return render_template('login.html')
3.2 获取表单数据的方法 #
python
@app.route('/submit', methods=['POST'])
def submit():
# 获取单个值
username = request.form.get('username')
# 获取值,不存在时返回默认值
remember = request.form.get('remember', 'off')
# 获取多个同名值
hobbies = request.form.getlist('hobby')
# 获取所有表单数据
all_data = request.form.to_dict()
return f'用户名: {username}, 爱好: {hobbies}'
3.3 处理文件上传 #
python
import os
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
return '没有文件'
file = request.files['file']
if file.filename == '':
return '没有选择文件'
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return '上传成功'
return render_template('upload.html')
四、CSRF保护 #
4.1 什么是CSRF #
CSRF(跨站请求伪造)是一种攻击方式,攻击者诱导用户在已登录的网站上执行非预期的操作。
4.2 手动实现CSRF保护 #
python
import secrets
from flask import session
@app.route('/form', methods=['GET', 'POST'])
def form():
if request.method == 'GET':
# 生成CSRF令牌
token = secrets.token_hex(32)
session['csrf_token'] = token
return render_template('form.html', csrf_token=token)
# POST请求验证CSRF令牌
token = request.form.get('csrf_token')
if token != session.get('csrf_token'):
return 'CSRF验证失败', 400
# 处理表单
return '提交成功'
html
<form method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<!-- 其他表单字段 -->
<button type="submit">提交</button>
</form>
五、表单验证 #
5.1 手动验证 #
python
@app.route('/register', methods=['GET', 'POST'])
def register():
errors = {}
if request.method == 'POST':
username = request.form.get('username', '').strip()
email = request.form.get('email', '').strip()
password = request.form.get('password', '')
confirm = request.form.get('confirm', '')
# 验证用户名
if not username:
errors['username'] = '用户名不能为空'
elif len(username) < 3:
errors['username'] = '用户名至少3个字符'
# 验证邮箱
if not email:
errors['email'] = '邮箱不能为空'
elif '@' not in email:
errors['email'] = '邮箱格式不正确'
# 验证密码
if not password:
errors['password'] = '密码不能为空'
elif len(password) < 6:
errors['password'] = '密码至少6个字符'
# 验证确认密码
if password != confirm:
errors['confirm'] = '两次密码不一致'
if not errors:
# 注册成功
return '注册成功'
return render_template('register.html', errors=errors)
5.2 在模板中显示错误 #
html
<form method="POST">
<div>
<label>用户名:</label>
<input type="text" name="username" value="{{ request.form.username }}">
{% if errors.username %}
<span class="error">{{ errors.username }}</span>
{% endif %}
</div>
<div>
<label>邮箱:</label>
<input type="email" name="email" value="{{ request.form.email }}">
{% if errors.email %}
<span class="error">{{ errors.email }}</span>
{% endif %}
</div>
<div>
<label>密码:</label>
<input type="password" name="password">
{% if errors.password %}
<span class="error">{{ errors.password }}</span>
{% endif %}
</div>
<button type="submit">注册</button>
</form>
六、表单重填 #
6.1 保留表单数据 #
python
@app.route('/edit/<int:id>', methods=['GET', 'POST'])
def edit(id):
item = get_item(id)
if request.method == 'POST':
# 更新数据
item.name = request.form.get('name')
item.save()
return redirect(url_for('view', id=id))
return render_template('edit.html', item=item)
html
<form method="POST">
<input type="text" name="name" value="{{ item.name }}">
<button type="submit">保存</button>
</form>
6.2 错误后保留输入 #
html
<form method="POST">
<input type="text" name="username"
value="{{ request.form.username or user.username }}">
<input type="email" name="email"
value="{{ request.form.email or user.email }}">
<button type="submit">保存</button>
</form>
七、多步骤表单 #
7.1 使用Session存储 #
python
@app.route('/wizard/step1', methods=['GET', 'POST'])
def wizard_step1():
if request.method == 'POST':
session['step1'] = {
'name': request.form.get('name'),
'email': request.form.get('email')
}
return redirect(url_for('wizard_step2'))
return render_template('wizard/step1.html')
@app.route('/wizard/step2', methods=['GET', 'POST'])
def wizard_step2():
if request.method == 'POST':
session['step2'] = {
'address': request.form.get('address'),
'phone': request.form.get('phone')
}
return redirect(url_for('wizard_step3'))
return render_template('wizard/step2.html')
@app.route('/wizard/step3', methods=['GET', 'POST'])
def wizard_step3():
if request.method == 'POST':
# 合并所有步骤数据
data = {}
data.update(session.get('step1', {}))
data.update(session.get('step2', {}))
data['notes'] = request.form.get('notes')
# 保存数据
save_data(data)
# 清除session
session.pop('step1', None)
session.pop('step2', None)
return redirect(url_for('wizard_done'))
return render_template('wizard/step3.html')
八、表单安全 #
8.1 输入过滤 #
python
from markupsafe import escape
@app.route('/comment', methods=['POST'])
def add_comment():
content = request.form.get('content')
# 转义HTML
safe_content = escape(content)
# 或使用bleach库清理HTML
import bleach
clean_content = bleach.clean(content, tags=['b', 'i', 'u'])
save_comment(safe_content)
return '评论成功'
8.2 文件上传安全 #
python
import os
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
if file:
# 安全文件名
filename = secure_filename(file.filename)
# 检查文件扩展名
ext = os.path.splitext(filename)[1].lower()
if ext not in ['.jpg', '.png', '.gif']:
return '不允许的文件类型'
# 检查文件大小
file.seek(0, os.SEEK_END)
size = file.tell()
file.seek(0)
if size > 5 * 1024 * 1024: # 5MB
return '文件太大'
# 保存文件
file.save(os.path.join(UPLOAD_FOLDER, filename))
return '上传成功'
九、最佳实践 #
9.1 使用POST处理表单 #
python
# 推荐:使用POST
@app.route('/create', methods=['GET', 'POST'])
def create():
if request.method == 'POST':
# 处理表单
pass
return render_template('create.html')
# 不推荐:使用GET处理敏感操作
@app.route('/delete/<int:id>') # 应该用POST
def delete(id):
delete_item(id)
return redirect(url_for('list'))
9.2 重定向模式 #
python
@app.route('/create', methods=['GET', 'POST'])
def create():
if request.method == 'POST':
# 处理表单
item = create_item(request.form)
# PRG模式:POST后重定向
return redirect(url_for('view', id=item.id))
return render_template('create.html')
9.3 表单验证分离 #
python
def validate_registration(form):
errors = {}
if not form.get('username'):
errors['username'] = '用户名不能为空'
if not form.get('email'):
errors['email'] = '邮箱不能为空'
return errors
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
errors = validate_registration(request.form)
if not errors:
create_user(request.form)
return redirect(url_for('login'))
return render_template('register.html', errors=errors)
return render_template('register.html')
十、总结 #
10.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 表单提交 | POST方法 |
| 获取数据 | request.form.get() |
| 文件上传 | request.files |
| CSRF保护 | 防止跨站请求伪造 |
| 数据验证 | 验证必填项和格式 |
| 安全处理 | 转义和过滤 |
10.2 下一步 #
现在你已经了解了表单基础,接下来让我们学习 Flask-WTF扩展,使用更强大的表单处理工具!
最后更新:2026-03-28