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)

这个函数的基本步骤是:

  1. 先是 打开request_context(environ), 然后我们就有了我们需要的“全局request”
  2. 然后preprocess_request()是处理数据前的准备
  3. 接下来调用dispatch_request()方法,这个方法会根据URL匹配的情况调用相关的视图函数
  4. 之后调用response(environ, start_response)方法将响应发送回WSGI服务器
  5. 最后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_stackrequest_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()
View Code

这是一个字典形式的结构,键代表当前线程的数值,值代表当前线程存储的变量,这样很容易实现线程分离,从而使每个线程都可访问自己的内的“全局变量”

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_apprequestsessiong 都会是自己线程内的“全局变量”

with

这里的 __enter____exit__ 方法是为了 wsgi_app 内 with 语句调用

  

延伸

flask0.9版本中加入了应用上下文的概念

原文地址:https://www.cnblogs.com/fuzzier/p/7647647.html