天天看點

Flask(三)之請求上下文源碼分析

目錄

  • 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 我們現在知道s

elf.__local

partial(_lookup_req_object, "request")

​ 那

self.__local()

就是

partial(_lookup_req_object, "request")()

執行

​ 4.2.1.3

partial(_lookup_req_objec,"request")

相當于給_l

ookup_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