Werkzeug库:routing模块

2017-09-09 20:00:00 Python开发者

(点击上方蓝字,快速关注我们)


来源: Learn Python

fanchunke.me/Flask/Werkzeug库——routing模块/

如有好文章投稿,请点击 → 这里了解详情


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 {}


看完本文有收获?请转发分享给更多人

关注「Python开发者」,提升Python技能