上一节我们留了一个思考题,就是request在啥时候从env中获得具体的参数呢?
大致流程是上面的流程,具体的代码如下:
### 伪代码
global request
class App:
def __call__(env, start_response):
# 根据url选择对应的函数,比如选的是A
A()
start_response()
return
def A():
print(request.name)
def B():
print(request.name)
app = App()
if __name__ == '__main__':
web_server = WebServer(host, port, app)#可以是gunicorn或者werkzeug的server
web_server.run()
根据代码可以看到在调用call函数的时候,request还是一个空值,所以一定要在调用A()之前把request赋值具体怎么赋值?
def __call__(environ, start_response):
ctx = self.request_context(environ)
ctx.push()
response = self.full_dispatch_request()
return response(environ, start_response)
根据flask的源码可以看到,其实和我们的伪代码相比,response等效于伪代码中的call
换句话说,就是在我们的伪代码前加了三行,那么这三行不用看源码都知道目的是:第一从environ读取信息并且赋值给request, 第二是根据url选出要执行的函数,也就是选择A()还是B()
始终带着上面分析出来的两个目的来看源码:
# 可以看出ctx是一个RequestContext类型,ctx是其一个对象
def request_context(self, environ):
return RequestContext(self, environ)
class RequestContext:
def push(self):
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop(top._preserved_exc)
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
app_ctx = self.app.app_context()
app_ctx.push()
self._implicit_app_ctx_stack.append(app_ctx)
else:
self._implicit_app_ctx_stack.append(None)
_request_ctx_stack.push(self)#把自己压入请求上下文栈
从上面的代码可以看到两个核心概念:_request_ctx_stack和_app_ctx_stack,这里可以直接告诉大家,这个两个对象是全局对象,这里就望文生义当做是个栈用来分别存请求上下文和应用上下文的,push函数首先是看看栈的顶部有没有RequestContext(请求上下文)的对象,理论来说是没有的,因为对于这个全局变量_request_ctx_stack我们到目前为止从来没有操作过,同理_app_ctx_stack也一样,由于app_ctx也是空的,所以就进入下面的代码。
app_ctx = self.app.app_context()
app_ctx.push()
|||
# 返回一个应用上下文
def app_context(self):
return AppContext(self)
# 应用上下文的类定义和push函数
class AppContext(object):
def push(self):
self._refcnt += 1
_app_ctx_stack.push(self)# 可以看到_app_ctx_stack压入一个应用上下文
appcontext_pushed.send(self.app)
终上可以发现前两行代码主要做了两件事,就是生成一个请求上下文(ctx)和应用上下文(app_ctx),然后分别压入请求上下文栈(_request_ctx_stack)和应用上下文栈(_app_ctx_stack),可以注意到ctx的初始化用到了environ, 而app_ctx是在ctx的push函数中创建的,盲猜全局变量request一定和ctx有关系。接下来看看源码:
# context locals
def _lookup_req_object(name):
top = _request_ctx_stack.top
return getattr(top, name)
def _lookup_app_object(name):
top = _app_ctx_stack.top
return getattr(top, name)
def _find_app():
top = _app_ctx_stack.top
return top.app
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request"))## 在这
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
可以看到request的定义在这,其实就是找_request_ctx_stack的顶部对象,然后这个请求上下中的request的属性就是全局request,到目前为止我们终于理出了逻辑。
为啥画蛇添足搞个_request_ctx_stack?我直接定义一个RequestContext全局变量a,都有请求的时候把ctx赋值给a, 用的时候request = a.request不就可以了吗?此外LocalStack,LocalProxy是啥?还有请求上下文可以理解,搞个应用上下文是干嘛滴?以上问题会在下一节一一解释,同时分析第三行代码。