首先明确一个概念,Flask是一个多线程服务器
程序和请求上下文
一个Flask服务器从客户端收到请求时,要让视图函数能够访问一些对象(例如提交的表单),这样才能处理请求。
request
object 就是一个很好的例子,它封装了 客户端发送的HTTP
请求
Things get more complicated if you consider that the request object is not the only object that view functions might need to access to fulfill a request.
所以要想让视图函数能够访问请求对象,一个显而易见的方式是将其作为参数传入视图函数,不过这样会导致程序中的每个视图函数都增加一个参数。除了访问请求对象
,如果视图函数在处理请求时还要访问其他对象,情况会变得更糟。
To avoid cluttering view functions with lots of arguments that may or may not be needed,
Flask uses contexts to temporarily make certain objects globally accessible. Thanks to
contexts, view functions like the following one can be written:
所以为了避免大量可有可无的参数把视图函数弄得一团糟,Flask使用contexts
(上下文)临时把某些对象变成全局可访问。
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
中的特定的变量在一个线程中全局可访问,于此同时却不会影响别的线程。
下面我们来看官方文档对于应用上下文的解释:
The Application Context:
Flask 背后的设计理念之一就是,代码在执行时会处于两种不同的“状态”(states)。当Flask
对象被实例化后在模块层次上应用便开始隐式地处于应用配置状态。一直到第一个请求还是到达这种状态才隐式地结束。这里重点是第二个状态, 到了第二个状态,在处理请求时,有一些其它的规则:
- 当一个请求激活时,上下文的本地对象( flask.request
和其它对象等)指向当前的请求 - 你可以在任何时间里使用任何代码与这些对象通信
这个上下文有两种调用方式:
第一种是隐式的:当一个请求上下文(request contexts
)被压栈时,如果有必要的话一个应用上下文会被一起创建。由于这个原因,你可以忽略应用上下文的存在,除非你需要它。
第二种是显式地调用 app_context()
方法:
from flask import Flask, current_app
app = Flask(__name__)
with app.app_context():
# within this block, current_app points to app.
print current_app.name
The Request Context
request_context()
方法返回一个新的RequestContext 对象,并结合with声明来绑定上下文。从相同线程中被调用的一切,直到with声明结束前,都可以访问全局的请求变量( flask.request
和其它)。
请求上下文内部工作如同一个栈。栈顶是当前活动的请求。push() 把上下文添加到栈顶,pop() 把它移出栈。在出栈时,应用的teardown_request() 函数也会被执行。
另一件需要注意的事是,请求上下文被压入栈时,并且没有当前应用的应用上下文,它会自动创建一个 应用上下文 。
Flask在分发请求之前激活(或推送)程序和请求上下文,请求处理完成后再将其删除。程序上下文被push后,就可以在线程中使用相应的变量了(current_app
,g
),类似,请求上下文被push后,就可以使用request
和session
了,如果使用的时候我们没有激活application contexts
或者request contexts
,就会导致错误。