假定你已经安装好了 Flask。若是没有,请跳转到 安装 章节。css
一个最小的 Flask 应用看起来会是这样:html
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
把它保存为 hello.py (或是相似的),而后用 Python 解释器来运行。 确保你的应用文件名不是 flask.py ,由于这将与 Flask 自己冲突。python
$ python hello.py
* Running on http://127.0.0.1:5000/
如今访问 http://127.0.0.1:5000/ ,你会看见 Hello World 问候。git
那么,这段代码作了什么?github
欲关闭服务器,按 Ctrl+C。web
外部可访问的服务器sql
若是你运行了这个服务器,你会发现它只能从你本身的计算机上访问,网络中其它任何的地方都不能访问。在调试模式下,用户能够在你的计算机上执行任意 Python 代码。所以,这个行为是默认的。shell
若是你禁用了 debug 或信任你所在网络的用户,你能够简单修改调用 run() 的方法使你的服务器公开可用,以下:flask
app.run(host='0.0.0.0')
这会让操做系统监听全部公网 IP。api
虽然 run() 方法适用于启动本地的开发服务器,可是你每次修改代码后都要手动重启它。这样并不够优雅,并且 Flask 能够作到更好。若是你启用了调试支持,服务器会在代码修改后自动从新载入,并在发生错误时提供一个至关有用的调试器。
有两种途径来启用调试模式。一种是直接在应用对象上设置:
app.debug = True app.run()
另外一种是做为 run 方法的一个参数传入:
app.run(debug=True)
两种方法的效果彻底相同。
注意
尽管交互式调试器在容许 fork 的环境中没法正常使用(也即在生产服务器上正常使用几乎是不可能的),但它依然容许执行任意代码。这使它成为一个巨大的安全隐患,所以它 绝对不能用于生产环境 。
运行中的调试器截图:
想用其它的调试器? 参见 调试器操做 。
现代 Web 应用的 URL 十分优雅,易于人们辨识记忆,这一点对于那些面向使用低速网络链接移动设备访问的应用特别有用。若是能够不访问索引页,而是直接访问想要的那个页面,他们多半会笑逐颜开而再度光顾。
如上所见, route() 装饰器把一个函数绑定到对应的 URL 上。
这里是一些基本的例子:
@app.route('/') def index(): return 'Index Page' @app.route('/hello') def hello(): return 'Hello World'
可是,不只如此!你能够构造含有动态部分的 URL,也能够在一个函数上附着多个规则。
要给 URL 添加变量部分,你能够把这些特殊的字段标记为 <variable_name> , 这个部分将会做为命名参数传递到你的函数。规则能够用 <converter:variable_name> 指定一个可选的转换器。这里有一些不错的例子:
@app.route('/user/<username>') def show_user_profile(username): # show the user profile for that user return 'User %s' % username @app.route('/post/<int:post_id>') def show_post(post_id): # show the post with the given id, the id is an integer return 'Post %d' % post_id
转换器有下面几种:
int | 接受整数 |
float | 同 int ,可是接受浮点数 |
path | 和默认的类似,但也接受斜线 |
惟一 URL / 重定向行为
Flask 的 URL 规则基于 Werkzeug 的路由模块。这个模块背后的思想是基于 Apache 以及更早的 HTTP 服务器主张的先例,保证优雅且惟一的 URL。
以这两个规则为例:
@app.route('/projects/') def projects(): return 'The project page' @app.route('/about') def about(): return 'The about page'
虽然它们看起来着实类似,但它们结尾斜线的使用在 URL 定义 中不一样。 第一种状况中,指向 projects 的规范 URL 尾端有一个斜线。这种感受很像在文件系统中的文件夹。访问一个结尾不带斜线的 URL 会被 Flask 重定向到带斜线的规范 URL 去。
然而,第二种状况的 URL 结尾不带斜线,相似 UNIX-like 系统下的文件的路径名。访问结尾带斜线的 URL 会产生一个 404 “Not Found” 错误。
这个行为使得在遗忘尾斜线时,容许关联的 URL 接任工做,与 Apache 和其它的服务器的行为并没有二异。此外,也保证了 URL 的惟一,有助于避免搜索引擎索引同一个页面两次。
若是 Flask 能匹配 URL,那么 Flask 能够生成它们吗?固然能够。你能够用 url_for()来给指定的函数构造 URL。它接受函数名做为第一个参数,也接受对应 URL 规则的变量部分的命名参数。未知变量部分会添加到 URL 末尾做为查询参数。这里有一些例子:
>>> from flask import Flask, url_for >>> app = Flask(__name__) >>> @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') ... print url_for('login', next='/') ... print url_for('profile', username='John Doe') ... / /login /login?next=/ /user/John%20Doe
(这里也用到了 test_request_context() 方法,下面会解释。即便咱们正在经过 Python 的 shell 进行交互,它依然会告诉 Flask 要表现为正在处理一个请求。请看下面的解释。 环境局部变量 )
为何你要构建 URL 而非在模板中硬编码?这里有三个绝妙的理由:
HTTP (与 Web 应用会话的协议)有许多不一样的访问 URL 方法。默认状况下,路由只回应 GET 请求,可是经过 route() 装饰器传递 methods 参数能够改变这个行为。这里有一些例子:
@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': do_the_login() else: show_the_login_form()
若是存在 GET ,那么也会替你自动地添加 HEAD,无需干预。它会确保遵守 HTTP RFC(描述 HTTP 协议的文档)处理 HEAD 请求,因此你能够彻底忽略这部分的 HTTP 规范。一样,自从 Flask 0.6 起, 也实现了 OPTIONS 的自动处理。
你不知道一个 HTTP 方法是什么?没必要担忧,这里会简要介绍 HTTP 方法和它们为何重要:
HTTP 方法(也常常被叫作“谓词”)告知服务器,客户端想对请求的页面 作 些什么。下面的都是很是常见的方法:
有趣的是,在 HTML4 和 XHTML1 中,表单只能以 GET 和 POST 方法提交到服务器。可是 JavaScript 和将来的 HTML 标准容许你使用其它全部的方法。此外,HTTP 最近变得至关流行,浏览器再也不是惟一的 HTTP 客户端。好比,许多版本控制系统就在使用 HTTP。
动态 web 应用也会须要静态文件,一般是 CSS 和 JavaScript 文件。理想情况下, 你已经配置好 Web 服务器来提供静态文件,可是在开发中,Flask 也能够作到。 只要在你的包中或是模块的所在目录中建立一个名为 static 的文件夹,在应用中使用 /static 便可访问。
给静态文件生成 URL ,使用特殊的 'static' 端点名:
url_for('static', filename='style.css')
这个文件应该存储在文件系统上的 static/style.css 。
用 Python 生成 HTML 十分无趣,并且至关繁琐,由于你必须手动对 HTML 作转义来保证应用的安全。为此,Flask 配备了 Jinja2 模板引擎。
你可使用 render_template() 方法来渲染模板。你须要作的一切就是将模板名和你想做为关键字的参数传入模板的变量。这里有一个展现如何渲染模板的简例:
from flask import render_template @app.route('/hello/') @app.route('/hello/<name>') def hello(name=None): return render_template('hello.html', name=name)
Flask 会在 templates 文件夹里寻找模板。因此,若是你的应用是个模块,这个文件夹应该与模块同级;若是它是一个包,那么这个文件夹做为包的子目录:
状况 1: 模块:
/application.py
/templates
/hello.html
状况 2: 包:
/application
/__init__.py
/templates
/hello.html
关于模板,你能够发挥 Jinja2 模板的所有实例。更多信息请见 Jinja2 模板文档 。
这里有一个模板实例:
<!doctype html> <title>Hello from Flask</title> {% if name %} <h1>Hello {{ name }}!</h1> {% else %} <h1>Hello World!</h1> {% endif %}
在模板里,你也能够访问 request 、 session 和 g [1] 对象, 以及 get_flashed_messages() 函数。
模板继承让模板用起来至关顺手。如欲了解继承的工做机理,请跳转到 模板继承 模式的文档。最起码,模板继承能使特定元素 (好比页眉、导航栏和页脚)能够出如今全部的页面。
自动转义功能默认是开启的,因此若是 name 包含 HTML ,它将会被自动转义。若是你能信任一个变量,而且你知道它是安全的(例如一个模块把 Wiki 标记转换为 HTML),你能够用 Markup 类或 |safe 过滤器在模板中把它标记为安全的。在 Jinja 2 文档中,你会看到更多的例子。
这里是一个 Markup 类如何使用的简单介绍:
>>> from flask import Markup >>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>' Markup(u'<strong>Hello <blink>hacker</blink>!</strong>') >>> Markup.escape('<blink>hacker</blink>') Markup(u'<blink>hacker</blink>') >>> Markup('<em>Marked up</em> » HTML').striptags() u'Marked up \xbb HTML'
在 0.5 版更改: 自动转义再也不在全部模板中启用。下列扩展名的模板会触发自动转义:.html 、 .htm 、.xml 、 .xhtml 。从字符串加载的模板会禁用自动转义。
[1] | 不肯定 g 对象是什么?它容许你按需存储信息, 查看( g )对象的文档和 在 Flask 中使用 SQLite 3 的文档以获取更多信息。 |
对于 Web 应用,与客户端发送给服务器的数据交互相当重要。在 Flask 中由全局的 request 对象来提供这些信息。若是你有必定的 Python 经验,你会好奇,为何这个对象是全局的,为何 Flask 还能保证线程安全。答案是环境做用域:
内幕
若是你想理解其工做机制及如何利用环境局部变量实现自动化测试,请阅读此节,不然可跳过。
Flask 中的某些对象是全局对象,但却不是一般的那种。这些对象其实是特定环境的局部对象的代理。虽然很拗口,但实际上很容易理解。
想象一下处理线程的环境。一个请求传入,Web 服务器决定生成一个新线程( 或者别的什么东西,只要这个底层的对象能够胜任并发系统,而不只仅是线程)。 当 Flask 开始它内部的请求处理时,它认定当前线程是活动的环境,并绑定当前的应用和 WSGI 环境到那个环境上(线程)。它的实现很巧妙,能保证一个应用调用另外一个应用时不会出现问题。
因此,这对你来讲意味着什么?除非你要作相似单元测试的东西,不然你基本上能够彻底无视它。你会发现依赖于一段请求对象的代码,因没有请求对象没法正常运行。解决方案是,自行建立一个请求对象而且把它绑定到环境中。单元测试的最简单的解决方案是:用 test_request_context() 环境管理器。结合 with 声明,绑定一个测试请求,这样你才能与之交互。下面是一个例子:
from flask import request with app.test_request_context('/hello', method='POST'): # now you can do something with the request until the # end of the with block, such as basic assertions: assert request.path == '/hello' assert request.method == 'POST'
另外一种多是:传递整个 WSGI 环境给 request_context() 方法:
from flask import request with app.request_context(environ): assert request.method == 'POST'
API 章节对请求对象做了详尽阐述(参见 request ),所以这里不会赘述。此处宽泛介绍一些最经常使用的操做。首先从 flask 模块里导入它:
from flask import request
当前请求的 HTTP 方法可经过 method 属性来访问。经过:attr:~flask.request.form 属性来访问表单数据( POST 或 PUT 请求提交的数据)。这里有一个用到上面提到的那两个属性的完整实例:
@app.route('/login', methods=['POST', 'GET']) def login(): error = None if request.method == 'POST': if valid_login(request.form['username'], request.form['password']): return log_the_user_in(request.form['username']) else: error = 'Invalid username/password' # the code below is executed if the request method # was GET or the credentials were invalid return render_template('login.html', error=error)
当访问 form 属性中的不存在的键会发生什么?会抛出一个特殊的 KeyError 异常。你能够像捕获标准的 KeyError 同样来捕获它。 若是你不这么作,它会显示一个 HTTP 400 Bad Request 错误页面。因此,多数状况下你并不须要干预这个行为。
你能够经过 args 属性来访问 URL 中提交的参数 ( ?key=value ):
searchword = request.args.get('q', '')
咱们推荐用 get 来访问 URL 参数或捕获 KeyError ,由于用户可能会修改 URL,向他们展示一个 400 bad request 页面会影响用户体验。
欲获取请求对象的完整方法和属性清单,请参阅 request 的文档。
用 Flask 处理文件上传很简单。只要确保你没忘记在 HTML 表单中设置enctype="multipart/form-data" 属性,否则你的浏览器根本不会发送文件。
已上传的文件存储在内存或是文件系统中一个临时的位置。你能够经过请求对象的 files属性访问它们。每一个上传的文件都会存储在这个字典里。它表现近乎为一个标准的 Python file 对象,但它还有一个 save() 方法,这个方法容许你把文件保存到服务器的文件系统上。这里是一个用它保存文件的例子:
from flask import request @app.route('/upload', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': f = request.files['the_file'] f.save('/var/www/uploads/uploaded_file.txt') ...
若是你想知道上传前文件在客户端的文件名是什么,你能够访问 filename 属性。但请记住, 永远不要信任这个值,这个值是能够伪造的。若是你要把文件按客户端提供的文件名存储在服务器上,那么请把它传递给 Werkzeug 提供的 secure_filename() 函数:
from flask import request from werkzeug import secure_filename @app.route('/upload', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': f = request.files['the_file'] f.save('/var/www/uploads/' + secure_filename(f.filename)) ...
一些更好的例子,见 上传文件 模式。
你能够经过 cookies 属性来访问 Cookies,用响应对象的 set_cookie 方法来设置 Cookies。请求对象的 cookies 属性是一个内容为客户端提交的全部 Cookies 的字典。若是你想使用会话,请不要直接使用 Cookies,请参考 会话 一节。在 Flask 中,已经注意处理了一些 Cookies 安全细节。
读取 cookies:
from flask import request @app.route('/') def index(): username = request.cookies.get('username') # use cookies.get(key) instead of cookies[key] to not get a # KeyError if the cookie is missing.
存储 cookies:
from flask import make_response @app.route('/') def index(): resp = make_response(render_template(...)) resp.set_cookie('username', 'the username') return resp
可注意到的是,Cookies 是设置在响应对象上的。因为一般视图函数只是返回字符串,以后 Flask 将字符串转换为响应对象。若是你要显式地转换,你可使用 make_response()函数而后再进行修改。
有时候你想设置 Cookie,但响应对象不能醋在。这能够利用 延迟请求回调 模式实现。
为此,也能够阅读 关于响应 。
你能够用 redirect() 函数把用户重定向到其它地方。放弃请求并返回错误代码,用 abort() 函数。这里是一个它们如何使用的例子:
from flask import abort, redirect, url_for @app.route('/') def index(): return redirect(url_for('login')) @app.route('/login') def login(): abort(401) this_is_never_executed()
这是一个至关无心义的例子由于用户会从主页重定向到一个不能访问的页面 (401 意味着禁止访问),可是它展现了重定向是如何工做的。
默认状况下,错误代码会显示一个黑白的错误页面。若是你要定制错误页面, 可使用 errorhandler() 装饰器:
from flask import render_template @app.errorhandler(404) def page_not_found(error): return render_template('page_not_found.html'), 404
注意 render_template() 调用以后的 404 。这告诉 Flask,该页的错误代码是 404 ,即没有找到。默认为 200,也就是一切正常。
视图函数的返回值会被自动转换为一个响应对象。若是返回值是一个字符串, 它被转换为该字符串为主体的、状态码为 200 OK``的 、 MIME 类型是 ``text/html 的响应对象。Flask 把返回值转换为响应对象的逻辑是这样:
若是你想在视图里操纵上述步骤结果的响应对象,可使用 make_response() 函数。
譬如你有这样一个视图:
@app.errorhandler(404) def not_found(error): return render_template('error.html'), 404
你只须要把返回值表达式传递给 make_response() ,获取结果对象并修改,而后再返回它:
@app.errorhandler(404) def not_found(error): resp = make_response(render_template('error.html'), 404) resp.headers['X-Something'] = 'A value' return resp
除请求对象以外,还有一个 session 对象。它容许你在不一样请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,而且对 Cookies 进行密钥签名。这意味着用户能够查看你 Cookie 的内容,但却不能修改它,除非用户知道签名的密钥。
要使用会话,你须要设置一个密钥。这里介绍会话如何工做:
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 action="" 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'
这里提到的 escape() 能够在你模板引擎外作转义(如同本例)。
如何生成强壮的密钥
随机的问题在于很难判断什么是真随机。一个密钥应该足够随机。你的操做系统能够基于一个密钥随机生成器来生成漂亮的随机值,这个值能够用来作密钥:
>>> import os >>> os.urandom(24) '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
把这个值复制粘贴进你的代码中,你就有了密钥。
使用基于 cookie 的会话需注意: Flask 会将你放进会话对象的值序列化至 Cookies。若是你发现某些值在请求之间并无持久存在,然而确实已经启用了 Cookies,但也没有获得明确的错误信息。这时,请检查你的页面响应中的 Cookies 的大小,并与 Web 浏览器所支持的大小对比。
反馈,是良好的应用和用户界面的重要构成。若是用户得不到足够的反馈,他们极可能开始厌恶这个应用。 Flask 提供了消息闪现系统,能够简单地给用户反馈。 消息闪现系统一般会在请求结束时记录信息,并在下一个(且仅在下一个)请求中访问记录的信息。展示这些消息一般结合要模板布局。
使用 flash() 方法能够闪现一条消息。要操做消息自己,请使用get_flashed_messages() 函数,而且在模板中也可使用。完整的例子见 消息闪现 部分。
0.3 新版功能.
有时候你会处于这样一种境地,你处理的数据本应该是正确的,但实际上不是。 好比,你会有一些向服务器发送请求的客户端代码,但请求显然是畸形的。这多是用户篡改了数据,或是客户端代码的粗制滥造。大多数状况下,正常地返回 400 Bad Request 就能够了,可是有时候不能这么作,而且要让代码继续运行。
你可能依然想要记录下,是什么不对劲。这时日志记录就派上了用场。从 Flask 0.3 开始,Flask 就已经预置了日志系统。
这里有一些调用日志记录的例子:
app.logger.debug('A value for debugging') app.logger.warning('A warning occurred (%d apples)', 42) app.logger.error('An error occurred')
附带的 logger 是一个标准日志类 Logger ,因此更多信息请查阅 logging 的文档 。
若是你想给你的应用添加 WSGI 中间件,你能够封装内部 WSGI 应用。例如如果你想用 Werkzeug 包中的某个中间件来应付 lighttpd 中的 bugs ,能够这样作:
from werkzeug.contrib.fixers import LighttpdCGIRootFix app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
准备好部署你的 Flask 应用了?你能够当即部署到托管平台来圆满完成快速入门,如下厂商均向小项目提供免费的方案:
托管 Flask 应用的其它选择:
若是你有本身的主机,而且准备本身托管,参见 部署选择 章节。