### 导航
- [索引](# "总目录")
- [下一页](# "用蓝图实现模块化的应用") |
- [上一页](# "应用上下文") |
- [Flask 0.10.1 文档](#) »
# 请求上下文
这部分文档描述了在 Flask 0.7 中的行为,与旧的行为基本一致,但有细小微妙的差异。
这里推荐先阅读 [*应用上下文*](#) 章节。
### 深入上下文作用域
比如说你有一个应用函数返回用户应该跳转到的 URL 。想象它总是会跳转到 URL的 next 参数,或 HTTP referrer ,或索引页:
~~~
from flask import request, url_for
def redirect_url():
return request.args.get('next') or \
request.referrer or \
url_for('index')
~~~
如你所见,它访问了请求对象。当你试图在纯 Python shell 中运行这段代码时,你会看见这样的异常:
~~~
>>> redirect_url()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'request'
~~~
这有很大意义,因为我们当前并没有可以访问的请求。所以我们需要制造一个请求并且绑定到当前的上下文。 [test_request_context](# "flask.Flask.test_request_context") 方法为我们创建一个 [RequestContext](# "flask.ctx.RequestContext"):
~~~
>>> ctx = app.test_request_context('/?next=http://example.com/')
~~~
可以通过两种方式利用这个上下文:使用 with 声明或是调用[push()](# "flask.ctx.RequestContext.push") 和[pop()](# "flask.ctx.RequestContext.pop") 方法:
~~~
>>> ctx.push()
~~~
从这点开始,你可以使用请求对象:
~~~
>>> redirect_url()
u'http://example.com/'
~~~
直到你调用 pop:
~~~
>>> ctx.pop()
~~~
因为请求上下文在内部作为一个栈来维护,所以你可以多次压栈出栈。这在实现内部重定向之类的东西时很方便。
更多如何从交互式 Python shell 中利用请求上下文的信息,请见 [*与 Shell 共舞*](#)章节。
### 上下文如何工作
如果你研究 Flask WSGI 应用内部如何工作,你会找到和这非常相似的一段代码:
~~~
def wsgi_app(self, environ):
with self.request_context(environ):
try:
response = self.full_dispatch_request()
except Exception, e:
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
~~~
request_context() 方法返回一个新的[RequestContext](# "flask.ctx.RequestContext") 对象,并结合 with 声明来绑定上下文。从相同线程中被调用的一切,直到 with 声明结束前,都可以访问全局的请求变量( [flask.request](# "flask.request") 和其它)。
请求上下文内部工作如同一个栈。栈顶是当前活动的请求。[push()](# "flask.ctx.RequestContext.push") 把上下文添加到栈顶,[pop()](# "flask.ctx.RequestContext.pop") 把它移出栈。在出栈时,应用的[teardown_request()](# "flask.Flask.teardown_request") 函数也会被执行。
另一件需要注意的事是,请求上下文被压入栈时,并且没有当前应用的应用上下文,它会自动创建一个 [*应用上下文*](#) 。
### 回调和错误
在 Flask 中,请求处理时发生一个错误时会发生什么?这个特殊的行为在 0.7 中变更了,因为我们想要更简单地得知实际发生了什么。新的行为相当简单:
1. 在每个请求之前,执行 [before_request()](# "flask.Flask.before_request") 上绑定的函数。如果这些函数中的某个返回了一个响应,其它的函数将不再被调用。任何情况下,无论如何这个返回值都会替换视图的返回值。
1. 如果 [before_request()](# "flask.Flask.before_request") 上绑定的函数没有返回一个响应,常规的请求处理将会生效,匹配的视图函数有机会返回一个响应。
1. 视图的返回值之后会被转换成一个实际的响应对象,并交给[after_request()](# "flask.Flask.after_request") 上绑定的函数适当地替换或修改它。
1. 在请求的最后,会执行 [teardown_request()](# "flask.Flask.teardown_request") 上绑定的函数。这总会发生,即使在一个未处理的异常抛出后或是没有请求前处理器执行过(例如在测试环境中你有时会想不执行请求前回调)。
现在错误时会发生什么?在生产模式中,如果一个异常没有被捕获,将调用500 internal server 的处理。在生产模式中,即便异常没有被处理过,也会往上冒泡抛给给 WSGI 服务器。如此,像交互式调试器这样的东西可以提供有用的调试信息。
在 0.7 中做出的一个重大变更是内部服务器错误不再被请求后回调传递处理,而且请求后回调也不再保证会执行。这使得内部的调度代码更简洁,易于定制和理解。
新的绑定于销毁请求的函数被认为是用于代替那些请求的最后绝对需要发生的事。
### 销毁回调
销毁回调是是特殊的回调,因为它们在不同的点上执行。严格地说,它们不依赖实际的请求处理,因为它们限定在 [RequestContext](# "flask.ctx.RequestContext") 对象的生命周期。当请求上下文出栈时, [teardown_request()](# "flask.Flask.teardown_request") 上绑定的函数会被调用。
这对于了解请求上下文的寿命是否因为在 with 声明中使用测试客户端或在命令行中使用请求上下文时被延长很重要:
~~~
with app.test_client() as client:
resp = client.get('/foo')
# the teardown functions are still not called at that point
# even though the response ended and you have the response
# object in your hand
# only when the code reaches this point the teardown functions
# are called. Alternatively the same thing happens if another
# request was triggered from the test client
~~~
从这些命令行操作中,很容易看出它的行为:
~~~
>>> app = Flask(__name__)
>>> @app.teardown_request
... def teardown_request(exception=None):
... print 'this runs after request'
...
>>> ctx = app.test_request_context()
>>> ctx.push()
>>> ctx.pop()
this runs after request
>>>
~~~
注意销毁回调总是会被执行,即使没有请求前回调执行过,或是异常发生。测试系统的特定部分也会临时地在不调用请求前处理器的情况下创建请求上下文。确保你写的请求销毁处理器不会报错。
### 留意代理
Flask 中提供的一些对象是其它对象的代理。背后的原因是,这些代理在线程间共享,并且它们在必要的情景中被调度到限定在一个线程中的实际的对象。
大多数时间你不需要关心它,但是在一些例外情况中,知道一个对象实际上是代理是有益的:
- 代理对象不会伪造它们继承的类型,所以如果你想运行真正的实例检查,你需要在被代理的实例上这么做(见下面的 _get_current_object )。
- 如果对象引用是重要的(例如发送 [*信号*](#) )
如果你需要访问潜在的被代理的对象,你可以使用[_get_current_object()](http://werkzeug.pocoo.org/docs/local/#werkzeug.local.LocalProxy._get_current_object "(在 Werkzeug v0.10)") [http://werkzeug.pocoo.org/docs/local/#werkzeug.local.LocalProxy._get_current_object] 方法:
~~~
app = current_app._get_current_object()
my_signal.send(app)
~~~
### 错误时的上下文保护
无论错误出现与否,在请求的最后,请求上下文会出栈,并且相关的所有数据会被销毁。在开发中,当你想在异常发生时,长期地获取周围的信息,这会成为麻烦。在 Flask 0.6 和更早版本中的调试模式,如果发生异常,请求上下文不会被弹出栈,这样交互式调试器才能提供给你重要信息。
从 Flask 0.7 开始,我们设定 PRESERVE_CONTEXT_ON_EXCEPTION 配置变量来更好地控制该行为。这个值默认与 DEBUG 的设置相关。当应用工作在调试模式下时,上下文会被保护,而生产模式下相反。
不要在生产模式强制激活 PRESERVE_CONTEXT_ON_EXCEPTION ,因为它会导致在异常时应用的内存泄露。不过,它在开发时获取开发模式下相同的错误行为来试图调试一个只有生产设置下才发生的错误时很有用。
© 版权所有 2013, Armin Ronacher.
- 欢迎使用 Flask
- 前言
- 给有经验程序员的前言
- 安装
- 快速入门
- 教程
- 介绍 Flaskr
- 步骤 0: 创建文件夹
- 步骤 1: 数据库模式
- 步骤 2: 应用设置代码
- 步骤 3: 创建数据库
- 步骤 4: 请求数据库连接
- 步骤 5: 视图函数
- 步骤 6: 模板
- 步骤 7: 添加样式
- 福利: 应用测试
- 模板
- 测试 Flask 应用
- 记录应用错误
- 配置处理
- 信号
- 即插视图
- 应用上下文
- 请求上下文
- 用蓝图实现模块化的应用
- Flask 扩展
- 与 Shell 共舞
- Flask 代码模式
- 大型应用
- 应用程序的工厂函数
- 应用调度
- 使用 URL 处理器
- 部署和分发
- 使用 Fabric 部署
- 在 Flask 中使用 SQLite 3
- 在 Flask 中使用 SQLAlchemy
- 上传文件
- 缓存
- 视图装饰器
- 使用 WTForms 进行表单验证
- 模板继承
- 消息闪现
- 用 jQuery 实现 Ajax
- 自定义错误页面
- 延迟加载视图
- 在 Flask 中使用 MongoKit
- 添加 Favicon
- 数据流
- 延迟请求回调
- 添加 HTTP Method Overrides
- 请求内容校验码
- 基于 Celery 的后台任务
- 部署选择
- mod_wsgi (Apache)
- 独立 WSGI 容器
- uWSGI
- FastCGI
- CGI
- 聚沙成塔
- API
- JSON 支持
- Flask 中的设计决策
- HTML/XHTML 常见问题
- 安全注意事项
- Flask 中的 Unicode
- Flask 扩展开发
- Pocoo 风格指引
- Python 3 支持
- 升级到最新版本
- Flask Changelog
- 许可证
- 术语表