[TOC]
Flask的优点是灵活小巧,三行代码即可运行一个web服务器,但基于Flask构建的功能并不比Django弱,关键就就是除了flask自带的基础功能外,还有丰富的组件进行支持,本文先对常用的自带组件进行简单的介绍。测试的Flask版本是0.12。
在构建Flask应用导入的时候,通常是from flask import Flask
的方式开始的,说明flask这个python package里面应该还藏了不少好东西,从源码来看看:
from werkzeug.exceptions import abort
from werkzeug.utils import redirect
from jinja2 import Markup, escape
from .app import Flask, Request, Response
from .config import Config
from .helpers import url_for, flash, send_file, send_from_directory, \
get_flashed_messages, get_template_attribute, make_response, safe_join, \
stream_with_context
from .globals import current_app, g, request, session, _request_ctx_stack, \
_app_ctx_stack
from .ctx import has_request_context, has_app_context, \
after_this_request, copy_current_request_context
from .blueprints import Blueprint
from .templating import render_template, render_template_string
# the signals
from .signals import signals_available, template_rendered, request_started, \
request_finished, got_request_exception, request_tearing_down, \
appcontext_tearing_down, appcontext_pushed, \
appcontext_popped, message_flashed, before_render_template
# We're not exposing the actual json module but a convenient wrapper around
# it.
from . import json
# This was the only thing that Flask used to export at one point and it had
# a more generic name.
jsonify = json.jsonify
# backwards compat, goes away in 1.0
from .sessions import SecureCookieSession as Session
json_available = True
Flask自带组件基本上都在这里了,我们一一来分析这些组件的功能及基本的用法。
Flask
Flask应用的主类,在源码分析中提到,不再赘述。这里给一个Flask应用的经典样例,请务必注意下面的代码都是在此样例基础上进行拓展的。
#示例-1
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
render_template:
def render_template(template_name_or_list, **context)
template_name_or_list参数是模板名称,context参数接受模板中已经定义的变量值。
Jinja是Flask的模板引擎,提供对HTML页面的数据渲染,render_template是Flask中调用template目录的html模板并渲染数据。代码示例:
#示例-2
from flask import render_template
@app.route("/")
def index():
name="He Kejun"
return render_template("index.html",name=name)
上述的代码中调用了/template/index.html
模板,并将“name: He Kejun”这一数据渲染进该模板其可以直接通过Hello, {{name}}
的方式显示‘Hello, He Kejun'
session
flask的session机制,Flask从服务器通过浏览器的cookie保存信息,该信息通过应用的秘钥进行加密确保数据安全。session在用户身份认证和数据共享提供较好的帮助。样例:
#示例-3
from flask import session
app.config["SECRET_KEY"]="YOU CAN NOT GUESS ME" #加密的秘钥,建议放在配置文件中
@app.route("/")
def index():
session["name"]="kejun"
return "hello,world"
@app.route("/name")
def name():
return session["name"]
首先访问http://127.0.0.1:5000 生成用户名,查看浏览器的cookie可以看到session已经写入了,这时候再访问http://127.0.0.1:5000/name就可以看到名字了,在两个HTTP请求之间共享了{name: kejun}
这个数据。
url_for
def url_for(endpoint, **values):
根据已经定义好的view函数来创建URL到指定的endpoint,Flask的endpoint可以理解为Restful框架中资源。输入的endpoint是函数名,values是关键字参数,每个关键字参数对应URL规则的变量部分,未知变量部分被插入到URL中作查询参数,具体的样例,我使用了官网的样例
#示例-4
from flask import url_for
@app.route("/")
def index(): pass
@app.route("/login")
def login(): pass
@app.route("/user/<username>")
def profile(username):pass
with app.test_request_context():
print(url_for("index)) #/
print(url_for("login")) #/login
print(url_for("login",next="/")) #/login?next=/
print(url_for("profile,username="kejun")) @/user/kejun
redirect
def redirect(location, code=302, Response=None):
参数location是跳转的目标位置,code是HTTP响应码默认是302(临时移动),也可以设置为301(永久移动,即所请求的文档在别处),303(参见其他信息),305(使用代理),不可以是300(多重选择),304(未修正更新)。
redirect的作用是重定向,设置响应并跳转到指定的位置。在Flask中通常与url_for这个功能一起使用。应用实例:
#示例-5
from flask import redirect
@app.route("/")
@app.route("/index")
def index():
return redirect(url_for("blog"))
@app.route("/blog")
def blog(): pass
在示例-5中,我们可以将一些用户可能会访问的链接重定向到博客主页上。
flash
def flash(message, category='message'):
参数message是消息内容,category是消息类型,方便前端过滤。
Web设计中提供用户的友好性是及时将反馈提供给用户。消息闪现Flash提供了一个非常简单的机制。
#示例-6-1
from flask import flash
def print_message():
flash("This is a message","message")
return "Message Page"
def print_error()
flash("This is a error","error")
return "Error Page"
在前端,我们可以这样定义HTML页面从而显示内容
#示例-6-2
{% with msgs = get_flashed_messages(category_filter=["message"]) %}
{% if msgs %}
{% for msg in msgs %}
<p>{{msg}}</p>
{% endfor %}
{% endif %}
{% endwith %}
{% with errors= get_flashed_messages(category_filter=["error"]) %}
{% iferrors%}
{% for errorinerrors%}
<p>{{error}}</p>
{% endfor %}
{% endif %}
{% endwith %}
make_response
def make_response(*args):
Flask会自动将view函数返回的转换成响应对象,除此之外,你可以通过调用make_response
#示例-7
from flask import make_response
def index():
return render_template('index.html', foo=42)
def index():
response = make_response(render_template('index.html', foo=42))
resp.set_cookie("am_i_handsome","yes") #设置cookie
return response
在上述的示例中,通过make_response,还可以定制cookie。也就是说,通过make_response你可以个性化定制返回的响应对象。
jsonify
对于数据进行json转化,应用于通过ajax方式访问时需要返回json数据。
#示例-8
from flask importjsonify
@app.route("/update_user_name",methods=['GET', 'POST'])
def update_user_name():
ip=request.headers.get('X-Real-Ip', request.remote_addr)
user_name=request.args.get('new_user_name',"",type=str)
if user_name:
if set_user_name(ip,user_name):
return jsonify(return_code="1")
return jsonify(return_code="0")
blueprint
blueprint蓝图是flask自带的一种拓展已有应用结构的方式,这是Flask在中大型Web应用中的一种框架设计。蓝图把把功能类似或者同一模块下的视图函数组合在一起,基于蓝图,我们可以把Flask应用拆分成不同的组件。每个蓝图都可以自定义自己的模板文件目录和静态目录。定义好的蓝图目录可以通过注册的方式加入Flask应用中。代码示例如下:
#示例-9-1
from flask import Blueprint
example=Blueprint('example',__name__,template_folder='templates/example',static_folder='static/example',url_prefix='/example')
#蓝图example下的视图函数
@example.route
def index():
return render_template("index.html")
一个蓝图就是一个模块,上述的example蓝图创建好后,需要注册到Flask应用中,样例代码如下:
示例-9-2
from flask import Flask
from yourapplication.examples import example
app = Flask(__name__)
app.register_blueprint(example)
本文之前介绍了url_for的使用方法,在蓝图中使用url_for定位视图函数需要增加蓝图名称,如定位到上述index视图函数url_for('example.index')
request
Flask构建了一个与请求相关的全局变量,在所有的视图函数及模板中都可以访问该对象。应用示例如下:
#示例-10
from flask import request
def get_user_ip():
ip=request.headers.get('X-Real-Ip', request.remote_addr)
return "Request ip address is {0}".format(ip)
abort
abort是Flask中Abort类的一个实例,通常采用abort(error_code)
的方式进行调用。abort的状态码最好是自己实现的错误定义,如示例-11所示。
g
Flask中的全局变量g,可以为特定请求临时存储任何需要的数据并且是线程安全的,当请求结束时,这个对象会被销毁,下一个新的请求到来时又会产升一个新的g。
#示例-11
from flask import g,session,abort,render_template
@app.before_request
def before_request():
if 'user_name' in session:
g.user=User.query.get(session['user_name'])
@app.route('/admin')
def admin():
if g.user is None:
abort(404)
return render_template('admin.html')
@app.errorhandler(404):
def page_not_found():
return render_template("404.html"),404
send_from_directory
def send_from_directory(directory, filename, **options):
参数directory是文件所在目录,filename是文件名称,options参数是send_file的传入参数,options可选的参数包括 mimetype、as_attachment、attachment_filename、add_etags、cache_timeout、conditional。
send_from_directory可以认为send_file上加了一层壳子,判断了文件路径及文件名,然后交由send_file处理,实际上将文件内容发给浏览器,所以它一个重要的应用场景是支持文件下载。
对于文件下载,媒体数据流传输的可以再研究一下send_file
和stream_with_context
这两个功能,也非常好用。
示例代码为:
#示例-11
from flask import send_from_directory
@app.route('/getfile', methods=['GET'])
def download_file():
return send_from_directory(r"D:\Workspace\flask_source", "aa.txt", as_attachment=True)
Markup
class Markup(text_type):
将输入文本text_type标记为安全,不需要进行转移。
如果将HTML作为变量通过Jinjia模板的方式作为变量插入页面中,Flask会自动尝试对HTML进行转义。这是必要的安全措施,确保恶意用户通过提交一段恶意代码并显示在页面中,从而让其他用户的浏览器进行执行。Flask同时提供两种方法来对安全的HTML不转义直接显示:一个是利用Markup类,二是利用Jinjia关键字safe
。示例代码为:
示例-12
from flask import Markup
@app.route("/")
def index():
return Markup('Hello %s!') % '<strong>kejun</strong>'#显示strong标签
@app.route("/1")
def index1():
return 'Hello %s!' % '<strong>kejun</strong>'#将strong标签转移成显示
current_app
应用上下文对象,官方文档有详细的介绍,