标签:
虽然Flask的请求对象给表单处理提供了足够的支持,但也有一些任务繁琐和重复。比如为表单生成HTML代码和验证提交表单数据。
跨站请求伪造(Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。中文维基百科参考, 英文维基百科参考。
配置加密的key可以防止CSRF,如下:
app = Flask(__name__) app.config[‘SECRET_KEY‘] = ‘hard to guess string‘
app.config字典是框架内,扩展或应用程序本身存储配置变量通用的地方,还有方法支持从文件或者环境变量导入。
SECRET_KEY是Flask和一些第三方扩展通用密钥。安全性处决于你的密钥,确认每个应用程序都有不同的安全密钥。更安全起见,密钥要写在环境变量,而不是代码中。
Flask-WTF中每个表单用继承自Form的类表示,类中定义表单的HTML对象列表。每个HTML对象用对象表示,可以有一个或多个验证。验证是检查用户输入的有效性。
from flask.ext.wtf import Form from wtforms import StringField, SubmitField from wtforms.validators import Required class NameForm(Form): name = StringField(‘What is your name?‘, validators=[Required()]) submit = SubmitField(‘Submit‘)
WTForms支持的标准HTML对象如下:
Field type | Description |
StringField | Text Field |
TextAreaField | Multiple-line text Field |
PasswordField | Password text Field |
HiddenField | Hidden text Field |
DateField | Text Field that accepts a datetime.date value in a given format |
DateTimeField | Text Field that accepts a datetime.datetime value in a given format |
IntegerField | Text Field that accepts an integer value |
DecimalField | Text Field that accepts a decimal.Decimal value |
FloatField | Text Field that accepts a floating-point value |
BooleanField | Checkbox with True and False values |
RadioField | List of radio buttons |
SelectField | Drop-down list of choices |
SelectMultipleField | Drop-down list of choices with multiple selection |
FileField | File upload Field |
SubmitField | Form submission button |
FormField | Embed a form as a Field in a container form |
FieldList | List of Fields of a given type |
WTForm内置的验证如下:
Validator | Description |
Validates an email address | |
EqualTo | Compares the values of two fields; useful when requesting a password to be entered twice for confirmation |
IPAddress | Validates an IPv4 network address |
Length | Validates the length of the string entered |
NumberRange | Validates that the value entered is within a numeric range |
Optional | Allows empty input on the field, skipping additional Validators |
Required | Validates that the field contains data |
Regexp | Validates the input against a regular expression |
URL | Validates a URL |
AnyOf | Validates that the input is one of a list of possible values |
NoneOf | Validates that the input is none of a list of possible values |
渲染简单的实例:
<form method="POST"> {{ form.name.label }} {{ form.name() }} {{ form.submit() }}</form>
增加属性:
<form method="POST"> {{ form.name.label }} {{ form.name(id=‘my-text-field‘) }} {{ form.submit() }}</form>
使用Flask-Bootstrap,上述渲染可以简化:
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky{% endblock %} {% block page_content %} <div class="page-header"> <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> </div> {{ wtf.quick_form(form) }} {% endblock %}
hello.py修改成如下:
from flask import Flask, render_template from flask.ext.script import Manager from flask.ext.bootstrap import Bootstrap from flask.ext.moment import Moment from flask.ext.wtf import Form from wtforms import StringField, SubmitField from wtforms.validators import Required app = Flask(__name__) app.config[‘SECRET_KEY‘] = ‘hard to guess string‘ manager = Manager(app) bootstrap = Bootstrap(app) moment = Moment(app) class NameForm(Form): name = StringField(‘What is your name?‘, validators=[Required()]) submit = SubmitField(‘Submit‘) @app.errorhandler(404) def page_not_found(e): return render_template(‘404.html‘), 404 @app.errorhandler(500) def internal_server_error(e): return render_template(‘500.html‘), 500 @app.route(‘/‘, methods=[‘GET‘, ‘POST‘]) def index(): name = None form = NameForm() if form.validate_on_submit(): name = form.name.data form.name.data = ‘‘ return render_template(‘index.html‘, form=form, name=name) if __name__ == ‘__main__‘: manager.run()
上面指定了方法methods=[‘GET‘, ‘POST‘],如果没有指定,默认使用GET。表单通常用POST来处理。POST时validate_on_submit()调用validate_on_submit()
上例只要一刷新,就会忘记你曾经输入的用户名,为此我们增加session,并使用Post/Redirect/Get模式,修改hello.py:
from flask import Flask, render_template, session, redirect, url_forfrom flask.ext.script import Managerfrom flask.ext.bootstrap import Bootstrapfrom flask.ext.moment import Momentfrom flask.ext.wtf import Formfrom wtforms import StringField, SubmitFieldfrom wtforms.validators import Required app = Flask(__name__)app.config[‘SECRET_KEY‘] = ‘hard to guess string‘manager = Manager(app)bootstrap = Bootstrap(app)moment = Moment(app)class NameForm(Form): name = StringField(‘What is your name?‘, validators=[Required()]) submit = SubmitField(‘Submit‘)@app.errorhandler(404)def page_not_found(e): return render_template(‘404.html‘), 404@app.errorhandler(500)def internal_server_error(e): return render_template(‘500.html‘), 500@app.route(‘/‘, methods=[‘GET‘, ‘POST‘])def index(): form = NameForm() if form.validate_on_submit(): session[‘name‘] = form.name.data return redirect(url_for(‘index‘)) return render_template(‘index.html‘, form=form, name=session.get(‘name‘))if __name__ == ‘__main__‘: manager.run()
下面我们在用户名改变的时候增加提示信息。
from flask import Flask, render_template, session, redirect, url_for, flashfrom flask.ext.script import Managerfrom flask.ext.bootstrap import Bootstrapfrom flask.ext.moment import Momentfrom flask.ext.wtf import Formfrom wtforms import StringField, SubmitFieldfrom wtforms.validators import Required app = Flask(__name__)app.config[‘SECRET_KEY‘] = ‘hard to guess string‘manager = Manager(app)bootstrap = Bootstrap(app)moment = Moment(app)class NameForm(Form): name = StringField(‘What is your name?‘, validators=[Required()]) submit = SubmitField(‘Submit‘)@app.errorhandler(404)def page_not_found(e): return render_template(‘404.html‘), 404@app.errorhandler(500)def internal_server_error(e): return render_template(‘500.html‘), 500@app.route(‘/‘, methods=[‘GET‘, ‘POST‘])def index(): form = NameForm() if form.validate_on_submit(): old_name = session.get(‘name‘) if old_name is not None and old_name != form.name.data: flash(‘Looks like you have changed your name!‘) session[‘name‘] = form.name.data return redirect(url_for(‘index‘)) return render_template(‘index.html‘, form=form, name=session.get(‘name‘))if __name__ == ‘__main__‘: manager.run()
templates/base.html修改如下:
{% extends "bootstrap/base.html" %}{% block title %}Flasky{% endblock %}{% block head %}{{ super() }}<link rel="shortcut icon" href="{{ url_for(‘static‘, filename=‘favicon.ico‘) }}" type="image/x-icon"><link rel="icon" href="{{ url_for(‘static‘, filename=‘favicon.ico‘) }}" type="image/x-icon">{% endblock %}{% block navbar %}<div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Flasky</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div></div>{% endblock %}{% block content %}<div class="container"> {% for message in get_flashed_messages() %} <div class="alert alert-warning"> <button type="button" class="close" data-dismiss="alert">×</button> {{ message }} </div> {% endfor %} {% block page_content %}{% endblock %}</div>{% endblock %}{% block scripts %}{{ super() }}{{ moment.include_moment() }}{% endblock %}
标签:
原文地址:http://my.oschina.net/u/1433482/blog/464054