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模式的對象,經過這個對象能夠解析並匹配請求對應的視圖函數。app
關於Rule
類有一些經常使用的方法:函數
empty()
——在實際狀況中,Rule
實例會和一個Map
實例進行綁定。經過empty()
方法能夠將Rule
實例和Map
實例解除綁定。工具
get_empty_kwargs()
——在empty()
方法中調用,能夠得到以前Rule
實例的參數,以便從新構造一個Rule
實例。post
get_rules(map)
——這個方法是對RuleFactory
類中get_rules
方法的重寫,返回Rule
實例自己。url
refresh()
——當修改Rule
實例(URL規則)後能夠調用該方法,以便更新Rule
實例和Map
實例的綁定關係。spa
bind(map, rebind=False)
——將Rule
實例和一個Map
實例進行綁定,這個方法會調用complie()
方法,會給Rule
實例生成一個正則表達式。設計
complie()
——根據Rule
實例的URL模式,生成一個正則表達式,以便後續對請求的path
進行匹配。code
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
過程進行分析:
設計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}複製代碼
構造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__>/?)$'
在設計WSGI
應用時,上述例子經過url_map.bind_to_environ(environ)
構建了一個MapAdapter實例。這個實例將請求的相關信息和已經建立好的Map
實例放在一塊兒,以便進行URL匹配。
進行URL匹配的過程是經過調用MapAdapter實例的match()
方法進行的。實質上,這個方法會將請求中的path
傳入到全部Rule實例的match(path)
方法中,通過正則表達式的匹配來分析path
是否和某個Rule實例匹配。若是匹配則返回對應的endpoint
和其餘的參數,這能夠做爲參數傳入視圖函數。
以後,訪問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 {}
本文章原載於個人技術博客,歡迎你們訪問。水平有限,若有不當之處還請指正,謝謝~