1. 在flask中配置URL和視圖函數的路由時,首先須要在main.py中實例化一個app對象:python
1 from flask import Flask, render_template 2 3 app = Flask(__name__)
2. 而後經過app實例的route方法裝飾視圖函數,實現路由的配置:express
1 @app.route('/') 2 def hello_world(): 3 return 'Hellow World!'
3. 全部這裏須要關注在Flask類裏定義的route方法,以理解Flask內部的路由配置邏輯flask
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
可見app實例的route其實是一個帶參數的裝飾器,其中rule是URL規則(字符串形式),而options能夠接收其餘按關鍵字傳參的配置項,在上面Hello World的例子中,options應該是一個空字典。app
這個裝飾器的做用是把URL規則和視圖函數交由app實例的add_url_rule方法處理,並返回被裝飾函數自己,因此在main.py中視圖函數名依然原來的視圖函數對象的引用。dom
4. 下一部須要關注的是add_url_rule方法的內部實現:ide
在add_url_rule方法裏首先處理endpoint,這裏endpoint能夠理解爲和URL規則映射的視圖函數對象函數
1 if endpoint is None: 2 endpoint = _endpoint_from_view_func(view_func) 3 options['endpoint'] = endpoint
因爲在Hellow World例子裏,endpoint是None, 這裏會調用_endpoint_from_view_func方法:ui
1 def _endpoint_from_view_func(view_func): 2 assert view_func is not None, 'expected view func if endpoint ' \ 3 'is not provided.' 4 return view_func.__name__ # 返回被裝飾視圖函數的函數名
這裏view_func就是被裝飾的視圖函數,因此endpoint就被設置成立被裝飾視圖函數的函數名url
因而可知,若是用戶但願endpoint不是被裝飾視圖函數時,須要在@app.route()裏以endpoint關鍵子傳參給定一個函數對象名spa
處理完以後,endpoint被添加到options字典中
接着add_url_rule方法繼續處理methods, 這裏methods能夠理解爲這條URL和視圖的映射適用於那種Http請求方法:
1 methods = options.pop('methods', None) 2 if methods is None: 3 methods = getattr(view_func, 'methods', None) or ('GET',) 4 if isinstance(methods, string_types): 5 raise TypeError('Allowed methods have to be iterables of strings, ' 6 'for example: @app.route(..., methods=["POST"])') 7 methods = set(item.upper() for item in methods) # 最後methods是一個包含用戶傳入的Http請求方法,或默認GET請求方法的集合
首先從options字典裏取出'methods'對應的值,在Hellow World的例子中,此時methods = None
接着,把methods設置爲視圖函數的‘methods’屬性。
p.s.:看到這裏使用了getattr函數,咱們能夠發現,app.route裝飾的視圖,並不要求是必定要定義成函數的形式,也能夠定義成一個python模塊導入到main.py中,這樣以來flask的視圖系統就具備了更加靈活的擴展性。因此methods參數既能夠做爲app.route的關鍵字參數,也經過定義視圖的模塊中methods標量來定義。
若是app.route()沒有傳入methods參數,也沒有再視圖模塊中定義methods變量,methods默認賦值爲('GET'),可見flask中路由配置默認是對應HTTP GET請求的。
Flask要求用戶傳入各個的methods方法必須是字符串形式,而且放在符合python協議的可迭代對象中,不然,會拋出異常提示,上面4 - 6行代碼都是在作這一層判斷
最後,methods變量裏的元素被取出並放入集合。
至此用戶定義的URL規則和Http請求方法處理完畢。
5. 若是視圖模塊中有定義了'requeire_methods'參數,也須要處理:
1 required_methods = set(getattr(view_func, 'required_methods', ()))
required_methods的做用這裏暫時先不關注,後續再介紹
6 接下來以前的處理的methods和required_methos進行並集處理,都添加到methods參數中
methods |= required_methods
7. 把處理好的URL規則和methods參數,以及options字典委託給app實例的url_rule_class方法作進一步的處理
1 rule = self.url_rule_class(rule, methods=methods, **options)
url_rule_class其實是一個叫Rule的類,這一步若是處理經過,參數rule會接收一個Rule的實例
8. Rule這個類的__init__方法以下:
1 class Rule(RuleFactory): 2 def __init__(self, string, defaults=None, subdomain=None, methods=None, 3 build_only=False, endpoint=None, strict_slashes=None, 4 redirect_to=None, alias=False, host=None): 5 if not string.startswith('/'): 6 raise ValueError('urls must start with a leading slash') 7 self.rule = string 8 self.is_leaf = not string.endswith('/') 9 10 self.map = None 11 self.strict_slashes = strict_slashes 12 self.subdomain = subdomain 13 self.host = host 14 self.defaults = defaults 15 self.build_only = build_only 16 self.alias = alias 17 if methods is None: 18 self.methods = None 19 else: 20 if isinstance(methods, str): 21 raise TypeError('param `methods` should be `Iterable[str]`, not `str`') 22 self.methods = set([x.upper() for x in methods]) 23 if 'HEAD' not in self.methods and 'GET' in self.methods: 24 self.methods.add('HEAD') 25 self.endpoint = endpoint 26 self.redirect_to = redirect_to 27 28 if defaults: 29 self.arguments = set(map(str, defaults)) 30 else: 31 self.arguments = set() 32 self._trace = self._converters = self._regex = self._argument_weights = None
這裏再回顧一下上面給__init__方法的傳入的參數:
1 rule = self.url_rule_class(rule, methods=methods, **options)
URL規則是第一個位置參數,methods以及options字典裏的鍵值對,都被__init__方法按關鍵字接收
首先,若是app.route傳入的URL不是一個以'/'開頭的字符串,會拋出異常
self.is_leaf記錄URL是否沒有以‘/’結尾
而後,若是methos裏有"GET"方法,而沒有"HEAD",會把'HEAD'添加進入,'HEAD'的做用會把後續筆記中分析。
這裏注意到,__init__裏有一個self.redirect_to = redirect_to,多是能夠直接在app.route()裏設置視圖的跳轉,這個放到後面再具體分析。
能夠發現,flask裏把路由相關的:URL,host,適用的HTTP請求方法,endpoint視圖都保存到了Rule這個類的實例中。
9. 獲得Rule的實例後,回到add_url_rule方法,繼續看對rule實例的處理:
1 self.url_map.add(rule)
這裏url_map是Map類的一個實例,是在app實例化的時候綁定到app實例的,下面只須要關注Map類的add方法:
class Map:
... ...無關代碼省略
def add(self, rulefactory): """Add a new rule or factory to the map and bind it. Requires that the rule is not bound to another map. :param rulefactory: a :class:`Rule` or :class:`RuleFactory` """ for rule in rulefactory.get_rules(self): rule.bind(self) self._rules.append(rule) self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) self._remap = True
能夠看到這裏rulefactory能夠接收Rule實例或者RuleFactory實例,RuleFactory實例對應另外一種設置路由的方法。在咱們這個例子裏,rulefactory應該是一個Rule的實例
因此還須要進一步關注,Rule的實例的get_rules方法:
1 def get_rules(self, map): 2 yield self
get_rules方法接收兩個參數,Rule的實例,和Map的實例,咱們的例子裏,Map實例沒有做用,這個方法直接yield返回了Rule實例
下面繼續看rule實例的bind方法:
1 Class Rule: 2 ... ... 省略無關代碼 3 1 def bind(self, map, rebind=False): 4 2 """Bind the url to a map and create a regular expression based on 5 3 the information from the rule itself and the defaults from the map. 6 4 7 5 :internal: 8 6 """ 9 7 if self.map is not None and not rebind: 10 8 raise RuntimeError('url rule %r already bound to map %r' % 11 9 (self, self.map)) 12 10 self.map = map 13 11 if self.strict_slashes is None: 14 12 self.strict_slashes = map.strict_slashes 15 13 if self.subdomain is None: 16 14 self.subdomain = map.default_subdomain 17 15 self.compile()
這個實在判斷rule實例的map屬性是否爲None,若是是None,就把map實例綁定到rule實例的map實行,不然報錯,這裏就控制了一個rule實例只能跟一個map實例進行綁定。
以後會把rule實例append到這個map實例的self._rules列表中
以後這個map實例的_rules_by_endpoint屬性的會添加這樣一個鍵值對:rule.endpoint: [rule] 也就是 視圖對象:[rule實例]
至此,整個經過app,route裝飾視圖,來綁定URL和視圖映射關係的邏輯流程已經結束,此時
app實例的self.map保存的Map類實例裏保存了一個:視圖對象 和 rule實例映射的鍵值對。
總結起來:
--- app.route()裝飾器
獲取URL, 視圖對象,其餘opeions方法,並調用app實例的add_url_rule方法
--- add_url_rule方法:
1. 獲取app.route的methods關鍵字參數,視圖模塊裏定義的methods參數等Http 請求方法
這裏視圖能夠是一個函數,也能夠是一個python模塊
2. 把URL,視圖對象,Http請求方法,綁定到一個Rule實例(app實例的),經過app實例的url_rule_class方法。
Rule的__init__方法的其餘參數來自app.route的關鍵字傳參,能夠控制一些URL的匹配規則
build_only參數可讓URL不綁定任何視圖,實現static文件夾等。
---- url_map.add
把Rule實例和app實例保存的map實例綁定。