一个应用程序大部分的时间都是在维护阶段,所以在实现功能的情况下,对于代码的可维护性提出了更高的要求,而对于可维护性来说,采用mvc架构,将视图与逻辑分离就是一个基本的要求了,对于flask来讲,使用的是jinja2模板引擎
基本用法
flask的繁多配置都有一个默认值,对于模板来讲,他会到templates文件夹中寻找,所以,首先在文件根目录创建一个名为templates的文件夹,并创建index.html文件,现在目录结构如下:
修改default.py中的部分代码为:
from flask import render_template #页头,导入函数
@app.route("/")
def index():
return render_template("index.html",site_name='myblog')
index.html中的代码为:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{site_name}}</title>
</head>
<body>
<h1>这个站点的名字为 {{site_name}} </h1>
</body>
</html>
flask通过render_template函数使用模板,其中函数的第一个参数为模板名,之后可以提供若干参数,均为键值对,为模板中的变量提供数据。如此例子中,为site_name提供了myblog的值,而模板内使用{{参数名}}来表示一个变量
此时浏览器输入地址输出结果为:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>myblog</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>
jinja2模板还提供了一些变量过滤器,如代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{site_name|upper}}</title>
</head>
<body>
<h1>这个站点的名字为 {{site_name}} </h1>
</body>
</html>
这时输出为:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MYBLOG</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>
常用过滤器如下
safe 不转义
capitalize 首字母大写
lower 转换为小写
upper 转换为大写
trim 去收尾空格
striptages 去除html标签
除此之外,Jinja2的变量还可以是一些复杂类型,甚至可以使用一些复杂类型的常用方法,如:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{site_name[2:]}}</title>
</head>
<body>
<h1>这个站点的名字为 {{site_name}} </h1>
</body>
</html>
这时输出为:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>blog</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>
控制语句
控制语句是一个模板的基本功能,同样的jinja2也提供了相应的功能:
选择
{% if name=='test' %}
这是测试
{% else %}
{{name}},你好
{% endif %}
循环
<ul>
{% for blog in blogs%}
{{ blog.title }}
{% endfor%}
</ul>
除了这些基本用法,模板还为一些代码的重复使用提供了宏的功能,如将如下代码写入macros.html文件中
{% macro render_title(blog)%}
<li>{{blog.title}}</li>
{% endmacro%}
然后在之前的模板中:
{% import 'macros.html' as macros %}
<ul>
{% for blog in blogs %}
{{ macros.render_title(blog) }}
{% endfor %}
</ul>
执行结果与之前的完全相同
Jinja2还提供了一个更为强大的功能,即模板集成,这个个人感觉有点像java的sitemesh框架,它首先需要创建一个base.html的基模板:
<!DOCTYPE html>
<html>
<head>
{% block head %}
<meta charset="UTF-8">
<title>{% block title%} {% endblock %} - 牛博客</title>
<script src="http://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
其中block标签的意思表示在子模板中可以修改,具体到此例子,则可修改的部分为 head,title,body。下面为子模板代码:
{% extends "base.html" %}
{% block title %}{{site_name[2:]}}{% endblock %}
{% block head %}
{{super()}}
{% endblock %}
{% block body %}
<h1>这个站点的名字为 {{site_name}} </h1>
{% endblock %}
此时执行结果仍为:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>blog</title>
</head>
<body>
<h1>这个站点的名字为 myblog </h1>
</body>
</html>
好了,现在基础内容已经完成,是时候建立一个比较实际的页面了,新建login.html模板
现在大体了解了Jinja2模板引擎的用法,但是即使有了模板引擎,我相信对于站点最基础的用于新增修改的表单也是很多人最苦恼的部分之一,而flask的flask-wtf扩展则将表单处理进行了丰富的简化。
首先当然要安装flask-wtf扩展,同样非常简单,命令为:
pip3.6 install flask-wtf
现在,假定我们需要一个登陆页面,页面中包含一个用户名的文本域和一个密码域,则创建表单对象:
from flask.ext.wtf import Form
from wtforms import StringField,PasswordField,SubmitField
from wtforms.validators import Required
class LoginForm(Form): #表示此LoginForm类继承于Form类
username=StringField("请输入用户名",validators=[Required])
password=PasswordField("请输入密码")
submit=SubmitField("登录")
注意这里出现了中文,所以需要在文件的第一行输入
# coding=utf-8
如此设置编码后,才可在此py文件中使用中文
同时创建模板文件login.html:
{% extends "base.html" %}
{% block title %}{{site_name[2:]}}{% endblock %}
{% block head %}
{{super()}}
{% endblock %}
{% block body %}
<form method="post">
{{form.hidden_tag()}}
{{form.username.label}} {{form.username()}}
{{form.password.label}} {{form.password()}}
{{form.submit()}}
</form>
{% if username%}
您输入的用户名为 {{username}}<Br>
您输入的密码为 {{password}}<Br>
{% endif %}
{% endblock %}
然后创建登录页的路由控制函数:
@app.route("/login",methods=["GET","POST"])
def login():
username=None;
password=None;
form=LoginForm()
if form.validate_on_submit():
username=form.username.data;
password=form.password.data;
return render_template("login.html",form=form,username=username,password=password)
好了,执行命令运行,并在地址栏输入/login的url,结果为:
阿欧,结果好像和预想的不一致,出现了错误,报错信息显示为"使用CSRF必须有一个秘钥"好了现在解释一下什么是CSRF:
CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性.-----摘自百度百科
简单说,就是A通过B的某些信息,如地址,cookie等,模拟一个表单进行提交,进行攻击
好了,我们了解了CSRF已经他的危害,那么这个问题如何解决呢?对于flask-wtf来说,非常的简单,设置一个秘钥即可,每次在form中包含秘密信息,进行用户cookie等之外的额外验证,在程序上需进行如下设置:
app=Flask(__name__)
app.config["SECRET_KEY"]="Niu_blog"
其中app.config是一个字典,可用来存储一些程序本身的变量,如db配置,log配置等
app.config['SECRET_KEY']为通用秘钥。
注意出于安全性,此秘钥一般不会写在硬编码中,要么写在一个config的文件中,要么设置在系统的环境变量里。
配置完成后,再次请求服务器,结果为:
此时生成的源代码为(注意csrf部分):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>blog - 牛博客</title>
<script src="http://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<form method="post">
<input id="csrf_token" name="csrf_token" type="hidden" value="IjQ0MmVlMTMyOWMzMDgyNDM0NGE4OGZiODg2MmMxNjI0ZmQxYjQ1YzQi.C2PJ8A.UmKkAGwKvACzM6qNbH0bN5bbn2M">
<label for="username">请输入用户名</label> <input id="username" name="username" type="text" value="">
<label for="password">请输入密码</label> <input id="password" name="password" type="password" value="">
<input id="submit" name="submit" type="submit" value="登录">
</form>
</body>
</html>
输入用户名密码后显示为: