Werkzeug
是一個Python寫成的WSGI
工具集。它遵循WSGI
規範,對伺服器和Web應用之間的“中間層”進行了開發,衍生出一系列非常有用的Web服務底層模組。
Werkzeug庫的routing
模組的主要功能在於URL解析。對於WSGI
應用來講,不同的URL對應不同的檢視函式,routing
模組則會對請求資訊的URL進行解析並匹配,觸發URL對應的檢視函式,以此生成一個響應資訊。routing
模組的解析和匹配功能主要體現在三個類上:Rule
、Map
和MapAdapter
。
Rule
類
Rule
類繼承自RuleFactory
類。一個Rule
的例項代表一個URL模式,一個WSGI
應用可以處理很多不同的URL模式,這也就是說可以產生很多不同的Rule
例項。這些Rule
例項最終會作為引數傳遞給Map
類,形成一個包含所有URL模式的物件,通過這個物件可以解析並匹配請求對應的檢視函式。
關於Rule
類有一些常用的方法:
empty()
——在實際情況中,Rule
例項會和一個Map
例項進行繫結。通過empty()
方法可以將Rule
例項和Map
例項解除繫結。get_empty_kwargs()
——在empty()
方法中呼叫,可以獲得之前Rule
例項的引數,以便重新構造一個Rule
例項。get_rules(map)
——這個方法是對RuleFactory
類中get_rules
方法的重寫,返回Rule
例項本身。refresh()
——當修改Rule
例項(URL規則)後可以呼叫該方法,以便更新Rule
例項和Map
例項的繫結關係。bind(map, rebind=False)
——將Rule
例項和一個Map
例項進行繫結,這個方法會呼叫complie()
方法,會給Rule
例項生成一個正規表示式。complie()
——根據Rule
例項的URL模式,生成一個正規表示式,以便後續對請求的path
進行匹配。match(path)
——將Rule
例項和給定的path
進行匹配。在呼叫complie()
方法生成的正規表示式將會對path
進行匹配。如果匹配,將返回這個path
中的引數,以便後續過程使用。如果不匹配,將會由其他的Rule
例項和這個path
進行匹配。
注意: 在對給定的URL進行匹配的過程中,會使用一些Converters
。關於Converters
的資訊後續加以介紹。
Map
類
通過Map
類構造的例項可以儲存所有的URL規則,這些規則是Rule
類的例項。Map
例項可以 通過後續的呼叫和給定的URL進行匹配。
關於Map
類有一些常用的方法:
add(rulefactory)
——這個方法在構造Map
例項的時候就會呼叫,它會將所有傳入Map
類中的Rule
例項和該Map
例項建立繫結關係。該方法還會呼叫Rule
例項的bind
方法。bind
方法 ——這個方法會生成一個MapAdapter
例項,傳入MapAdapter
的包括一些請求資訊,這樣可以呼叫MapAdapter
例項的方法匹配給定URL。bind_to_environ
方法 ——通過解析請求中的environ
資訊,然後呼叫上面的bind
方法,最終會生成一個MapAdapter
例項。
MapAdapter
類
MapAdapter
類執行URL匹配的具體工作。關於MapAdapter
類有一些常用的方法:
dispatch
方法 ——該方法首先會呼叫MapAdapter
例項的match()
方法,如果有匹配的Rule
,則會執行該Rule
對應的檢視函式。match
方法 ——該方法將會進行具體的URL匹配工作。它會將請求中的url和MapAdapter
例項中的所有Rule
進行匹配,如果有匹配成功的,則返回該Rule
對應的endpoint
和一些引數rv
。endpoint
一般會對應一個檢視函式,返回的rv
可以作為引數傳入檢視函式中。
一個簡單的例子
為了說明routing
模組的工作原理,這裡使用Werkzeug
文件中的一個例子,稍加改動後如下所示:
from werkzeug.routing import Map, Rule, NotFound, RequestRedirect, HTTPException
url_map = Map([
Rule('/', endpoint='blog/index'),
Rule('/<int:year>/', endpoint='blog/archive'),
Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),
Rule('/<int:year>/<int:month>/<int:day>/', endpoint='blog/archive'),
Rule('/<int:year>/<int:month>/<int:day>/<slug>',
endpoint='blog/show_post'),
Rule('/about', endpoint='blog/about_me'),
Rule('/feeds/', endpoint='blog/feeds'),
Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
])
def application(environ, start_response):
urls = url_map.bind_to_environ(environ)
try:
endpoint, args = urls.match()
except HTTPException, e:
return e(environ, start_response)
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Rule points to %r with arguments %r' % (endpoint, args)]
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, application)複製程式碼
這裡我們使用werkzeug
自帶的伺服器模組構造了一個Web伺服器,並且設計了一個簡單的WSGI
應用——application
。這個Web伺服器可以根據URL的不同返回不同的結果。關於伺服器的構造這裡不再贅述,以下部分簡單對URL Routing
過程進行分析:
1. 設計URL模式
設計URL模式的過程就是構造Rule
例項的過程。上面的例子中我們構造了8個Rule
例項,分別對應8個不同的URL模式。每個Rule
例項還對應一個endpoint
,這個endpoint
可以和檢視函式進行對應,以便訪問某個URL時,可以觸發與之對應的檢視函式。下面的例子展示了endpoint
和檢視函式的對應關係。
from werkzeug.wrappers import Response
from werkzeug.routing import Map, Rule
def on_index(request):
return Response('Hello from the index')
url_map = Map([Rule('/', endpoint='index')])
views = {'index': on_index}複製程式碼
2. 構造Map例項
構造Map例項時,會呼叫它的add(rulefactory)
方法。這個方法會在Map例項和各個Rule例項之間建立繫結關係,並通過呼叫Rule例項的bind()
方法為每個Rule例項生成一個正規表示式。
例如,對於'/about'
這個URL,它對應的正規表示式為:
'^\\|\\/about$'
對於'/<int:year>/<int:month>/<int:day>/'
這個URL,它對應的正規表示式為:
'^\\|\\/(?P<year>\\d+)\\/(?P<month>\\d+)\\/(?P<day>\\d+)(?<!/)(?P<__suffix__>/?)$'
3. 構造MapAdapter例項
在設計WSGI
應用時,上述例子通過url_map.bind_to_environ(environ)
構建了一個MapAdapter例項。這個例項將請求的相關資訊和已經建立好的Map
例項放在一起,以便進行URL匹配。
進行URL匹配的過程是通過呼叫MapAdapter例項的match()
方法進行的。實質上,這個方法會將請求中的path
傳入到所有Rule例項的match(path)
方法中,經過正規表示式的匹配來分析path
是否和某個Rule例項匹配。如果匹配則返回對應的endpoint
和其他的引數,這可以作為引數傳入檢視函式。
4. 訪問URL可得相關結果
之後,訪問URL可以得到相對應的結果。
例如,訪問http://localhost:4000/2017/
,可以得到:
Rule points to 'blog/archive' with arguments {'year': 2017}
訪問http://localhost:4000/2017/3/20/
,可以得到:
Rule points to 'blog/archive' with arguments {'month': 3, 'day': 20, 'year': 2017}
訪問http://localhost:4000/about
,可以得到:
Rule points to 'blog/about_me' with arguments {}
本文章原載於我的技術部落格,歡迎大家訪問。水平有限,如有不當之處還請指正,謝謝~