1. 微框架
Flask是一套基于WSGI协议的Python Web开发微型框架,致力于保持核心框架的简洁与可扩展性,非核心功能模块拥有大量第三方实现,可由用户根据实际需要自行选择或自行开发实现。
2. 依赖库
Flask的核心框架依赖于Werkzeug和Jinjia2,Werkzeug负责HTTP请求和WSGI协议的相关处理,Jinjia2负责模板的解析和渲染工作。
2.1 Werkzeug
Werkzeug是一个被广泛使用的WSGI工具库,为Python Web开发提供一套开箱即用的工具集,包括:
- HTTP头部解析和复制
- 易于使用的request和resqonse对象
- 包含基于JS的客户端浏览器交互式调试工具
- 完全兼容WSGI 1.0协议
- 基本的session和signed cookie支持
- 支持Unicode的URL和URI工具集
- 内置库包含WSGI服务器和客户端浏览器的缺陷修复
- 内部集成了URL路由匹配功能
- 支持Unicode
Flask 的HTTP请求、HTTP响应、路由处理、session接口等部分均是基于Werkzeug库完成的。
2.2 Jinjia2
Jinjia2是一个基于Python开发的现代的、设计师友好型模板语言,包含如下特性:
- 沙箱执行
- 自动HTML转码,以防XSS攻击
- 支持模板继承
- 可实时编译为优化过的python源代码
- 可预编译模板
- 调试时可输出模板错误行行号
- 可配置模板语法
Flask 的内置模板处理模块是基于Jinjia2实现的。
3. 安装
3.1 全局安装
若使用全局安装,则直接执行以下命令即可:
1 |
$ sudo pip install Flask |
3.2 在虚拟环境中安装
若需要在虚拟环境中安装,请先确保已安装virtualenv:
1 |
$ sudo pip install virtualenv |
之后在指点位置新建工程目录,然后进入工程目录,安装虚拟环境:
1 2 3 |
$ mkdir myproject $ cd myproject $ virtualenv venv |
在使用虚拟环境前需要先激活:
1 |
$ . venv/bin/activate |
3.3 使用github最新源码安装
进入到指定代码存放目录,执行如下操作:
1 2 3 4 5 |
$ git clone http://github.com/pallets/flask.git $ cd flask $ virtualenv venv $ . venv/bin/activate $ python setup.py develop |
4. 运行
4.1 使用cli执行
在命令行中执行如下命令(若使用虚拟环境,请先激活虚拟环境):
1 2 |
$ export FLASK_APP=hello.py $ flask run |
其中hello.py为网站的入口文件,大致如下:
1 2 3 4 5 6 |
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return "Hello, World!" |
4.2 直接启动
直接启动的命令如下(若使用虚拟环境,请先激活虚拟环境):
1 |
python hello.py |
其中hello.py为网站的入口文件,大致如下:
1 2 3 4 5 6 7 8 9 |
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return "Hello, World!" if __name__ == '__main__': app.run() |
5. 路由
FLask中路由可以使用固定的url地址,也可以使用转换器,在url请求路径中包含参数。
固定url地址实例:
1 2 3 4 5 6 |
@app.route('/') def index(): return 'Index Page' @app.route('/hello') def hello(): return 'Hello, World!' |
包含地址参数的实例:
1 2 3 4 5 6 7 |
@app.route('/user/<username>') def show_user_profile(username): return 'User %s' % username @app.route('/post/<int:post_id>') def show_post(post_id): return 'Post %d' % post_id |
Flask中可用的地址参数转换器有:
数据类型 | 描述 |
---|---|
string | 匹配任何不带斜杠的路径字符串,该类型为路径参数缺省类型 |
int | 接受整型参数 |
float | 接受浮点型参数 |
path | 和string类型相似,但可以带斜杠 |
any | 接受所提供的参数类型中的任何一种 |
uuid | 接受UUID类型的字符串 |
参数书写格式:
1 |
<converter:variable_name> |
当变量为string类型时,可以不指定变量类型。
此外,在设置入口处理函数的路由路径时还可以指定HTTP请求方法,如下:
1 2 3 4 5 6 7 8 |
from flask import request @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': do_the_login() else: show_the_login_form() |
6. 模板
Flask使用Jinjia2作为模板引擎,实际使用时可以使用其它模板引擎,但必须安装Jinja2。使用过程如下:
1 2 3 4 5 |
from flask import render_template @app.route('/hello/') @app.route('/hello/<name>') def hello(name=None): return render_template('hello.html', name=name) |
其中hello.html为模板定义文件,其内容大致如下:
1 2 3 4 5 6 7 |
<!doctype html> <title>Hello from Flask</title> {% if name %} <h1>Hello {{ name }}!</h1> {% else %} <h1>Hello, World!</h1> {% endif %} |
在模板文件中,除了可以使用Jinja2语法外,还可以使用Flask中的全局变量和函数:config、request、session、g、url_for()和get_flashed_messages()等。
7. 访问Request数据
Flask中的request全局对象用于处理来自客户端的请求数据,其使用过程如下:
1 2 3 4 5 6 7 8 9 10 |
from flask import request @app.route('/login', methods=[POST, GET]) def login(): assert request.path == '/login' assert request.method == 'POST' f = request.files['a_upload_file'] username = request.args.get('username', 'defalut_value') username = request.cookies.get('username') username = request.form['username'] |
Request对象包含的常用属性或方法有:
属性/方法名称 | 说明 |
---|---|
form | 客户端提交的Form表单数据字典 |
args | 请求URL中的查询字符串字典 |
values | form和args的合集 |
cookies | 请求中的cookies字典 |
headers | 请求中的HTTP头信息字典 |
files | 请求中的上传文件对象字典 |
get_data() | 获取缓存的请求体数据 |
get_json() | 获取请求体中的json对应的字典 |
endpoint | 返回处理当前请求的endpoint对象 |
method | 返回HTTP请求的方法名称,如:GET/POST |
host | 返回请求中的主机域名 |
path | 返回请求的URL中的路径部分 |
query_string | 返回请求的URL中的查询字符串部分 |
full_path | 返回完整的请求URL |
remote_addr | 返回客户端的IP地址 |
8. 错误处理与重定向
Flask提供
redirect() 函数来处理服务器端重定向操作,可结合
url_for() 函数获取指定入口点的完整url访问路径。
Flask具有完备的错误处理机制,可以通过调用
abort() 函数终止执行,抛出异常代码;也可以直接使用raise excption抛出异常;异常的处理函数也可以通过
@app.errorhandler() 装饰器指定;Flask对未处理的异常提供了默认实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from flask import abort, redirect, url_for, render_template @app.route('/') def index(): return redirect(url_for('login')) @app.route('/login') def login(): abort(404) this_is_never_executed() @app.errorhandler(404) def page_not_found(error): return render_template(page_not_found.html), 404 |
9. 设置Response数据
Flask中,视图函数的返回值将被自动转换为response对象,并设定相应的HTTP响应代码和mimetype类型,其转换规则如下:
- 如果视图函数返回的是response对象,则无需任何转换;
- 如果是图函数返回值为string,则将string转换为response对象的内容,并将response对象的状态码设定为
200 OK
,mimetype类型设定为text/html
; - 如果返回值为一个tuple,则将按照
(response, status, headers)
的次序尝试解析返回值,若有缺省,则会尝试使用默认值填充缺省部分
以下为错误处理时response对象创建和修改过程:
1 2 3 4 5 6 |
@app.errorhandler(404) def not_found(error): resp = make_response(render_template('error.html'), 404) resp.headers['X-Something'] = 'A value' # ... return resp |
10. 使用Session
在Flask中,默认Session对象是基于客户端Cookie实现的:Session中存储的信息通过服务器端加密后传输到客户端,并在客户端以Cookie的形式存储,每次客户端访问服务器时均携带上述存储了Session信息的Cookie,服务器端收到该Cookie时再解密,获取原始Session数据。
在使用Session时,不必关注Session的底层实现,只需按照常规的使用方式使用即可。以下是使用Session记录用户登录信息的处理示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) @app.route('/') def index(): if 'username' in session: return 'Logged in as %s' % escape(session['username']) return 'You are not logged in' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return ''' <form method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> ''' @app.route('/logout') def logout(): # remove the username from the session if it s there session.pop('username', None) return redirect(url_for('index')) # set the secret key. keep this really secret: app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' |
11. 记录日志
Flask封装了Python标准库中的logging
库,可在多线程多进程环境中安全记录日志。
1 2 3 |
app.logger.debug('A value for debugging') app.logger.warning('A warning occurred (%d apples)', 42) app.logger.error('An error occurred') |
12. 代码工程结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
projectName manage.py appName __init__.py templates static views __init__.py errors.py login.py and_so_on.py migrations tests venv requirements.txt config.py README.md |