flask0.1版本源码浅析——请求上下文
说先flask应用在请求时会调用 wsgi_app(self, environ, start_response) 这个方法
def wsgi_app(self, environ, start_response): # ... with self.request_context(environ): rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() response = self.make_response(rv) response = self.process_response(response) return response(environ, start_response)
这个函数的基本步骤是:
- 先是 打开request_context(environ), 然后我们就有了我们需要的“全局request”
- 然后preprocess_request()是处理数据前的准备
- 接下来调用dispatch_request()方法,这个方法会根据URL匹配的情况调用相关的视图函数
- 之后调用response(environ, start_response)方法将响应发送回
WSGI
服务器 - 最后with语句结束,当前线程会被销毁
request_context
可以看出,requests_context 方法会调用 _RequestContext(self, environ) 类, 所以我们看这个类就可以
def request_context(self, environ): """Creates a request context from the given environment and binds it to the current context. This must be used in combination with the `with` statement because the request is only bound to the current context for the duration of the `with` block. :params environ: a WSGI environment """ return _RequestContext(self, environ)
_RequestContext(self, environ)
class _RequestContext(object): """The request context contains all request relevant information. It is created at the beginning of the request and pushed to the `_request_ctx_stack` and removed at the end of it. It will create the URL adapter and request object for the WSGI environment provided. """ def __init__(self, app, environ): self.app = app self.url_adapter = app.url_map.bind_to_environ(environ) self.request = app.request_class(environ) self.session = app.open_session(self.request) self.g = _RequestGlobals() self.flashes = None def __enter__(self): _request_ctx_stack.push(self) def __exit__(self, exc_type, exc_value, tb): # do not pop the request stack if we are in debug mode and an # exception happened. This will allow the debugger to still # access the request object in the interactive shell. if tb is None or not self.app.debug: _request_ctx_stack.pop()
其中对该类的属性:
- app —— 属于当前Flask应用
- url_adapter —— 创建一个 Map 实例,为了注册路由使用
- request —— 接收 environ 后创建了 Request 的实例,储存请求信息
- session —— 接收 request 信息后,创建Session的相关对象,储存会话信息
- g —— 存储全局变量
- flash —— 消息闪现
_request_ctx_stack
_request_ctx_stack = LocalStack()
_request_ctx_stack 是由 LocalStack 类实现的类似栈的结构。
我们通过 push(self) 可以看出,_request_ctx_stack 将 request_context 存储到里面。存储结构为
{14272: {'stack': [<RequestContext 'http://localhost/' [GET] of test5>]}, 16696: {'stack': [<RequestContext 'http://localhost/' [GET] of test5>]}, 16856: {'stack': [<RequestContext 'http://localhost/' [GET] of test5>]}}
测试的代码为
from flask import Flask, _request_ctx_stack import threading app = Flask(__name__) print _request_ctx_stack._local.__storage__ def foo(): request_context = app.test_request_context() _request_ctx_stack.push(request_context) q = [] for i in range(3): t = threading.Thread(target=foo) t.start() q.append(t) for j in q: j.join()
这是一个字典形式的结构,键代表当前线程的数值,值代表当前线程存储的变量,这样很容易实现线程分离,从而使每个线程都可访问自己的内的“全局变量”
current_app = LocalProxy(lambda: _request_ctx_stack.top.app) request = LocalProxy(lambda: _request_ctx_stack.top.request) session = LocalProxy(lambda: _request_ctx_stack.top.session) g = LocalProxy(lambda: _request_ctx_stack.top.g)
LocalProxyi是一个 local对象的代理,负责把所有对自己的操作转发给内部的 Local
对象。
我们可以看出,current_app,request,session,g 都会是自己线程内的“全局变量”
with
这里的 __enter__ 和 __exit__ 方法是为了 wsgi_app 内 with 语句调用
延伸
flask0.9版本中加入了应用上下文的概念