Flask進(jìn)擊篇(1)——Flask運(yùn)行流程

微信公眾號:戰(zhàn)渣渣
歡迎大家關(guān)注,一起分享。

一次完整的請求

在我們了解Flask運(yùn)行流程之前,先看一下我們在瀏覽器一次請求中所經(jīng)歷的過程,下面這張是結(jié)合Flask的源碼繪制的一張流程圖

Python-Web開發(fā)中Flask運(yùn)行流程

能看到Flask在一次請求中實(shí)際上只是做了最后一部分功能,這里沒有將Flask的具體處理流程列出來,我們在下面會繼續(xù)講解。
在上圖中出現(xiàn)WSGIRequestHandler,WSGI協(xié)議是在Python Web開發(fā)中很核心的部分,如果想繼續(xù)進(jìn)擊的話,需要對這部分有深刻的理解。
這部分我在另一篇文章中有寫到,如有需要可以點(diǎn)擊WEB開發(fā)——Python WSGI協(xié)議查看

Flask處理流程

我所理解Flask要做的事情,是根據(jù)請求的HTTP協(xié)議中url和method映射相應(yīng)的處理函數(shù),處理完并返回。這是基礎(chǔ)的功能,F(xiàn)lask在這基礎(chǔ)上又增加了一些其他功能。下面我們就通過Flask的源碼中一些屬性來進(jìn)行分析。

Flask部分重要屬性

Flask部分屬性.png
Flask啟動時構(gòu)建屬性

Flask在啟動時已將各屬性根據(jù)需求配置好,但實(shí)際映射函數(shù)的屬性就是view_functions,此屬性類型為字典,key是endpoint。

endpoint可自定義,若不指定將會根據(jù)函數(shù)名生成,若出現(xiàn)重復(fù)的endpoint將會提示錯誤。

endpoint會與url和method統(tǒng)一封裝成到rule放入到url_map中,在請求過來時會根據(jù)url和和method生成reuqest到url_map中匹配,如果匹配到則根據(jù)endpoint獲取到相應(yīng)的函數(shù)去執(zhí)行,并將結(jié)果返回。這部分可以看Flask源碼部分。

添加到url_map

# flask/app.py
def add_url_rule(
    self,
    rule,
    endpoint=None,
    view_func=None,
    provide_automatic_options=None,
    **options
    ):
    if endpoint is None:
        endpoint = _endpoint_from_view_func(view_func)
    options["endpoint"] = endpoint
    methods = options.pop("methods", None)

    if methods is None:
        methods = getattr(view_func, "methods", None) or ("GET",)
    if isinstance(methods, string_types):
        raise TypeError(
            "Allowed methods have to be iterables of strings, "
            'for example: @app.route(..., methods=["POST"])'
        )
    methods = set(item.upper() for item in methods)

    # Methods that should always be added
    required_methods = set(getattr(view_func, "required_methods", ()))

    # starting with Flask 0.8 the view_func object can disable and
    # force-enable the automatic options handling.
    if provide_automatic_options is None:
        provide_automatic_options = getattr(
            view_func, "provide_automatic_options", None
        )

    if provide_automatic_options is None:
        if "OPTIONS" not in methods:
            provide_automatic_options = True
            required_methods.add("OPTIONS")
        else:
            provide_automatic_options = False

    # Add the required methods now.
    methods |= required_methods

    rule = self.url_rule_class(rule, methods=methods, **options)
    rule.provide_automatic_options = provide_automatic_options

    self.url_map.add(rule)
    if view_func is not None:
        old_func = self.view_functions.get(endpoint)
        if old_func is not None and old_func != view_func:
            raise AssertionError(
                "View function mapping is overwriting an "
                "existing endpoint function: %s" % endpoint
            )
        self.view_functions[endpoint] = view_func

請求時匹配請求

  1. 生成請求
# flask/app.py
def create_url_adapter(self, request):
    if request is not None:
        subdomain = (
            (self.url_map.default_subdomain or None)
            if not self.subdomain_matching
            else None
        )
        return self.url_map.bind_to_environ(
            request.environ,
            server_name=self.config["SERVER_NAME"],
            subdomain=subdomain,
        )
    if self.config["SERVER_NAME"] is not None:
        return self.url_map.bind(
            self.config["SERVER_NAME"],
            script_name=self.config["APPLICATION_ROOT"],
            url_scheme=self.config["PREFERRED_URL_SCHEME"],
        )

# flask/ctx.py
def match_request(self):
    try:
        result = self.url_adapter.match(return_rule=True)
        self.request.url_rule, self.request.view_args = result
    except HTTPException as e:
        self.request.routing_exception = e  

此處是在生成上下文的push中執(zhí)行會執(zhí)行match_request,這里沒有貼出來。
實(shí)質(zhì)就是請求過來了,根據(jù)url和method匹配啟動時的url_map,如果沒有的話則返回匹配不到

  1. 匹配請求

# flask/app.py
def dispatch_request(self):
    req = _request_ctx_stack.top.request
    if req.routing_exception is not None:
        self.raise_routing_exception(req)
    rule = req.url_rule
    if (
        getattr(rule, "provide_automatic_options", False)
        and req.method == "OPTIONS"
    ):
        return self.make_default_options_response()
    # otherwise dispatch to the handler for that endpoint
    return self.view_functions[rule.endpoint](**req.view_args)

根據(jù)上面從url_map得到的rule,然后根據(jù)endpoint取得要執(zhí)行的函數(shù)。

Flask另外幾個屬性,則表示在請求之前和請求之后做一些處理,并且可以針對不同的blueprints來進(jìn)行處理,關(guān)于blueprints我們等幾個章節(jié)再細(xì)分析。

Flask的處理流程

Flask實(shí)際的處理流程是什么樣子,先看一下Flask的源碼

# flask/app.py

# 1. 先通過wsgi協(xié)議到這個函數(shù)
def __call__(self, environ, start_response):
    return self.wsgi_app(environ, start_response)

# 2. 然后調(diào)用這個函數(shù),處理上下文
def wsgi_app(self, environ, start_response):
    # 下文處理?。。?    ctx = self.request_context(environ)
    error = None
    try:
        try:
            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)
# 3. 請求處理流程
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)

基本流程可以看的比較清晰,至于每個函數(shù)列表的來源以及作用,我在開始的屬性圖上已將其標(biāo)識出來。


Flask執(zhí)行流程.

至此可以大體知道請求過來之后Flask是如何處理及前期Flask會構(gòu)建哪些內(nèi)容。

但Flask還有很多東西。例如我們經(jīng)常使用request,current_app對象和常用的blueprints是怎么個原理。

下章節(jié)會針對Flask的上下文處理再做深入的理解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容