国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

flask 源碼解析:路由

freecode / 3411人閱讀

摘要:而對于動態路由,還需要更復雜的匹配邏輯。路由邏輯事實上,核心的路由邏輯是在中實現的。所以在繼續分析之前,我們先看一下提供的路由功能。可以發現,在路由過程中非常重要。的路由過程,其實是到的轉換通過找到處理該的。

文章屬于作者原創,原文發布在個人博客。

構建路由規則

一個 web 應用不同的路徑會有不同的處理函數,路由就是根據請求的 URL 找到對應處理函數的過程。

在執行查找之前,需要有一個規則列表,它存儲了 url 和處理函數的對應關系。最容易想到的解決方案就是定義一個字典,key 是 url,value 是對應的處理函數。如果 url 都是靜態的(url 路徑都是實現確定的,沒有變量和正則匹配),那么路由的過程就是從字典中通過 url 這個 key ,找到并返回對應的 value;如果沒有找到,就報 404 錯誤。而對于動態路由,還需要更復雜的匹配邏輯。flask 中的路由過程是這樣的嗎?這篇文章就來分析分析。

在分析路由匹配過程之前,我們先來看看 flask 中,構建這個路由規則的兩種方法:

通過 @app.route() decorator,比如文章開頭給出的 hello world 例子

通過 app.add_url_rule,這個方法的簽名為 add_url_rule(self, rule, endpoint=None, view_func=None, **options),參數的含義如下:

rule: url 規則字符串,可以是靜態的 /path,也可以包含 /

endpoint:要注冊規則的 endpoint,默認是 view_func 的名字

view_func:對應 url 的處理函數,也被稱為視圖函數

這兩種方法是等價的,也就是說:

@app.route("/")
def hello():
    return "hello, world!"

也可以寫成

def hello():
    return "hello, world!"

app.add_url_rule("/", "hello", hello)

NOTE: 其實,還有一種方法來構建路由規則——直接操作 app.url_map 這個數據結構。不過這種方法并不是很常用,因此就不展開了。

注冊路由規則的時候,flask 內部做了哪些東西呢?我們來看看 route 方法:

def route(self, rule, **options):
    """A decorator that is used to register a view function for a
    given URL rule.  This does the same thing as :meth:`add_url_rule`
    but is intended for decorator usage.
    """

    def decorator(f):
        endpoint = options.pop("endpoint", None)
        self.add_url_rule(rule, endpoint, f, **options)
        return f

    return decorator

route 方法內部也是調用 add_url_rule,只不過在外面包了一層裝飾器的邏輯,這也驗證了上面兩種方法等價的說法。

def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    """Connects a URL rule.  Works exactly like the :meth:`route`
    decorator.  If a view_func is provided it will be registered with the
    endpoint.
    """

    methods = options.pop("methods", None)

    rule = self.url_rule_class(rule, methods=methods, **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

上面這段代碼省略了處理 endpoint 和構建 methods 的部分邏輯,可以看到它主要做的事情就是更新 self.url_mapself.view_functions 兩個變量。找到變量的定義,發現 url_mapwerkzeug.routeing:Map 類的對象,rulewerkzeug.routing:Rule 類的對象,view_functions 就是一個字典。這和我們之前預想的并不一樣,這里增加了 RuleMap 的封裝,還把 urlview_func 保存到了不同的地方。

需要注意的是:每個視圖函數的 endpoint 必須是不同的,否則會報 AssertionError

werkzeug 路由邏輯

事實上,flask 核心的路由邏輯是在 werkzeug 中實現的。所以在繼續分析之前,我們先看一下 werkzeug 提供的路由功能。

>>> m = Map([
...     Rule("/", endpoint="index"),
...     Rule("/downloads/", endpoint="downloads/index"),
...     Rule("/downloads/", endpoint="downloads/show")
... ])
>>> urls = m.bind("example.com", "/")
>>> urls.match("/", "GET")
("index", {})
>>> urls.match("/downloads/42")
("downloads/show", {"id": 42})

>>> urls.match("/downloads")
Traceback (most recent call last):
  ...
RequestRedirect: http://example.com/downloads/
>>> urls.match("/missing")
Traceback (most recent call last):
  ...
NotFound: 404 Not Found

上面的代碼演示了 werkzeug 最核心的路由功能:添加路由規則(也可以使用 m.add),把路由表綁定到特定的環境(m.bind),匹配url(urls.match)。正常情況下返回對應的 endpoint 名字和參數字典,可能報重定向或者 404 異常。

可以發現,endpoint 在路由過程中非常重要。werkzeug 的路由過程,其實是 url 到 endpoint 的轉換:通過 url 找到處理該 url 的 endpoint。至于 endpoint 和 view function 之間的匹配關系,werkzeug 是不管的,而上面也看到 flask 是把這個存放到字典中的。

flask 路由實現

好,有了這些基礎知識,我們回頭看 dispatch_request,繼續探尋路由匹配的邏輯:

def dispatch_request(self):
    """Does the request dispatching.  Matches the URL and returns the
    return value of the view or error handler.  This does not have to
    be a response object.  In order to convert the return value to a
    proper response object, call :func:`make_response`.
    """

    req = _request_ctx_stack.top.request
    if req.routing_exception is not None:
        self.raise_routing_exception(req)
    rule = req.url_rule

    # dispatch to the handler for that endpoint
    return self.view_functions[rule.endpoint](**req.view_args)

這個方法做的事情就是找到請求對象 request,獲取它的 endpoint,然后從 view_functions 找到對應 endpointview_func ,把請求參數傳遞過去,進行處理并返回。view_functions 中的內容,我們已經看到,是在構建路由規則的時候保存進去的;那請求中 req.url_rule 是什么保存進去的呢?它的格式又是什么?

我們可以先這樣理解:_request_ctx_stack.top.request 保存著當前請求的信息,在每次請求過來的時候,flask 會把當前請求的信息保存進去,這樣我們就能在整個請求處理過程中使用它。至于怎么做到并發情況下信息不會相互干擾錯亂,我們將在下一篇文章介紹。

_request_ctx_stack 中保存的是 RequestContext 對象,它出現在 flask/globals.py 文件中,和路由相關的邏輯如下:

class RequestContext(object):
    def __init__(self, app, environ, request=None):
        self.app = app
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.match_request()
    
    def match_request(self):
        """Can be overridden by a subclass to hook into the matching
        of the request.
        """
        try:
            url_rule, self.request.view_args = 
                self.url_adapter.match(return_rule=True)
            self.request.url_rule = url_rule
        except HTTPException as e:
            self.request.routing_exception = e

            
class Flask(_PackageBoundObject):
    def create_url_adapter(self, request):
        """Creates a URL adapter for the given request.  The URL adapter
        is created at a point where the request context is not yet set up
        so the request is passed explicitly.
        """
        if request is not None:
            return self.url_map.bind_to_environ(request.environ,
                server_name=self.config["SERVER_NAME"])

在初始化的時候,會調用 app.create_url_adapter 方法,把 appurl_map 綁定到 WSGI environ 變量上(bind_to_environ 和之前的 bind 方法作用相同)。最后會調用 match_request 方法,這個方式調用了 url_adapter.match 方法,進行實際的匹配工作,返回匹配的 url rule。而我們之前使用的 url_rule.endpoint 就是匹配的 endpoint 值。

整個 flask 的路由過程就結束了,總結一下大致的流程:

通過 @app.route 或者 app.add_url_rule 注冊應用 url 對應的處理函數

每次請求過來的時候,會事先調用路由匹配的邏輯,把路由結果保存起來

dispatch_request 根據保存的路由結果,調用對應的視圖函數

match 實現

雖然講完了 flask 的路由流程,但是還沒有講到最核心的問題:werkzeug 中是怎么實現 match 方法的。Map 保存了 Rule 列表,match 的時候會依次調用其中的 rule.match 方法,如果匹配就找到了 match。Rule.match 方法的代碼如下:

def match(self, path):
        """Check if the rule matches a given path. Path is a string in the
        form ``"subdomain|/path(method)"`` and is assembled by the map.  If
        the map is doing host matching the subdomain part will be the host
        instead.

        If the rule matches a dict with the converted values is returned,
        otherwise the return value is `None`.
        """
        if not self.build_only:
            m = self._regex.search(path)
            if m is not None:
                groups = m.groupdict()

                result = {}
                for name, value in iteritems(groups):
                    try:
                        value = self._converters[name].to_python(value)
                    except ValidationError:
                        return
                    result[str(name)] = value
                if self.defaults:
                    result.update(self.defaults)

                return result

它的邏輯是這樣的:用實現 compile 的正則表達式去匹配給出的真實路徑信息,把所有的匹配組件轉換成對應的值,保存在字典中(這就是傳遞給視圖函數的參數列表)并返回。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/38371.html

相關文章

  • flask 源碼解析:簡介

    摘要:簡介官網上對它的定位是一個微開發框架。另外一個必須理解的概念是,簡單來說就是一套和框架應用之間的協議。功能比較豐富,支持解析自動防止攻擊繼承變量過濾器流程邏輯支持代碼邏輯集成等等。那么,從下一篇文章,我們就正式開始源碼之旅了 文章屬于作者原創,原文發布在個人博客。 flask 簡介 Flask 官網上對它的定位是一個微 python web 開發框架。 Flask is a micro...

    megatron 評論0 收藏0
  • flask 源碼解析:響應

    摘要:我們知道響應分為三個部分狀態欄版本狀態碼和說明頭部以冒號隔開的字符對,用于各種控制和協商服務端返回的數據。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應用啟動流程 flask 源碼解析:路由 flask 源碼解析:上下文 flask 源碼解析:請求 flask 源碼解析:響應 response 簡介 在 f...

    wslongchen 評論0 收藏0
  • flask 源碼解析:應用啟動流程

    摘要:中有一個非常重要的概念每個應用都是一個可調用的對象。它規定了的接口,會調用,并傳給它兩個參數包含了請求的所有信息,是處理完之后需要調用的函數,參數是狀態碼響應頭部還有錯誤信息。一般來說,嵌套的最后一層是業務應用,中間就是。 文章屬于作者原創,原文發布在個人博客。 WSGI 所有的 python web 框架都要遵循 WSGI 協議,如果對 WSGI 不清楚,可以查看我之前的介紹文章。 ...

    whatsns 評論0 收藏0
  • flask源碼走讀

    摘要:另外,如果你對模板渲染部分的內容感興趣,也可以考慮閱讀文檔文檔文檔源碼閱讀,可以參考下面的函數打斷點,再測試一個請求,理清過程。 Flask-Origin 源碼版本 一直想好好理一下flask的實現,這個項目有Flask 0.1版本源碼并加了注解,挺清晰明了的,我在其基礎上完成了對Werkzeug的理解部分,大家如果想深入學習的話,可以參考werkzeug_flow.md. 閱讀前 為...

    Coly 評論0 收藏0
  • flask 源碼解析:請求

    摘要:可以看到,雖然是同樣的請求數據,在不同的階段和不同組件看來,是完全不同的形式。請求還有一個不那么明顯的特性它不能被應用修改,應用只能讀取請求的數據。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應用啟動流程 flask 源碼解析:路由 flask 源碼解析:上下文 flask 源碼解析:請求 flask 源碼解...

    weizx 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<