Flask是目前爲止我最喜歡的一個Python Web框架了,爲了更好的掌握其內部實現機制,這兩天準備學習下Flask的源碼,將由淺入深跟你們分享下,其中Flask版本爲1.1.1。html
上次瞭解了Flask服務的啓動流程,今天咱們來看下路由的內部實現機理。python
Flask系列文章:app
所謂路由,就是處理請求URL和函數之間關係的程序。框架
Flask中也是對URL規則進行統一管理的,建立URL規則有兩種方式:ide
在開始閱讀源碼以前,我是有這幾點疑問的?函數
- 註冊路由的過程是什麼?
- Flask內部是如何進行URL規則管理的?
- 一個視圖函數綁定多個URL內部是如何實現的?
- 動態URL是如何進行視圖函數匹配的呢?
- 匹配路由的過程是怎樣的呢?
那就讓咱們帶着這幾點疑問一塊兒去學習源碼吧!工具
首先,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
route()有兩個參數,rule表示url規則。該函數對參數進行處理以後,調用方法add_url_role(),這裏也就驗證了兩種註冊路由的方法等價。咱們來看下代碼:學習
def add_url_rule( self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options ): 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. if provide_automatic_options is None: 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
入參包括:ui
能夠看到,add_url_rule()首先進行參數處理,包括:
在處理完全部的參數後,將該URL規則寫入url_map(建立好Rule對象,並添加到Map對象中),將視圖函數寫入view_function字典中。
其中,url_map 是werkzeug.routing:Map
類的對象,rule是 werkzeug.routing:Rule
類的對象,也就是Flask的核心路由邏輯是在werkzeug中實現的。
werkzeug
werkzeug是使用Python編寫的一個WSGI工具集,werkzeug.routing模塊主要用於url解析。
Rule類
Rule類繼承自RuleFactory類,一個Rule實例表明一個URL模式,一個WSGI應用會處理不少個不一樣的URL模式,與此同時產生不少個Rule實例,這些實例將做爲參數傳給Map類。
Map類
Map類構造的實例存儲全部的url規則,解析並匹配請求對應的視圖函數。
在應用初始化的過程當中,會註冊全部的路由規則,能夠調用(app.url_map)查看,當服務收到URL請求時,就須要進行路由匹配,以找到對應的視圖函數,對應的流程和原理是什麼呢?
當用戶請求進入Flask應用時,調用Flask類的wsgi_app方法:
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) error = None try: try: ctx.push() response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
該函數的處理過程包括:
接下來咱們看下full_dispatch_request方法:
def full_dispatch_request(self): self.try_trigger_before_first_request_functions() try: request_started.send(self) rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) return self.finalize_request(rv)
能夠看到,重點執行dispatch_request():
def dispatch_request(self): req = _request_ctx_stack.top.request if req.routing_exception is not None: self.raise_routing_exception(req) rule = req.url_rule # if we provide automatic options for this URL and the # request came with the OPTIONS method, reply automatically if ( getattr(rule, "provide_automatic_options", False) and req.method == "OPTIONS" ): return self.make_default_options_response() # otherwise dispatch to the handler for that endpoint return self.view_functions[rule.endpoint](**req.view_args)
處理的過程是:獲取請求對象的request,找到對應的endpoint,繼而從view_functions中找到對應的視圖函數,傳遞請求參數,視圖函數處理內部邏輯並返回,完成一次請求分發。
以上,就是Flask路由的內部實現原理。