zoukankan      html  css  js  c++  java
  • Werkzeug源码阅读笔记(四)

    今天主要讲一下werkzeug中的routing模块。这个模块是werkzeug中的重点模块,Flask中的路由相关的操作使用的都是这个模块

    routing模块的用法

    在讲解模块的源码之前,先讲讲这个模块怎么用。
    创建Map()对象:

    >>> m = Map([
    ...     # Static URLs
    ...     Rule('/', endpoint='static/index'),
    ...     Rule('/about', endpoint='static/about'),
    ...     Rule('/help', endpoint='static/help'),
    ...     # Knowledge Base
    ...     Subdomain('kb', [
    ...         Rule('/', endpoint='kb/index'),
    ...         Rule('/browse/', endpoint='kb/browse'),
    ...         Rule('/browse/<int:id>/', endpoint='kb/browse'),
    ...         Rule('/browse/<int:id>/<int:page>', endpoint='kb/browse')
    ...     ])
    ... ], default_subdomain='www')
    

    我们可以看到,一个Map中以列表的形式包含多个Rule. 示例里面还有个Subdomain,除了Subdomain名外,它里面以列表的形式包含多个Rule,如果没有Subdomain,后面的default_subdomain可以省略(default_subdomain适配于除了Subdomain之外的Rule部分)
    在创建了Map的实例后,我们可以为每个Subdomain创建URL适配器

    >>> c = m.bind('example.com')
    >>> c.build("kb/browse", dict(id=42))   #如果url有参数,使用dict()里面填参数名和值
    'http://kb.example.com/browse/42/'
    >>> c.build("kb/browse", dict())        #build接受的参数是endpoint,返回url地址
    'http://kb.example.com/browse/'
    >>> c.build("kb/browse", dict(id=42, page=3))
    'http://kb.example.com/browse/42/3'
    >>> c.build("static/about")
    '/about'
    >>> c.build("static/index", force_external=True)
    'http://www.example.com/'
    
    >>> c = m.bind('example.com', subdomain='kb')
    >>> c.build("static/about")
    'http://www.example.com/about'
    

    部分源码分析

    routing模块中有个RuleFactory类,提供了get_rules()工厂方法,该方法的设计目的是使得URL重用. 所有继承该类的类必须实现该方法
    比如Subdomain类:

    class Subdomain(RuleFactory):
        def __init__(self, subdomain, rules):
            self.subdomain = subdomain
            self.rules = rules
        def get_rules(self, map):
            for rulefactory in self.rules:
                for rule in rulefactory.get_rules(map):
                    rule = rule.empty()
                    rule.subdomain = self.subdomain
                    yield rule
    

    在该类中get_rules方法是一个生成器,调用一次返回一个subdomain中的rule,且该Rule未绑定.
    同理还有Submount,EndpointPrefix类,源码差不多,就不细讲了。只说下怎么用:
    Submount的用法:

    url_map = Map([
                Rule('/', endpoint='index'),
                Submount('/blog', [
                    Rule('/', endpoint='blog/index'),
                    Rule('/entry/<entry_slug>', endpoint='blog/show')
                ])
            ])
    

    把submount中第一个元素(路径)跟在原路径后,里面的Rule均以这个路径为挂载点
    这里当访问'blog/entry/<entry_slug>'就会找到'blog/show'这个endpoint;当访问'blog'就会找到'blog/index'这个endpoint

    EndpointPrefix的用法:

    url_map = Map([
                Rule('/', endpoint='index'),
                EndpointPrefix('blog/', [Submount('/blog', [
                    Rule('/', endpoint='index'),
                    Rule('/entry/<entry_slug>', endpoint='show')
                ])])
            ])
    

    和上一个示例等效,只不过是提取出了所有endpoint中相同的前缀放在前面

    Rule类

    在该类的实例中最重要的就是存储了URL地址。以下是它的构造方法的头部:

    def __init__(self, string, defaults=None, subdomain=None, methods=None,
                 build_only=False, endpoint=None, strict_slashes=None,
                 redirect_to=None, alias=False, host=None)
    
    

    在该类中,实现了bind方法,源码如下:

    def bind(self, map, rebind=False):
    	if self.map is not None and not rebind:
    		raise RuntimeError('url rule %r already bound to map %r' %
                                   (self, self.map))
    		self.map = map
    	if self.strict_slashes is None:
    		self.strict_slashes = map.strict_slashes
    	if self.subdomain is None:
    		self.subdomain = map.default_subdomain
    	self.compile()
    

    该方法的作用就是把Rule的实例绑定到一个Map实例上去

    Map类

    在该类的实例中,存储了很多个Rule类的实例,同时还有部分配置参数。以下是它的构造方法的头部:

    def __init__(self, rules=None, default_subdomain='', charset='utf-8',
                 strict_slashes=True, redirect_defaults=True,
                 converters=None, sort_parameters=False, sort_key=None,
                 encoding_errors='replace', host_matching=False)
    

    对照本文中开头的部分,可以发现rules参数是一个列表,该列表中包含了多个Rule的实例
    在该类中实现了几个重要的方法:

    • add()方法:它的作用是把一个Rule的实例添加到该Map实例中,并绑定。源码很简单:
    def add(self, rulefactory):
    	for rule in rulefactory.get_rules(self):   #获得rule实例
    		rule.bind(self)   #绑定该实例(源码见上面Rule类中的bind方法)
    		#在Map的_rules列表中加入该Rule实例,_rules用来装Map初始化函数中rules参数传进的Rules
    		self._rules.append(rule)   
    		self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
    	self._remap = True
    
    • bind()方法:返回一个MapAdapter类的实例,该类的作用是用来做URL的匹配。bind的头部为:
    bind(self, server_name, script_name=None, subdomain=None,
            url_scheme='http', default_method='GET', path_info=None,
            query_args=None)
    

    MapAdapter类

    该类用来做URL匹配。
    dispatcher()方法:该方法的作用是,传入path_info,该方法会使用match()方法找到对应的endpoint和相关参数,然后再把这个endpoint作为参数传入view_func视图函数中,返回一个view_func对象. 源码如下

    def dispatch(self, view_func, path_info=None, method=None,
                     catch_http_exceptions=False):
    	try:
    		try:
    			endpoint, args = self.match(path_info, method)  #获得endpoint和所需参数
    		except RequestRedirect as e:
    			return e
    		return view_func(endpoint, args)
    	except HTTPException as e:
    		if catch_http_exceptions:
    			return e
    		raise
    

    match()方法:该方法的作用是,传入path_infomethod,该方法会返回对应的endpoint和路径包含的参数,比如:

        >>> m = Map([
        ...     Rule('/', endpoint='index'),
        ...     Rule('/downloads/', endpoint='downloads/index'),
        ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
        ... ])
        >>> urls = m.bind("example.com", "/")  #urls是MapAdapter对象
        >>> urls.match("/", "GET")
        ('index', {})
        >>> urls.match("/downloads/42")
        ('downloads/show', {'id': 42})
    

    build()方法:该方法与match()对应,传入endpoint和对应参数,返回path_info. 用法如下

        >>> m = Map([
        ...     Rule('/', endpoint='index'),
        ...     Rule('/downloads/', endpoint='downloads/index'),
        ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
        ... ])
        >>> urls = m.bind("example.com", "/")
        >>> urls.build("index", {})
        '/'
        >>> urls.build("downloads/show", {'id': 42})
        '/downloads/42'
  • 相关阅读:
    Session的使用与Session的生命周期
    Long-Polling, Websockets, SSE(Server-Sent Event), WebRTC 之间的区别与使用
    十九、详述 IntelliJ IDEA 之 添加 jar 包
    十八、IntelliJ IDEA 常用快捷键 之 Windows 版
    十七、IntelliJ IDEA 中的 Maven 项目初体验及搭建 Spring MVC 框架
    十六、详述 IntelliJ IDEA 创建 Maven 项目及设置 java 源目录的方法
    十五、详述 IntelliJ IDEA 插件的安装及使用方法
    十四、详述 IntelliJ IDEA 提交代码前的 Code Analysis 机制
    十三、IntelliJ IDEA 中的版本控制介绍(下)
    十二、IntelliJ IDEA 中的版本控制介绍(中)
  • 原文地址:https://www.cnblogs.com/eric-nirnava/p/werkzeug4.html
Copyright © 2011-2022 走看看