Werkzeug 庫——routing 模塊簡析

Werkzeug是一個Python寫成的WSGI工具集。它遵循WSGI規範,對服務器和Web應用之間的「中間層」進行了開發,衍生出一系列很是有用的Web服務底層模塊。正則表達式

Werkzeug庫的routing模塊的主要功能在於URL解析。對於WSGI應用來說,不一樣的URL對應不一樣的視圖函數,routing模塊則會對請求信息的URL進行解析並匹配,觸發URL對應的視圖函數,以今生成一個響應信息。routing模塊的解析和匹配功能主要體如今三個類上:RuleMapMapAdapter服務器

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和一些參數rvendpoint通常會對應一個視圖函數,返回的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 {}


本文章原載於個人技術博客,歡迎你們訪問。水平有限,若有不當之處還請指正,謝謝~

相關文章
相關標籤/搜索