Python Flask endpoint
說明
在寫登入後帳號是否啟動判斷時發現,『flask web開發』的作者在裡面用了request.endpoint條件式,讓原預計僅使用登入狀態與啟動狀態來判斷的我好奇了起來,到底在flask內endpoint是什麼!
首先,我看了一下app.route這個裝飾器。
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::
在route這個function的說明提到,使用裝飾器的效果跟使用add_url_rule一樣,這讓我想起了一開始在學flask的時候有看到前輩寫到,flask也可以跟django一樣,將所有的route寫在一個文件中,靠的就是add_url_rule。
@app.route('/fine/<name>')
def fine(name):
return 'My name is %s.' % name
舉例範例來說,上面這個標準寫法,也可以改寫成下面這樣
def fine(name):
return 'My name is %s.' % name
app.add_url_rule('/fine/<name>', 'fine', fine)
為什麼?
我們繼續追下去看route的內容就不難發現原因了,在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
第3行:如果你沒有給endpoint的話,就賦值None
第4行:調用add_url_rule這個method,那我們就看一下這個method的參數。
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.
:param rule: the URL rule as string
:param endpoint: the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint
:param view_func: the function to call when serving a request to the provided endpoint"""
rule:指路由名稱,就如同@app.route(’/abcd’)內的這個/abcd
endpoint:指endpoint,flask將view function的名稱視為endpoint
view_func:指view function,就是@app.route下的function,上例來看就是fine。
關於endpoint的部份,參數說明上提到了,沒有特別設置情況下,名稱同view function。如下可發現一二:
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
目前為止,還是無法知道endpoint是幹嘛的,追到最後面會找到到兩句重點。
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
self.url_map.add(rule)
路由的部份,似乎做了一點動作,一樣的,我們追蹤url_rule_class看看。
`endpoint`
The endpoint for this rule.
This can be anything. A reference to a
function, a string, a number etc.
The preferred way is using a string
because the endpoint is used for URL generation.
在class上對於endpoint的說明是,The endpoint for this rule。
這令人振奮,因為我們看到了一個關聯,接著rule被add進去了url_map,我們看url_map.add做了什麼。
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
從上面的說明可以看的出來,透過url_map.add,讓rule(url)與endpoint有了關聯。
第10行:_rules_by_enpoint是一個字典檔,透過字典檔記錄了rule(url)跟endpoint的關聯。
最後,我們繼續看就可以發現,做完了rule(url)與endpoint的關聯之後,flask開始處理view_function與endpoint的關聯。
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
第6行:似乎是個字典檔,觀其說明如下:
#: A dictionary of all view functions registered. The keys will
#: be function names which are also used to generate URLs and
#: the values are the function objects themselves.
#: To register a view function, use the :meth:`route` decorator.
self.view_functions = {}
這個字典檔,就是用來記錄endpoint與view_function的對應。
其實,如果去查詢url_for的話會發現,它的第一個參數放的是endpoint,那也可以明白為什麼可以利用url_for(‘view_function’)來回到route,因為其實是url_for(‘endpoint’)。
直觀的理解就是,flask透過了endpoint來記錄了跟rule(url)與view_function的關聯,所以有endpoint,就有辦法找到相關的rule與view_function。
因此,透過了url_for有辦法直接的尋找到相對應的route與view_function,以及利用route能夠找到相對應的view_function。
範例
知道了道理,就可以開始寫一個測試程式確認我們心中的想法。
首先,我們設置一個route
@app.route('/urlroot', endpoint="root")
def func_root():
print(url_for('root'))
print("View function: {view}. Endpoint: {endpoint}".format(view="func_root", endpoint=flask.request.endpoint))
第1行:指定endpoint為root
第4行:利用url_for來看root會定位到那
第5行:列印request.endpoint確認
得到的結果如下:
/urlroot
View function: func_root. Endpoint: root
可以確認到,利用endpoint我們確實的找到了rule,來源request.endpoint為root。
接著註解掉print(url_for(‘root’)),改換用view_function做參數測試,這時候得到的回應是一個錯誤,利用view_function是沒有辦法找到相對應的rule。
在查詢資料的期間也學習到另一種方式
app.add_url_rule(rule='/rule', endpoint="enpo", methods=["GET", "DELETE"])
app.view_functions['enpo'] = func_root
第1行:手動加入路由,但不指定view_function
第4行:手動關聯endpoint與view_function
得到的結果如下
View function: func_root. Endpoint: enpo
一個路由,可以讓多個endpoint去關聯。
以上學習記錄。
學習參考
stackoverflow
stackoverflow翻譯_感謝對岸同好
對岸討論區_segmentfault
這篇的案例可以讓我們更直觀的理解到endpoint的用途。