Flask路由系統(tǒng)

通常有3種定義路由函數(shù)的方法:

  1. 使用flask.Flask.route() 修飾器聘殖。
  2. 使用flask.Flask.add_url_rule()函數(shù)。
  3. 直接訪問基于werkzeug路由系統(tǒng)的flask.Flask.url_map.

Part 1


讓我們從最常用的@app.route()修飾器開始。

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

可以看到修飾器是對add_url_rule函數(shù)的包裝,當我們寫如下代碼時:

    @app.route('/index.html')
    def index():
        return "Hello World!"

實際上上面的代碼轉(zhuǎn)換成:

def index():
    return "Hello World!"
index = app.route('/index.html')(index)

也就是,rule = '/index.html', options = { }, 執(zhí)行decorator(index) 時會執(zhí)行self.add_url_rule(rule, endpoint, f, **options):

    @setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=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:
            # View Function Options (視圖函數(shù)選項)
            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)

        # View Function Options (視圖函數(shù)選項)
        required_methods = set(getattr(view_func, 'required_methods', ()))

        # View Function Options (視圖函數(shù)選項)
        provide_automatic_options = getattr(view_func,
            'provide_automatic_options', None)
        # View Function Options (視圖函數(shù)選項)
        if provide_automatic_options is None:
            if 'OPTIONS' not in methods:
                provide_automatic_options = True
                required_methods.add('OPTIONS')
            else:
                provide_automatic_options = False

        # View Function Options (視圖函數(shù)選項)
        methods |= required_methods
        
        # url_rule_class默認為Werkzeug的Rule類,
        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options

        self.url_map.add(rule)
        # view_func 不能重復定義 
        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參數(shù)為None种吸,那么:

def _endpoint_from_view_func(view_func):
    """Internal helper that returns the default endpoint for a given
    function.  This always is the function name.
    """
    assert view_func is not None, 'expected view func if endpoint ' \
                                  'is not provided.'
    return view_func.__name__

endpoint就設置為view_func.name視圖函數(shù)的名字。然后將endpoint添加到options字典中呀非,對于methods = options.pop('methods', None)坚俗,當我們指定時,@app.route('/login', methods=['GET', 'POST'])岸裙,methods = ['GET', 'POST'] 否則methods = None. 如果methods == None, 同時猖败,view_func 沒有methods屬性,則methods默認設置為('GET', ). 當然降允,methods不能設置為字符串類型恩闻,‘POST’可以不區(qū)分大小寫。

關(guān)于View Function Options的代碼暫時忽略拟糕。
add_url_rule執(zhí)行完畢后判呕,我們獲得了Flask.url_map, 以及填充了Flask.view_functions.
我們可以做實驗看看url_map里面都有啥倦踢,詳見示例代碼

Part 2


下面回過頭侠草,來看看當Flask運行時辱挥,一個Request來了,會發(fā)生什么边涕,仍然從Flask.wsgi_app開始閱讀晤碘。
已經(jīng)知道,當一個Request到來時功蜓,會首先push RequestContext和AppContext园爷,在RequestContext中的init函數(shù)中有:

...
self.url_adapter = app.create_url_adapter(self.request)
...
self.match_request()
    def create_url_adapter(self, request):
        if request is not None:
            return self.url_map.bind_to_environ(request.environ,
                server_name=self.config['SERVER_NAME'])
        ...

首先將Flask.url_map與當前到來的Request中environ進行綁定,獲得一個url_adapter式撼。

    def match_request(self):
        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

獲得url_adaptor之后童社,調(diào)用match_request,url_adapter.match()會返回一個元組view_args就是url_rule中的參數(shù)著隆,比如Rule(/<int:year>/, endpoint='blog/archive')這個Rule,而請求是/2016/扰楼,那么view_args={year: 2016}. url_rule和view_args被儲存在Request中。在Request類中美浦,我們可以直接Request.endpoint將返回url_rule.endpoint.

在url_rule和view_args被裝載到Request中后弦赖,我們繼續(xù)對wsgi_app中的response = self.full_dispatch_request()這個過程與路由相關(guān)的內(nèi)容進行分析。

    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()
         ...

在preprocess_request()中處理與藍本和@before_request有關(guān)的東西.暫時忽略浦辨。

    def dispatch_request(self):
        # 從_request_ctx_stack獲得當前request.
        req = _request_ctx_stack.top.request
        # 如果有異常蹬竖,處理異常
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        # 獲得儲存在request中的url_rule.
        rule = req.url_rule
        # 視圖函數(shù)選項(暫時忽略)
        if getattr(rule, 'provide_automatic_options', False) \
           and req.method == 'OPTIONS':
            return self.make_default_options_response()
        # 如果沒有設定視圖函數(shù)選項,直接調(diào)用視圖函數(shù)流酬,在view_functions中查找
        # 鍵值為rule.endpoint的函數(shù)币厕,并傳入req.view_args(字典)作為
        # key-word參數(shù)。
        return self.view_functions[rule.endpoint](**req.view_args)

dispatch_request()處理完畢康吵,將返回值儲存在rv變量中劈榨。通常,視圖函數(shù)會return render_template(...). 返回值接下來經(jīng)過一系列處理晦嵌,發(fā)送到客戶端。

Part 3


視圖函數(shù)選項拷姿,藍本惭载?(to be contiued...)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市响巢,隨后出現(xiàn)的幾起案子描滔,更是在濱河造成了極大的恐慌,老刑警劉巖踪古,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件含长,死亡現(xiàn)場離奇詭異券腔,居然都是意外死亡,警方通過查閱死者的電腦和手機拘泞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門纷纫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陪腌,你說我怎么就攤上這事辱魁。” “怎么了诗鸭?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵染簇,是天一觀的道長。 經(jīng)常有香客問我强岸,道長锻弓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任蝌箍,我火速辦了婚禮青灼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘十绑。我一直安慰自己聚至,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布本橙。 她就那樣靜靜地躺著扳躬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甚亭。 梳的紋絲不亂的頭發(fā)上贷币,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音亏狰,去河邊找鬼役纹。 笑死,一個胖子當著我的面吹牛暇唾,可吹牛的內(nèi)容都是我干的促脉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼策州,長吁一口氣:“原來是場噩夢啊……” “哼瘸味!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起够挂,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤旁仿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后孽糖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枯冈,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡毅贮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尘奏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滩褥。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罪既,靈堂內(nèi)的尸體忽然破棺而出铸题,到底是詐尸還是另有隱情,我是刑警寧澤琢感,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布丢间,位于F島的核電站,受9級特大地震影響驹针,放射性物質(zhì)發(fā)生泄漏烘挫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一柬甥、第九天 我趴在偏房一處隱蔽的房頂上張望饮六。 院中可真熱鬧,春花似錦苛蒲、人聲如沸卤橄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窟扑。三九已至,卻和暖如春漏健,著一層夾襖步出監(jiān)牢的瞬間嚎货,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工蔫浆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留殖属,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓瓦盛,卻偏偏與公主長得像洗显,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子原环,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 22年12月更新:個人網(wǎng)站關(guān)停墙懂,如果仍舊對舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,184評論 22 257
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)扮念,斷路器,智...
    卡卡羅2017閱讀 134,672評論 18 139
  • [TOC]一直想做源碼閱讀這件事碧库,總感覺難度太高時間太少柜与,可望不可見巧勤。最近正好時間充裕,決定試試做一下弄匕,并記錄一下...
    何柯君閱讀 7,190評論 3 98
  • 本文大致梳理一下Flask框架在處理url路由時的主要過程颅悉。 類圖 route裝飾器 在Flask應用中,我們一般...
    Jakiro閱讀 3,533評論 0 6
  • 在flask框架中迁匠,我們經(jīng)常會遇到endpoint這個東西剩瓶,最開始也沒法理解這個到底是做什么的。最近正好在研究Fl...
    卡薩諾瓦_閱讀 947評論 0 0