Flask 應用中的 URL 處理

發表於2017-09-20

在文章:一個Flask應用執行過程剖析中,在一個上下文環境中可以處理請求。如果不考慮在處理請求前後做的一些操作,Flask原始碼中真正處理請求的是dispatch_request()方法。其原始碼如下:

從上面的原始碼中可以看到,dispatch_request()方法做了如下的工作:

  1. 對請求的URL進行匹配;
  2. 如果URL可以匹配,則返回相對應檢視函式的結果;
  3. 如果不可以匹配,則進行錯誤處理。

對於錯誤的處理,本文暫不做介紹。本文主要對Flask應用的URL模式以及請求處理過程中的URL匹配進行剖析。

Flask應用的url_map

Flask應用例項化的時候,會為應用增添一個url_map屬性。這個屬性是一個Map類,這個類在werkzeug.routing模組中定義,其主要的功能是為了給應用增加一些URL規則,這些URL規則形成一個Map例項的過程中會生成對應的正規表示式,可以進行URL匹配。相關的概念和內容可以參考:Werkzeug庫——routing模組

在Flask原始碼中,它通過兩個方法可以很方便地定製應用的URL。這兩個方法是:route裝飾器和add_url_rule方法。

1. add_url_rule

add_url_rule方法很簡單,只要向其傳遞一條URL規則rule和一個endpoint即可。endpoint一般為和這條URL相關的檢視函式的名字,這樣處理就可以將URL和檢視函式關聯起來。除此之外,還可以傳遞一些關鍵字引數。呼叫該方法後,會呼叫Map例項的add方法,它會將URL規則新增進Map例項中。

2. route裝飾器

為了更加方便、優雅地寫應用的URL,Flask實現了一個route裝飾器。

route裝飾器會裝飾一個檢視函式。經route裝飾的檢視函式首先會呼叫add_url_rule方法,將裝飾器中的URL規則新增進Map例項中,檢視函式的名字會作為endpoint進行傳遞。然後在該應用的view_functions中增加endpoint和檢視函式的對應關係。這種對應關係可以在請求成功時方便地呼叫對應的檢視函式。

3. 一個簡單的例子

我們用一個簡單的例子來說明以上過程的實現:

以上程式碼,我們建立了一個Flask應用app,並且通過route裝飾器的形式為app增加了3條URL規則。

首先: 我們看一下Flask應用的url_map長啥樣:

可以看到,url_map是一個Map例項,這個例項中包含4個Rule例項,分別對應4條URL規則,其中/static/<filename>在Flask應用例項化時會自動新增,其餘3條是使用者建立的。整個Map類便構成了Flask應用app的URL“地圖”,可以用作URL匹配的依據。

接下來: 我們看一下url_map中的一個屬性:_rules_by_endpoint

可以看出,_rules_by_endpoint屬性是一個字典,反映了endpoint和URL規則的對應關係。由於用route裝飾器建立URL規則時,會將檢視函式的名字作為endpoint進行傳遞,所以以上字典的內容也反映了檢視函式和URL規則的對應關係。

再接下來: 我們看一下Flask應用的view_functions

在用route裝飾器建立URL規則時,它還會做一件事情:self.view_functions[f.__name__] = f。這樣做是將函式名和檢視函式的對應關係放在Flask應用的view_functions。由於Map例項中儲存了函式名和URL規則的對應關係,這樣只要在匹配URL規則時,如果匹配成功,只要返回一個函式名,那麼便可以在view_functions中執行對應的檢視函式。

最後: 我們看一下URL如何和Map例項中的URL規則進行匹配。我們以/page/<int:id>這條規則為例:

可以看到,在將一條URL規則的例項Rule新增進Map例項的時候,會為這個Rule生成一個正規表示式的屬性_regex。這樣當這個Flask應用處理請求時,實際上會將請求中的url和Flask應用中每一條URL規則的正規表示式進行匹配。如果匹配成功,則會返回endpoint和一些引數,返回的endpoint可以用來在view_functions找到對應的檢視函式,返回的引數可以傳遞給檢視函式。具體的過程就是:

相關文章