如今你已经玩过一个基本的Flask应用程序,你也许想要知道更多关于Flask如何施展魔力。下面章节描述了一些框架设计方面的特色。python
当Flask从客户端收到一个请求,它须要提供几个可用对象给视图函数处理。request对象是个不错的例子,它封装了客户端发送的HTTP请求。git
Flask视图函数访问request对象的最好方式,就是做为一个参数发送它,但这须要每一个单一视图函数在应用程序中有一个额外的参数。考虑一下,若是request对象不是惟一一个视图函数须要访问完成请求的对象,事情将会变得更加复杂。github
为了不弄乱视图函数那些可能须要或不须要的参数,Flask使用context来临时肯定可访问的全局对象。也多亏了context,视图函数能够写成下面这样:web
from flask import request @app.route('/') def index(): user_agent = request.headers.get('User-Agent') return ' <p>Your browser is %s</p> ' % user_agent
注意,在这个视图函数中,request是如何被做为一个全局变量来使用的。现实中,request是不能做为全局变量的,若是是多线程服务器,同一时间线程做用于不一样客户端的不一样请求,因此每个线程须要看到request中的不一样对象。contexts使得Flask肯定可访问的全局变量而不干扰其余线程。shell
注:线程是能够独立管理的最小指令序列。一个进程中有多个活动的线程是很是常见的,有时分享内存或文件句柄资源。多线程web服务器会启动一个线程池并从池中选择一个线程来处理每一个传入的请求。数据库
Flask有两类context:应用级context 和 请求级context。表2-1展现了这些context提供的变量。flask
表2-1. Flask全局context浏览器
Flask激活(或压栈)应用级context和请求级context在调度请求以前,而后删除他们当请求被处理后。当应用程序context被压入栈,线程中current_app
和g
变量变得可用;一样的,当请求级context被压入栈,request
和session
变量也一样变得可用。若是这些变量中的任何一个不是由激活的应用级或请求级context访问,会产生错误。在后面的章节会详细讨论四个context变量,因此不要担忧你不理解它们的用处。服务器
下面的Python shell会话演示了应用级context是如何工做的:cookie
>>> from hello import app >>> from flask import current_app >>> current_app.name Traceback (most recent call last): ... RuntimeError: working outside of the application context >>> app_ctx = app.app_context() >>> app_ctx.push() >>> current_app.name 'hello' >>> app_ctx.pop()
在这个示例中,当应用级context没有激活,可是却做为有效的context被压入栈中,current_app.name
报错。注意在应用程序实例中一个应用级context是如何经过调用app.app_context()
来得到的。
当一个应用程序收到客户端的请求,它须要找到响应的视图函数为之服务。对于这个任务,Flask会在应用程序的URL映射中查找请求的URL,该映射包含URLs和操做它们的视图函数。Flask经过app.route
装饰器或非装饰器版本app.add_url_rule()
来创建这个映射。
看一下Flask应用程序中URL映射是怎样的,你能够在Python shell中检查hello.py
建立的映射。测试中,请确保你的虚拟环境是激活状态:
(venv) % python >>> from hello import app >>> app.url_map Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>, <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
/
和/user/<name>
路由是由应用程序中的app.route
所定义。/static/<filename>
路由是由Flask添加,用于访问静态文件的一个特殊路由。你将在第三章学习更多关于静态文件的内容。
URL映射中所示的HEAD
、OPTIONS
、GET
元素为request方法,由路由处理。Flask链接方法到每一个路由,这样不一样的请求方法发送到相同的URL能够被不一样的视图函数处理。HEAD
和OPTIONS
方法由Flask自动管理,因此实际上能够说,在这个应用程序中URL映射的三个路由都链接到GET
方法了。在第四章你将学习为路由指定不一样的请求方法。
有些时候在每一个请求处理以前或以后执行代码是很是有用的。例如,在开始每个请求前可能有必要建立数据库链接,或对用户请求进行验证。为了不复制处理这些操做的代码到每个视图函数中,Flask给你选择注册相同函数来调用,在请求被分配给视图函数以前或以后。
请求hooks由装饰器实现。下面是四个Flask支持的hooks:
before_first_request
:在第一个请求被处理前注册一个函数运行。before_request
:在每个请求前注册一个函数运行。after_request
:若是没有未处理的异常发生,在每个请求后注册一个函数运行。teardown_request
:即便未处理的异常发生,在每个请求后注册一个函数运行。在请求hook函数和视图函数之间共享数据的惯用方法就是使用g
全局context。例如,before_request
处理程序能够从数据库加载已登陆的用户并保存在g.user
中。以后,当视图函数被调用,能够从那访问用户。
请求hooks的示例会在将来的章节中展现给你们,因此不用担忧,
当Flask调用一个视图函数,并指望它的返回值去响应该请求。大多数的响应是将简单字符串构成的HTML页面发回给客户端。
可是HTTP协议须要比字符串更多的信息做为请求的响应。一个HTTP响应中很是重要的部分是状态码,Flask默认设置200来指示请求已经成功处理。
当视图函数须要用不一样的状态码响应,能够在响应文本后添加数字码做为第二个返回值。例如,下面的视图函数返回400错误状态码的请求:
@app.route('/') def index(): return '<h1>Bad Request</h1>', 400
视图函数返回的响应还能够携带第三个参数,添加一个头部字典给HTTP响应。一般不多用到,可是你能够在第十四章看到示例。
除了返回一个、两个或三个值的元组,Flask视图函数能够选择返回response对象。make_response()
函数可携带一个、两个或三个参数,和视图函数返回的值同样,并返回一个response对象。有时候在视图函数中执行这个转换是很是有用的,而后使用response对象中的方法进一步配置响应。下面的示例建立response对象并设置cookie:
from flask import make_response @app.route('/') def index(): response = make_response(' <h1>This document carries a cookie!</h1> ') response.set_cookie('answer', '42') return response
有一类特殊的响应称做重定向。这类响应不包含页面文档;只是给浏览器一个新的URL去加载新的页面。重定向一般和web表单一块儿使用,你将在第四章学习。
重定向一般由302响应状态码注明而且重定向的URL由头部的Location
给出。重定向响应可使用三个值的返回生成,也可经过响应对象生成,可是鉴于它频繁的使用,Flask提供redirect()
函数来建立这样的响应:
from flask import redirect @app.route('/') def index(): return redirect('http://www.example.com')
另外一个具备中断功能的特殊响应用来错误处理。下面的示例,当URL给出的id动态参数不是一个合法的用户时返回状态码404:
from flask import abort @app.route('/user/<id>') def get_user(id): user = load_user(id) if not user: abort(404) return ' <h1>Hello, %s</h1> ' % user.name
注意终止不是指将控制权返回给调用它的函数,而是指经过抛出异常将控制权返回给web服务。
Flask是可扩展的。它故意腾出地给重要的功能,例如数据库和用户受权,给你自由去选择最适合你的应用程序的包,或写一个本身想要的。
社区开发了很是多的扩展用于各类用途,若是这还不够,可使用任何Python标准包和库。为了让你了解一个扩展是如何并入一个应用程序的,下面的章节给hello.py添加一个扩展,增长应用程序的命令行参数。
Flask开发,其web服务器支持一系列的启动配置选项,可是配置它们的惟一方式只有在脚本中传递参数给app.run()
并调用。这不是很是的方便,理想方法是经过命令行参数传递配置选项。
Flask-Script是给你的Flask应用程序添加命令行解释的扩展。它打包了一组通用的选项,还支持自定义命令。
使用pip安装扩展:
(venv) $ pip install flask-script
示例2-3展现了在 hello.py 应用程序中添加命令行解释的变化。
示例2-3. hello.py:使用Flask-Script
from flask.ext.script import Manager manager = Manager(app) # ... if __name__ == '__main__': manager.run()
专为Flask开发的扩展暴露在flask.ext
命名空间下。Flask-Script从flask.ext.script
中导出一个名为Manager
的类。
初始化这个扩展的方法和其余许多扩展同样:主类实例的初始化是经过将应用程序实例做为参数传递给构造函数实现的。建立的对象适当的用于每个扩展。在这个示例中,服务器启动经过manager.run()
来路由,且命令行在这被解析。
建议:若是你有克隆在GitHub上的应用程序,你如今能够运行
git checkout 2c
来切换到这个版本的应用程序。
由于这些变化,应用程序得到一组基本的命令行选项。运行hello.py
显示可用信息:
$ python hello.py usage: hello.py [-h] {shell, runserver} ... positional arguments: {shell, runserver} shell 在Flask应用程序上下文的内部运行一个Python Shell。 runserver 运行Flask开发服务器,例如:app.run() optional arguments: -h, --help 显示这个帮助信息并退出
shell
命令用于在应用程序上下文中启动一个Python shell会话。你可使用这个会话去运行维护任务,或测试,或调试错误。
runserver
命令,就像它的名称同样,启动web服务。运行python hello.py runserver
在调试模式下启动web服务,还有更多的选项:
(venv) $ python hello.py runserver --help usage: hello.py runserver [-h] [-t HOST] [-p PORT] [--threaded] [--processes PROCESSES] [--passthrough-errors] [-d] [-r] 运行Flask开发服务器,例如:app.run() optional arguments: -h, --help 显示这个帮助信息并退出 -t HOST, --host HOST -p PORT, --port PORT --threaded --processes PROCESSES --passthrough-errors -d, --no-debug -r, --no-reload
--host
参数是一个很是有用的选项,由于它能告诉web服务器监听哪一个网络接口的客户端链接。默认,Flask开发的web服务器监听localhost
的链接,因此只有来自内部计算机运行的服务器能够接收。下面的命令使得web服务器监听公网接口,其余网络上的计算机能够链接:
(venv) $ python hello.py runserver --host 0.0.0.0 * Running on http://0.0.0.0:5000/ * Restarting with reload
如今web服务器应该能够从网络中的任何一台计算机访问 http://a.b.c.d:5000 ,“a.b.c.d”是运行服务的计算机的外部IP地址。
这一章介绍了请求响应的概念,但说的更多的是响应。Flask使用模板为生成响应提供很是好的支持,这是很是重要的话题,下一章会重点讲它。