请求上下文 — Flask 文档
请求上下文
请求上下文在请求期间跟踪请求级数据。 不是将请求对象传递给在请求期间运行的每个函数,而是访问 request 和 session 代理。
这类似于 应用程序上下文 ,它跟踪独立于请求的应用程序级数据。 当推送请求上下文时,会推送相应的应用程序上下文。
上下文的目的
当 Flask
应用程序处理一个请求时,它会根据它从 WSGI 服务器接收到的环境创建一个 Request
对象。 因为一个 worker(线程、进程或协程取决于服务器)一次只处理一个请求,所以在该请求期间,请求数据可以被认为是该 worker 的全局数据。 Flask 为此使用术语 context local。
Flask 在处理请求时自动 推送 请求上下文。 在请求期间运行的视图函数、错误处理程序和其他函数将有权访问 request 代理,该代理指向当前请求的请求对象。
上下文的生命周期
当 Flask 应用程序开始处理请求时,它会推送一个请求上下文,这也会推送一个 应用程序上下文 。 当请求结束时,它会弹出请求上下文,然后是应用程序上下文。
每个线程(或其他工作线程类型)的上下文都是唯一的。 request 不能传递给另一个线程,另一个线程将有不同的上下文堆栈,并且不知道父线程指向的请求。
上下文局部变量在 Werkzeug 中实现。 请参阅 werkzeug:local 以获取有关其内部工作原理的更多信息。
手动推送上下文
如果您尝试在请求上下文之外访问 request 或使用它的任何内容,您将收到以下错误消息:
这通常只在测试需要活动请求的代码时发生。 一种选择是使用 test client
来模拟完整请求。 或者,您可以在 with
块中使用 test_request_context()
,该块中运行的所有内容都可以访问 request,其中填充了您的测试数据。
如果您在代码中的其他地方看到与测试无关的错误,则很可能表明您应该将该代码移动到视图函数中。
有关如何从交互式 Python shell 使用请求上下文的信息,请参阅 使用 Shell 。
上下文如何工作
调用 Flask.wsgi_app()
方法来处理每个请求。 它在请求期间管理上下文。 在内部,请求和应用程序上下文作为堆栈工作,_request_ctx_stack 和 _app_ctx_stack。 当上下文被压入堆栈时,依赖于它们的代理可用并指向堆栈顶部上下文的信息。
当请求开始时,会创建并推送一个 RequestContext
,如果该应用程序的上下文还不是顶级上下文,则会首先创建并推送一个 AppContext
。 在推送这些上下文时,current_app、g、request 和 session 代理可用于处理请求的原始线程。
由于上下文是堆栈,因此在请求期间可能会推送其他上下文以更改代理。 虽然这不是一种常见的模式,但它可以用于高级应用程序,例如,执行内部重定向或将不同的应用程序链接在一起。
在分派请求并生成并发送响应后,将弹出请求上下文,然后弹出应用程序上下文。 在它们被弹出之前,会立即执行 teardown_request()
和 teardown_appcontext()
函数。 即使在调度期间发生未处理的异常,它们也会执行。
回调和错误
Flask 在多个阶段分派请求,这会影响请求、响应以及错误的处理方式。 上下文在所有这些阶段都处于活动状态。
Blueprint
可以为这些特定于蓝图的事件添加处理程序。 如果蓝图拥有与请求匹配的路由,则蓝图的处理程序将运行。
- 在每个请求之前,会调用
before_request()
函数。 如果这些函数之一返回值,则跳过其他函数。 返回值被视为响应并且不调用视图函数。 - 如果
before_request()
函数没有返回响应,则调用匹配路由的视图函数并返回响应。 - 视图的返回值被转换为实际的响应对象并传递给
after_request()
函数。 每个函数返回一个修改过的或新的响应对象。 - 返回响应后,弹出上下文,调用
teardown_request()
和teardown_appcontext()
函数。 即使在上面的任何一点引发了未处理的异常,也会调用这些函数。
如果在拆卸函数之前引发异常,Flask 会尝试将其与 errorhandler()
函数匹配以处理异常并返回响应。 如果没有找到错误处理程序,或者处理程序本身引发异常,Flask 将返回一个通用的 500 Internal Server Error
响应。 仍然会调用拆卸函数,并传递异常对象。
如果启用调试模式,未处理的异常不会转换为 500
响应,而是传播到 WSGI 服务器。 这允许开发服务器向交互式调试器提供回溯。
拆解回调
拆卸回调独立于请求分派,而是在弹出时由上下文调用。 即使在调度期间存在未处理的异常以及手动推送的上下文,也会调用这些函数。 这意味着不能保证请求分派的任何其他部分首先运行。 请务必以不依赖于其他回调且不会失败的方式编写这些函数。
在测试期间,在请求结束后推迟弹出上下文会很有用,以便可以在测试函数中访问它们的数据。 使用 test_client()
作为 with
块来保留上下文,直到 with
块退出。
信号
如果 signals_available 为真,则发送以下信号:
- request_started 在调用
before_request()
函数之前发送。 - request_finished 在调用
after_request()
函数后发送。 - got_request_exception 在开始处理异常时发送,但在查找或调用
errorhandler()
之前。 - request_tearing_down 在调用
teardown_request()
函数后发送。
错误时的上下文保护
在请求结束时,请求上下文被弹出并销毁与其关联的所有数据。 如果在开发过程中发生错误,延迟销毁数据以进行调试是很有用的。
当开发服务器在开发模式下运行时(FLASK_ENV
环境变量设置为 'development'
),错误和数据将被保留并显示在交互式调试器中。
这种行为可以通过 PRESERVE_CONTEXT_ON_EXCEPTION 配置来控制。 如上所述,在开发环境中默认为True
。
不要在生产中启用 PRESERVE_CONTEXT_ON_EXCEPTION,因为它会导致您的应用程序在异常时泄漏内存。
代理注意事项
Flask 提供的一些对象是其他对象的代理。 每个工作线程以相同的方式访问代理,但指向绑定到每个工作线程的唯一对象,如本页所述。
大多数时候你不必关心这个,但有一些例外,知道这个对象实际上是一个代理:
- 代理对象不能将它们的类型伪装成实际的对象类型。 如果要执行实例检查,则必须在被代理的对象上执行此操作。
- 在某些情况下需要对代理对象的引用,例如发送 Signals 或将数据传递到后台线程。
如果需要访问被代理的底层对象,请使用 _get_current_object()
方法: