本文大致梳理一下Flask框架在處理url路由時(shí)的主要過程。
類圖
路由類圖
route裝飾器
在Flask應(yīng)用中,我們一般通過decorator裝飾view函數(shù),來注冊一個(gè)路由,表示url和處理函數(shù)的對應(yīng)關(guān)系,例如:
@app.route('/')
def index():
return 'Hello World'
route裝飾器定義如下, 其本質(zhì)是調(diào)用了Flask對象的add_url_rule函數(shù):
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ù)簽名為def add_url_rule(self, rule, endpoint=None, view_func=None, **options)
,其主要做了以下4件事情:
- endpoint默認(rèn)取view函數(shù)名稱
- 提供默認(rèn)的 http方法(HEAD, OPTION)
- 創(chuàng)建url_rule_class對象(url_rule_class默認(rèn)為werkzeug.routing.Route類),并添加到url_map中(werkzeug.routing.Map對象)
- 將endpoint和view_func保存到view_functions字典中
werkzeug中的Rule、Map和MapAdapter對象
- 一個(gè)Rule對象代表一個(gè)url模式,其中重要的字段為:
- rule: string類型,存儲原始的url路徑,其中包括類似于
<converter(arguments):name>
的placeholder - endpoint: 一般為string類型,F(xiàn)lask通過endpoint找到對應(yīng)的view函數(shù)
- map: Map對象引用,bind過程中賦值
- _regex: 根據(jù)rule以及一些設(shè)置參數(shù)創(chuàng)建的正則表達(dá)式,bind過程中調(diào)用compile時(shí)創(chuàng)建,match過程中會根據(jù)_regex進(jìn)行匹配
- 一個(gè)Map對象包含了所有Rule對象集合,以及一些配置參數(shù),其中重要的字段為:
- _rules: list,通過add方法添加Rule對象,同時(shí)會調(diào)用Rule對象的bind方法將Rule和Map對象綁定,二者為1:n的雙向引用關(guān)系
- _rules_by_endpoint: 以endpoint為key的字典,方便查找
- converters: dict,url轉(zhuǎn)換器,有默認(rèn)的和自定義的,Rule對象在compile過程中創(chuàng)建_regex時(shí)會用到converters。
- MapAdapter對象由Map對象的bind或bind_to_environ方法創(chuàng)建(與Rule的bind方法不同,后者是Rule與Map綁定),在RequestContext對象初始化時(shí),會將請求參數(shù)environ傳給Map對象創(chuàng)建MapAdapter對象,保存在url_adapter字段中,其主要字段和方法為:
- map: 保存Map對象
- match方法: 今次遍歷map中_rules列表對象,調(diào)用Rule對象的match方法進(jìn)行實(shí)際的匹配過程,如果匹配成功,則返回該Rule(或?qū)?yīng)的endpoint)和url路徑中的參數(shù)
請求路由過程
- Flask應(yīng)用初始化過程
此過程主要通過route裝飾器(或直接調(diào)用add_url_rule方法)創(chuàng)建好Rule對象,并添加到Map對象中 - 請求分發(fā)過程
- 創(chuàng)建RequestContext對象并推入棧中
- RequestContext初始化時(shí)會將請求參數(shù)environ傳給Map對象創(chuàng)建MapAdapter對象,保存在url_adapter字段中
- 調(diào)用MapAdapter對象的match方法找到匹配的Rule并解析出參數(shù),保存在Request對象的url_rule和view_args字段中
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
- 調(diào)用Flask對象的full_dispatch_request、dispatch_request方法,通過request中保存的匹配到的Rule獲取endpoint以及的view_args參數(shù),調(diào)用對應(yīng)的view函數(shù),完成一次請求分發(fā)