Flask扩展库 | Flask-WTF学习

本文介绍Flask-WTF库的使用,包括安全表单、文件上传和reCAPTCHA功能,以及如何在Flask应用中集成CSRF保护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Flask-WTF是Flask与WTForms的简单集成,是Flask的一个扩展库,实现的功能如下:

  1. 集成了WTForms
  2. 使用CSRF token保护表单(防止CSRF);
  3. 全球CSRF保护;
  4. 支持reCAPTCHA(验证码);
  5. 使用Flask-Uploads支持文件上传
  6. 使用Flask-Babel提供国际化。

CSRF:Cross-Site Request Forgery简写,跨站请求伪造。Flask-WTF封装了防止CSRF功能。原理可参考博客
WTForms:是一个用于Python Web开发的提供灵活的表单验证和渲染的库。
表单:在网页中主要负责数据采集功能。一个表单有3个基本组成部分(表单标签、表单域、表单按钮)。

flask_wtf源码 目录结构:

├── _compat.py
├── csrf.py             ----防止CSRF
├── file.py             ----文件上传
├── form.py             ----表单
├── html5.py
├── i18n.py             ----国际化
├── __init__.py
└── recaptcha           ----验证码
    ├── fields.py
    ├── __init__.py
    ├── validators.py
    └── widgets.py

1、安装

不作过多讲述
像其他Flask扩展一样,安装时名称是flask-wtfimport时是flask_wtf,注意横线、下划线,这是惯例。

2、表单

创建表单主要是3方面:
  1. 安全表单
  2. 文件上传(表单)
  3. reCAPTCHA(表单)

2.1、安全表单

2.1.1、创建表单

from flask_wtf import FlaskForm # 从flask_wtf包中的__init__.py导入这个FlaskForm类,来自forms.py模块
from wtforms import StringField # 从wtforms包导入StringField类
from wtforms.validatiors import DataRequired

class MyForm(FlaskForm):
	name = StringField('name', validators=[DataRequired()])

注意:从flask-wtf 0.9.0开始,Flask-WTF不再从wtforms import任何内容,因此必须开发者自己从wtforms去导入(如StringField等)
另外,CSRF token 会被自动创建,我们可以在模板中渲染它:

<form method="POST" action="/">
	{{ form.csrf_token }}
	{{ form.name.label }} {{ form.name(size=20) }}
	<input type="submit" value="Go">
</form>

假如在表单中有多个隐藏字段(??),可在块中使用hidden_tag():

<form method="POST" action="/">
	{{ form.hidden_tag() }}
	{{ form.name.label }} {{ form.name(size=20) }}
</form>

默认情况下
FlaskForm就是一个具有CSRF保护的session安全表单了。如果不想CSRF保护,可传入:

form = FlaskForm(csrf_enabled=False)

也可以在全局上配置它,但不建议这么做:

WTF_CSRF_ENABLED = False

为了生成csrf token,我们必须有一个secret key,通常它与我们的Flask app的secret key是相同的。但是若想设置一个不同的,也可以:

WTF_CSRF_SECRET_KEY = ' a random string'

2.1.2、验证表单

在view handler中验证请求:

@app.route('/submit', methods=('GET', 'POST'))
def submit():
	form = MyForm()
	if form.validate_on_submit():
		return redirect('/success')
	return render_template('submit.html', form=form)

注意:我们不必将request.form传递给Flask-WTF,因为它将自动load;便捷的是validate_on_submit将检查它是否为一个POST请求、以及是否有效。

2.2、文件上传

2.2.1、创建文件上传表单

Flask-WTF提供的FileField类 不同于WTForms提供的。它会检查文件是一个FileStorage的非空实例,另外data将是None。

from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename

class PhotoForm(FlaskForm):
	photo = FileField(validators=[FileRequired()])

@app.route('/upload', methods=['GET', 'POST'])
def unpload():
	if form.validate_on_submit():
		f = form.photo.data
		filename = secure_filename(f.filename)
		f.save(os.path.join(
			app.instance_path, 'photos', filename
		))
		return redirect(url_for('index'))
	return render_template('upload.html', form=form)

记得将HTML模板 form标签的enctype设置为multipart/form-data,另外request.files为空:

<form method="POST" enctype="multipart/form-data">
	...
</form>

Flask-WTF handler将会为我们将form data传递给表单。如果想在data中显式地传递,记得request.form必须结合request.files,这样让表单看到file data。

form = PhotoForm()

# 等价于
from flask import request
from werkzeug.datastructures import CombinedMultiDict
form = PhotoForm(CombinedMultiDict((request.files, request.form)))

2.2.2、验证表单

Flask-WTF通过FileRequiredFileAllowed验证文件上传。他它们使用Flask-WTF和WTForms的FileField类都行。FileAllowed将和Flask-Uploads一起工作。

from flask_uploads import UploadSet, IMAGES
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed, FileRequired

images = UploadSet('images', IMAGES)

class UploadForm(FlaskForm):
	upload = FileField('image', validators=[
		FileRequired(), FileAllowed(images, 'Images only!')
	])

也可以直接通过传递扩展名,而不使用Flask-Uploads:

class UploadForm(FlaskForm):
    upload = FileField('image', validators=[
        FileRequired(),
        FileAllowed(['jpg', 'png'], 'Images only!')
    ])

2.3、reCAPTCHA(验证码)

Flask-WTF 通过RecaptchaField提供了reCAPTCHA:

from flask_wtf import FlaskForm, RecaptchaField
from wtforms import TextField

class SignupForm(FlaskForm):
	username = TextField('Username')
	recaptcha = RecaptchaField()

下方提供了一些配置,得实现它们:

RECAPTCHA_PUBLIC_KEYrequired A public key.
RECAPTCHA_PRIVATE_KEYrequired A private key.
RECAPTCHA_API_SERVERoptional Specify your Recaptcha API server.
RECAPTCHA_PARAMETERSoptional A dict of JavaScript (api.js) parameters.
RECAPTCHA_DATA_ATTRSoptional A dict of data attributes options. https://developers.google.com/recaptcha/docs/display

以下是RECAPTCHA_PARAMETERSRECAPTCHA_DATA_ATTRS的示例:

RECAPTCHA_PARAMETERS = {'hl': 'zh', 'render': 'explicit'}
RECAPTCHA_DATA_ATTRS = {'theme': 'dark'}

为了测试应用程序,假如app.testingTrue,recaptcha字段必须总是有效。
它可以在模板中很容易地设置:

<form action="/" method="post">
    {{ form.username }}
    {{ form.recaptcha }}
</form>

3、CSRF保护

任何使用FlaskForm来处理请求的视图都已经得到CSRF的保护。如果我们有不使用FlaskForm 或创建Ajax请求的视图,也可以使用所提供的CSRF扩展来保护这些请求的。

3.1、setup

若要对一个Flask应用程序全局提供CSRF保护,可注册CSRFProtect扩展。如下:

from flask_wtf.csrf import CSRFProject

csrf = CSRFProtect(app)

也可以像其他Flask扩展一样,这样使用:

csrf = CSRFProject()

def create_app():
	app = Flask(__name__)
	csrf.init_app(app)

注意:CSRF保护需要一个密钥来安全地签名token。默认情况下,它会使用Flask应用程序的SECRET_KEY。当然也可以单独地设置WTF_CSRF_SECRET_KEY

3.2、HTML Forms

当在使用一个FlaskForm时,可以像正常一样渲染表单的CSRF字段:

<form method="post">
	{{ form.csrf_token }}
</form>

如果模板没有使用FlaskForm,可以这样在表单中渲染一个带有token的隐藏输入:

<form method="post">
	<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>

3.3、JavaScript Requests

当发送一个AJAX请求时,可以添加X-CSRFToken header给它。例如,在jQuery中,可以配置所有请求去发送这个token:
<script type="text/javascript">
    var csrf_token = "{{ csrf_token() }}";

    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrf_token);
            }
        }
    });
</script>

3.4、自定义错误响应

当CSRF验证失败时,将引发CSRFError异常。默认情况下,会返回一个有失败原因和400状态码的响应。我们也可以使用Flask的errorhandler()来自定义错误响应:

from flask_wtf.csrf import CSRFError

@app.errorhandler(CSRFError)
def handle_csrf_error(e):
    return render_template('csrf_error.html', reason=e.description), 400

3.5、给某些视图取消CSRF保护

强烈建议给所有视图使用CSRF保护。但是,也可以使用装饰器排除一些视图:

@app.route('/foo', methods=('GET', 'POST'))
@csrf.exempt
def my_handler():
    # ...
    return 'ok'

也可以取消蓝图下的所有视图 保护:

csrf.exempt(account_blueprint)

也可以通过默认方式在所有视图里设置CSRF保护为不可用,即 WTF_CSRF_CHECK_DEFAULT=False。也可以在需要时,选择调用protect()。还可以在检查CSRF token前对请求进行一些预处理:

@app.before_request
def check_csrf():
    if not is_oauth(request):
        csrf.protect()

开发者API

在这里插入图片描述

参考文档:
1、官方
2、github 包含所有历史版本的源码
3、WTForms官网
4、WTForm github源码
5、官方实例
6、开发者接口----其实就是源码

附:WTForms 思维导图(其中最常用的)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值