在分析路由匹配過程以前,咱們先來看看 flask
中,構建這個路由規則的兩種方法:html
經過 @app.route()
decorator前端
經過 app.add_url_rule
,這個方法的簽名爲 add_url_rule(self, rule, endpoint=None, view_func=None, **options)
,參數的含義以下:python
rule
: url 規則字符串,能夠是靜態的 /path
,也能夠包含 /
flask
endpoint
:要註冊規則的 endpoint,默認是 view_func
的名字api
view_func
:對應 url 的處理函數,也被稱爲視圖函數數據結構
這兩種方法是等價的,也就是說:app
@app.route('/') def hello(): return "hello, world!"
也能夠寫成ide
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匹配 # 可經過定義完成 # 一、from werkzeug.routing import BaseConverter # 二、自定義類 #轉換器 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] # 三、要將定義的類註冊到APP的url_map中,定義名稱 # app.url_map.converters['regex'] = RegexConverter # 四、使用 @app.route('/user/<regex("[a-z]{3}[A-Z]{3}"):username>') def user(username): print username return 'Username:%s' % username
註冊路由規則的時候,flask 內部作了哪些東西呢?咱們來看看 route
方法:ui
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
。
未完待續。。。