Flask進擊篇(1)——Flask運行流程

一次完整的請求

在我們瞭解Flask運行流程之前,先看一下我們在瀏覽器一次請求中所經歷的過程,下面這張是結合Flask的源碼繪製的一張流程圖

在這裏插入圖片描述
能看到Flask在一次請求中實際上只是做了最後一部分功能,這裏沒有將Flask的具體處理流程列出來,我們在下面會繼續講解。
在上圖中出現WSGIRequestHandler,WSGI協議是在Python Web開發中很核心的部分,如果想繼續進擊的話,需要對這部分有深刻的理解。
這部分我在另一篇文章中有寫到,如有需要可以點擊WEB開發——Python WSGI協議查看

Flask處理流程

我所理解Flask要做的事情,是根據請求的HTTP協議中url和method映射相應的處理函數,處理完並返回。這是基礎的功能,Flask在這基礎上又增加了一些其他功能。下面我們就通過Flask的源碼中一些屬性來進行分析。

Flask部分重要屬性

在這裏插入圖片描述
在這裏插入圖片描述
Flask在啓動時已將各屬性根據需求配置好,但實際映射函數的屬性就是view_functions,此屬性類型爲字典,key是endpoint。

endpoint可自定義,若不指定將會根據函數名生成,若出現重複的endpoint將會提示錯誤。

endpoint會與url和method統一封裝成到rule放入到url_map中,在請求過來時會根據url和和method生成reuqest到url_map中匹配,如果匹配到則根據endpoint獲取到相應的函數去執行,並將結果返回。這部分可以看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中執行會執行match_request,這裏沒有貼出來。
實質就是請求過來了,根據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)

根據上面從url_map得到的rule,然後根據endpoint取得要執行的函數。

Flask另外幾個屬性,則表示在請求之前和請求之後做一些處理,並且可以針對不同的blueprints來進行處理,關於blueprints我們等幾個章節再細分析。

Flask的處理流程

Flask實際的處理流程是什麼樣子,先看一下Flask的源碼

	# flask/app.py

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

	# 2. 然後調用這個函數,處理上下文
    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)

基本流程可以看的比較清晰,至於每個函數列表的來源以及作用,我在開始的屬性圖上已將其標識出來。
在這裏插入圖片描述

至此可以大體知道請求過來之後Flask是如何處理及前期Flask會構建哪些內容。

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

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

微信公衆號:戰渣渣

加深交流聯繫看下方

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章