Flask 應用中的 URL 處理
來源:fanchunke1991
fanchunke.me/Flask/Flask應用中的URL處理/
在文章:《一個Flask應用運行過程剖析中》,在一個上下文環境中可以處理請求。如果不考慮在處理請求前後做的一些操作,Flask源碼中真正處理請求的是dispatch_request()方法。其源碼如下:
defdispatch_request(self):
"""Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
be a response object. In order to convert the return value to a
proper response object, call :func:`make_response`.
"""
try:
endpoint,values=self.match_request()
returnself.view_functions[endpoint](**values)
exceptHTTPException,e:
handler=self.error_handlers.get(e.code)
ifhandlerisNone:
returne
returnhandler(e)
exceptException,e:
handler=self.error_handlers.get(500)
ifself.debugorhandlerisNone:
raise
returnhandler(e)
從上面的源碼中可以看到,dispatch_request()方法做了如下的工作:
對請求的URL進行匹配;
如果URL可以匹配,則返回相對應視圖函數的結果;
如果不可以匹配,則進行錯誤處理。
對於錯誤的處理,本文暫不做介紹。本文主要對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
defadd_url_rule(self,rule,endpoint,**options):
options[ endpoint ]=endpoint
options.setdefault( methods ,( GET ,))
self.url_map.add(Rule(rule,**options))
add_url_rule方法很簡單,只要向其傳遞一條URL規則rule和一個endpoint即可。endpoint一般為和這條URL相關的視圖函數的名字,這樣處理就可以將URL和視圖函數關聯起來。除此之外,還可以傳遞一些關鍵字參數。調用該方法後,會調用Map實例的add方法,它會將URL規則添加進Map實例中。
2. route裝飾器
為了更加方便、優雅地寫應用的URL,Flask實現了一個route裝飾器。
defroute(self,rule,**options):
defdecorator(f):
self.add_url_rule(rule,f.__name__,**options)
self.view_functions[f.__name__]=f
returnf
returndecorator
route裝飾器會裝飾一個視圖函數。經route裝飾的視圖函數首先會調用add_url_rule方法,將裝飾器中的URL規則添加進Map實例中,視圖函數的名字會作為endpoint進行傳遞。然後在該應用的view_functions中增加endpoint和視圖函數的對應關係。這種對應關係可以在請求成功時方便地調用對應的視圖函數。
3. 一個簡單的例子
我們用一個簡單的例子來說明以上過程的實現:
>>>fromflaskimportFlask
>>>app=Flask(__name__)
>>>@app.route( / )
defindex():
return"Hello, World!"
>>>@app.route( / )
defuser(username):
return"Hello, %s"%username
>>>@app.route( /page/ )
defpage(id):
return"This is page %d"%id
以上代碼,我們創建了一個Flask應用app,並且通過route裝飾器的形式為app增加了3條URL規則。
首先: 我們看一下Flask應用的url_map長啥樣:
>>>url_map=app.url_map
>>>url_map
Map([index>,
static>,
page>,
user>
])
可以看到,url_map是一個Map實例,這個實例中包含4個Rule實例,分別對應4條URL規則,其中/static/在Flask應用實例化時會自動添加,其餘3條是用戶創建的。整個Map類便構成了Flask應用app的URL「地圖」,可以用作URL匹配的依據。
接下來: 我們看一下url_map中的一個屬性:_rules_by_endpoint:
>>>rules_by_endpoint=url_map._rules_by_endpoint
>>>rules_by_endpoint
{ index :[index>],
page :[page>],
static :[static>],
user :[user>]
}
可以看出,_rules_by_endpoint屬性是一個字典,反映了endpoint和URL規則的對應關係。由於用route裝飾器創建URL規則時,會將視圖函數的名字作為endpoint進行傳遞,所以以上字典的內容也反映了視圖函數和URL規則的對應關係。
再接下來: 我們看一下Flask應用的view_functions:
>>>view_functions=app.view_functions
>>>view_functions
{ index : ,
page : ,
user :
}
在用route裝飾器創建URL規則時,它還會做一件事情:self.view_functions[f.__name__] = f。這樣做是將函數名和視圖函數的對應關係放在Flask應用的view_functions。由於Map實例中存儲了函數名和URL規則的對應關係,這樣只要在匹配URL規則時,如果匹配成功,只要返回一個函數名,那麼便可以在view_functions中運行對應的視圖函數。
最後: 我們看一下URL如何和Map實例中的URL規則進行匹配。我們以/page/這條規則為例:
>>>rule=url_map._rules[2]
page>
>>>rule._regex
re.compile(ur ^\/page/(?Pd+)$ ,re.UNICODE)
>>>rule._regex.pattern
u ^\\/page\/(?P\d+)$
可以看到,在將一條URL規則的實例Rule添加進Map實例的時候,會為這個Rule生成一個正則表達式的屬性_regex。這樣當這個Flask應用處理請求時,實際上會將請求中的url和Flask應用中每一條URL規則的正則表達式進行匹配。如果匹配成功,則會返回endpoint和一些參數,返回的endpoint可以用來在view_functions找到對應的視圖函數,返回的參數可以傳遞給視圖函數。具體的過程就是:
try:
# match_request()可以進行URL匹配
endpoint,values=self.match_request()
returnself.view_functions[endpoint](**values)
...
看完本文有收穫?請轉發分享給更多人
關注「Python開發者」,提升Python技能


※Python 中的作用域規則和閉包簡析
※Python 源碼閱讀:int
※Python 源碼閱讀: String
※Python 中的屬性訪問與描述符
※Python 源碼閱讀:對象
TAG:Python開發者 |