Flask(三)之请求上下文源码分析

目录

Flask请求上下文源码分析

from  flask import Flask

app  = Flask(__name__)

if __name__ == '__main__':
    
    app.run()

分析入口app.run(),点入查看源码:

from werkzeug.serving import run_simple

        try:
            run_simple(host, port, self, **options)
            #这里的self是app自己

run_simple是werkzeug内部的方法,在run_simple执行时会将app加括号调用从而执行app的__call__方法,来看__call__源码:

def __call__(self, environ, start_response):
        """The WSGI server calls the Flask application object as the
        WSGI application. This calls :meth:`wsgi_app` which can be
        wrapped to applying middleware."""
        return self.wsgi_app(environ, start_response)

进入wsgi_app:

ctx就是RequestContext对象,请求上下文对象ctx中初始化所有请求所有内容,并且其内部封装着Request对象,Request对象把请求过来的信息格式化并且储存起来。

1 ctx = self.request_context(environ)---》ctx本质是RequestContext的对象
1.1 RequestContext(self, environ) :self是当前的app,environ是请求相关的
1.2 RequestContext(self, environ)执行的结果是:RequestContext类的对象,该对象中包含了请求相关和当前的app
1.3 所以这个ctx就是RequestContext的对象。

'''
environ:A WSGI environment
start_response: A callable accepting a status code,a list of headers, and an optional exception context to start the response.         
'''
def wsgi_app(self, environ, start_response):

    ctx = self.request_context(environ)
    #在self.request_context(environ)中
#RequestContext类中的 `__init__`实例化出请求上下文对象ctx
    
        error = None
        try:
            try:

                #把ctx放到了Local对象里面了
                ctx.push()
                #请求扩展以及响应函数
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)
            

2 ctx.push():当前ctx是RequestContext的对象,那就是执行RequestContext的对象的push方法
2.1 ctx.push方法中有一个 _request_ctx_stack.push(self);这个self是ctx, 那就是把ctx,传递给_request_ctx_stack
2.2 _request_ctx_stack是什么?--》 LocalStack的对象
2.3 _request_ctx_stack.push方法是什么?看源码:
obj就是_request_ctx_stack.push(self),传过来的self,也就是ctx

        def push(self, obj):
            rv = getattr(self._local, "stack", None)
            if rv is None:
                #storage["线程或者协程的id"][stack] = []
                self._local.stack = rv = []
            # storage["线程或者协程的id"][stack] = [ctx,]
            rv.append(obj)
            return rv

​ 2.3.1 在2.3的代码中self._local.stack做了什么?
​ 我们发先self._local是Local对象,所以self._local就执行
​ Local对象的__setattr__:代码如下:

def __setattr__(self, name, value):
    ident = self.__ident_func__()
    storage = self.__storage__
    try:
        #name = stack
        storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

对2的总结:就是将ctx放到Locl对象中以这样的形式存储storage[ident][stack] = [ctx,]

response = self.full_dispatch_request() :这里面是所有请求扩展以及真正的响应函数:
源码如下:

 def full_dispatch_request(self):
            self.try_trigger_before_first_request_functions()
            try:
                request_started.send(self)
                rv = self.preprocess_request()
                #如果请求扩展中的,请求之前的所有函数,如果没有返回值,就会执行下面的,
                #下面的是:请求路由中的函数
                if rv is None:
                    rv = self.dispatch_request()
            except Exception as e:
                rv = self.handle_user_exception(e)
            return self.finalize_request(rv)

3.1 self.try_trigger_before_first_request_functions():这是在执行请求扩展中的befor_first_request
它是如何判断项目启动后只执行里面方法一次的呢?
答:它是通过self._got_first_request变量来判断的,初始值为False,一旦执行过该函数,在函数的末尾self._got_first_request设置成True
源码如下:

def try_trigger_before_first_request_functions(self):
    if self._got_first_request:
        return
    with self._before_request_lock:
        if self._got_first_request:
            return
        #这里去循环第一次执行的函数,请求扩展的里面
        for func in self.before_first_request_funcs:
            func()
            self._got_first_request = True

3.2 rv = self.preprocess_request() 这里执行的是befor_request的函数,
源码如下:

def preprocess_request(self):
    bp = _request_ctx_stack.top.request.blueprint
    funcs = self.url_value_preprocessors.get(None, ())
    if bp is not None and bp in self.url_value_preprocessors:
        funcs = chain(funcs, self.url_value_preprocessors[bp])
        for func in funcs:
            func(request.endpoint, request.view_args)
            #请求之前的要做的事情,请求之前的请求扩展
            funcs = self.before_request_funcs.get(None, ())
            if bp is not None and bp in self.before_request_funcs:
                funcs = chain(funcs, self.before_request_funcs[bp])
                for func in funcs:
                    rv = func()
                    if rv is not None:
                        return r

3.2.1 funcs = self.before_request_funcs.get(None, ())这里是获取所有注册进来的befor_request
3.2.2 下面的代码可以看出:如果befor_before_request函数有一个有返回值,那后面的函数都不执行,并且把返回值给rv = self.preprocess_request()的rv

for func in funcs:
    rv = func()
    if rv is not None:
        return rv

3.3 if rv is None: 这个rv是3.2中befor_reuqest返回的,如果没有返回值才会执行rv = self.dispatch_request();有返回值不会执行
rv = self.dispatch_request()这是真正的响应函数
通过这个代码:我们知道,如果befor_request有返回值,就不会执行真正的响应函数
3.4 return self.finalize_request(rv):这个个rv是3.2或者3.3的返回值
源码如下:

def finalize_request(self, rv, from_error_handler=False):
    response = self.make_response(rv)
    try:
        #这里执行的是执行完请求视图后,after_request的请求
        response = self.process_response(response)
        request_finished.send(self, response=response)
        except Exception:
            if not from_error_handler:
                raise
                self.logger.exception(
                    "Request finalizing failed with an error while handling an error"
                )
                return response

3.4.1 response = self.process_response(response):这里做after_request请求扩展的
源码如下:

def process_response(self, response):
    ctx = _request_ctx_stack.top
    bp = ctx.request.blueprint
    funcs = ctx._after_request_functions
    if bp is not None and bp in self.after_request_funcs:
        funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
        if None in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[None]))
            for handler in funcs:
                response = handler(response)
                if not self.session_interface.is_null_session(ctx.session):
                    self.session_interface.save_session(self, ctx.session, response)
                    return response

在上述代码中有两行代码:
if None in self.after_request_funcs:
funcs是所有after_request的函数列表,并用reversed做了反转
这个我们就知道,在after_request为什么先注册的后执行funcs = chain(funcs, reversed(self.after_request_funcs[None]))
for handler in funcs:
在这里知道after_request的函数必须接收这个response,并且要做返回response = handler(response)

4 我们知道3中就可以调用request属性了,那它怎么做到呢?当我们在3中的任意一个位置,都能调用request或者其他的。比如我们request.methons,它是如果找到当前请求的request呢
4.1 当我们调用request.methons的时候,我们先要看看rquest是什么?
看源码我们知道:request = LocalProxy(partial(_lookup_req_object, "request"))也就说是LocalProxy对象

4.2 当我们调用request.methons就会返回属性methons属性给我们,但是我们在LocalProxy根本就没有methons属性,那我们想到,既然没有这个属性,那一定会走__getattr__
LocalProxy的__getattr__源码如下:

def __getattr__(self, name):           
    if name == "__members__":
        return dir(self._get_current_object())
    return getattr(self._get_current_object(), name) 

上述代码中name,就是我们要找的methons属性,那它是从self._get_current_object()通过反射那拿去的,从下面的分析中我们知道self._get_current_object()就是request。

4.2.1 self._get_current_object()的返回值是什么?通过下面的源码我们可以看到就self.__local()执行结果
源码:

def _get_current_object(self):
    if not hasattr(self.__local, "__release_local__"):
        return self.__local()
    try:
        return getattr(self.__local, self.__name__)
    except AttributeError:
        raise RuntimeError("no object bound to %s" % self.__name__)

   

​ 4.2.1.1 self.__local()的执行结果是什么?我们先搞清楚self.__local是什么?
​ 我们发现self.__local是通过如下初始化得到的:

def __init__(self, local, name=None):
    object.__setattr__(self, "_LocalProxy__local", local)

​ 那我们可以知道self.__local就是4.1 中 LocalProxy(partial(_lookup_req_object, "request"))的参数,
​ 也就 partial(_lookup_req_object, "request")偏函数

​ 4.2.1.2 我们现在知道self.__localpartial(_lookup_req_object, "request")
​ 那 self.__local()就是 partial(_lookup_req_object, "request")()执行

​ 4.2.1.3 partial(_lookup_req_objec,"request")相当于给_lookup_req_objec函数传递了一个"request"参数
_lookup_req_objec的源码如下:

#name 为 "request"
def _lookup_req_object(name):
    #到Local中取取ctx取出来了
    top = _request_ctx_stack.top
    #top就是ctx
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
        #cxt中取request,把request返回回去了
        return getattr(top, name)

我们从 4.2.1.3.1得知:top就是ctx,getattr(top, name)就是从ctx中找request,并且返回那4.2.1.2中self.__local执行的结果就是request
4.2.1.3.1 上述代码中_request_ctx_stack.top的源码如下:

@property
def top(self):
    try:
        #返回了一开始进去的ctx对象
        return self._local.stack[-1]
    except (AttributeError, IndexError):
        return None  

    self._local也是Local对象.stack就是在执行__getattr__
    代码如下:
    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

这样_request_ctx_stack.top的到的结果就ctx,那 4.2.1.3 中的 top = _request_ctx_stack.top;top就是ctx

上一篇:webpack打包时候去掉console.log配置


下一篇:golang实现路由中间件middleware