URL篇
在分析路由匹配過程之前,我們先來看看 flask 中,構建這個路由規則的兩種方法:
-
通過
@app.route()decorator -
通過
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)
其實,還有一種方法來構建路由規則——直接操作 app.url_map 這個數據結構。不過這種方法並不是很常用,因此就不展開了
靜態路由
@app.route('/')
def hello_world():
# 變量可以通過賦值傳到前端,前端可以通過Jinja語法{{}}渲染
return render_template('t1.html', name='t1', age=16)
@app.route('/services')
def services():
return 'Services'
@app.route('/about')
def about():
return 'About'
# 相對projects解釋類似於文件夾解釋形式,指向某一個文件夾下的某個文件
@app.route('/projects/')
@app.route('/projects_our') # 可以定義多個URL到同一個視圖函數上,Flask支持
def projects():
return 'Projects'
@app.route('/login',methods=["GET","POST"])
def login():
return render_template('login.html', req_method=request.method)
動態路由
# 動態路由
@app.route('/user/<username>')
def user(username):
print username
return username
# 路由轉換器:指定參數類型
# flask提供3種:int(整形)|float(浮點型)|path(路徑,並支持一個/)
@app.route('/user/<int:user_id>')
def user(user_id):
print user_id
return 'User_id:%s'%user_id
自定義路由規則
# flask不提供正則表達的形式的URL匹配
# 可通過定義完成
# 1、from werkzeug.routing import BaseConverter
# 2、自定義類
#轉換器
class RegexConverter(BaseConverter):
def __init__(self,url_map,*items):
super(RegexConverter,self).__init__(self)
# print items # (u'[a-z]{3}[A-Z]{3}',)
# print url_map # URL 的一個MAP對象,類似路由表
self.regex = items[0]
# 3、要將定義的類注冊到APP的url_map中,定義名稱
# app.url_map.converters['regex'] = RegexConverter
# 4、使用
@app.route('/user/<regex("[a-z]{3}[A-Z]{3}"):username>')
def user(username):
print username
return 'Username:%s' % username
淺析源碼
注冊路由規則的時候,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. """ if endpoint is None: endpoint = _endpoint_from_view_func(view_func) options['endpoint'] = endpoint methods = options.pop('methods', None) # if the methods are not given and the view_func object knows its # methods we can use that instead. If neither exists, we go with # a tuple of only ``GET`` as default. if methods is None: 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) # Methods that should always be added required_methods = set(getattr(view_func, 'required_methods', ())) # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. provide_automatic_options = getattr(view_func, 'provide_automatic_options', None) if provide_automatic_options is None: if 'OPTIONS' not in methods: provide_automatic_options = True required_methods.add('OPTIONS') else: provide_automatic_options = False # Add the required methods now. methods |= required_methods rule = self.url_rule_class(rule, methods=methods, **options) rule.provide_automatic_options = provide_automatic_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_map 和 self.view_functions 兩個變量。找到變量的定義,發現 url_map 是 werkzeug.routeing:Map 類的對象,rule 是 werkzeug.routing:Rule 類的對象,view_functions 就是一個字典。這和我們之前預想的並不一樣,這里增加了 Rule 和 Map 的封裝,還把 url 和 view_func 保存到了不同的地方。
需要注意的是:每個視圖函數的 endpoint 必須是不同的,否則會報 AssertionError。
未完待續。。。
