werkzeug/routing.py-Rule源码分析
Rule类主要用来定义和表示一个URL的模式。主要定义了一些关键字参数,用来改变url的行为。例如:这个url可以接收的请求方法,url的子域名,默认路径,端点名称,是否强制有斜杠在末尾等等
在最开始使用route()传递的option参数,最后都是传给了Rule这个类来处理的
class Rule(RuleFactory): 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): if not string.startswith('/'): raise ValueError('urls must start with a leading slash') self.rule = string self.is_leaf = not string.endswith('/') self.map = None self.strict_slashes = strict_slashes self.subdomain = subdomain self.host = host self.defaults = defaults self.build_only = build_only self.alias = alias if methods is None: self.methods = None else: if isinstance(methods, str): raise TypeError('param `methods` should be `Iterable[str]`, not `str`') self.methods = set([x.upper() for x in methods]) if 'HEAD' not in self.methods and 'GET' in self.methods: self.methods.add('HEAD') self.endpoint = endpoint self.redirect_to = redirect_to if defaults: self.arguments = set(map(str, defaults)) else: self.arguments = set() self._trace = self._converters = self._regex = self._argument_weights = None
参数解析:
- string:一个有含有占位符的url路径(<converter(arguments):name>), converter是可选的(converter定义在Map里面),默认是字符串
- defaults:可选项。在同一个endpoint下的一个字典格式的默认规则,如果你访问某个url的时候,这个url存在default配置,则会显示defaluts对用的url路径 例如:
url_map = Map([ Rule('/all/', defaults={'page': 1}, endpoint='all_entries'), Rule('/all/page/<int:page>', endpoint='all_entries') ]) # 当用户访问``http://example.com/all/page/1`` h将会重定向到``http://example.com/all/``.
- subdomain:用来定义这个rule的子域名规则的一个字符串。默认是disable的,用法如下
url_map = Map([ Rule('/', subdomain='<username>', endpoint='user/homepage'), Rule('/stats', subdomain='<username>', endpoint='user/stats') ])
- methods:定义允许访问的方法。默认只允许get方法。在0.6.1版本中,会自动添加HEAD如果存在GET请求的话
- bulid_only:True, 只创建url,而不会被匹配。一般用于静态文件的路径创建,不需要WSGI application处理的情况下
- endpoint:这个url的端点名称,可以是任何类型。最好的选择是使用字符串表示,
- strict_slashes:是否严格要求URL后面的斜杠。为这个URL重写Map里面的strict_slashes配置
- redirect_to:重定向到其他地址。可以是一个字符串(其他url路径),也可以是一个可调用的对西对象。例如:
def foo_with_slug(adapter, id): # ask the database for the slug for the old id. this of # course has nothing to do with werkzeug. return 'foo/' + Foo.get_slug_for_id(id) url_map = Map([ Rule('/foo/<slug>', endpoint='foo'), Rule('/some/old/url/<slug>', redirect_to='foo/<slug>'), # 重定向到上面定义的路径 Rule('/other/old/url/<int:id>', redirect_to=foo_with_slug) # 重定向到foo_with_slug函数 ])
- alias:在同一个endpoint里面为rule serves定义一个别名
- host:为整个host提供匹配规则。如果设置了host,则subdomain会是disabled状态
函数体解析:
1 def __init__(self, string, defaults=None, subdomain=None, methods=None, 2 build_only=False, endpoint=None, strict_slashes=None, 3 redirect_to=None, alias=False, host=None): 4 # 判断url路径是否是斜线开头 5 if not string.startswith('/'): 6 # 抛出异常:urls必须以斜线开头 7 raise ValueError('urls must start with a leading slash') 8 self.rule = string 9 self.is_leaf = not string.endswith('/') 10 11 self.map = None 12 self.strict_slashes = strict_slashes 13 self.subdomain = subdomain 14 self.host = host 15 self.defaults = defaults 16 self.build_only = build_only 17 self.alias = alias 18 # 对methods参数的判断 19 if methods is None: 20 self.methods = None 21 else: 22 if isinstance(methods, str): 23 raise TypeError('param `methods` should be `Iterable[str]`, not `str`') 24 self.methods = set([x.upper() for x in methods]) 25 # 如果没有HEAD和有GET的时候,把HEAD添加到methods里面 26 if 'HEAD' not in self.methods and 'GET' in self.methods: 27 self.methods.add('HEAD') 28 self.endpoint = endpoint 29 self.redirect_to = redirect_to 30 31 if defaults: 32 # 通过map对象以及str函数,取出defaulte中的每一个键,并设置成字符串格式。最终保存为set格式:{'abc'} 33 self.arguments = set(map(str, defaults)) 34 else: 35 self.arguments = set() 36 self._trace = self._converters = self._regex = self._argument_weights = None
在Map对象的add方法中调用了Rule对象的get_rules方法
def get_rules(self, map): yield self
在Map对象的app方法中调用了Rule类的bind方法
这个方法主要用来绑定路由规则到map,并基于路由规则信息创建正则表达式
1 def bind(self, map, rebind=False): 2 # 判断这个rule的map属性是否已经被绑定过了map值 3 if self.map is not None and not rebind: 4 raise RuntimeError('url rule %r already bound to map %r' % 5 (self, self.map)) 6 # 把Map的对象赋值给self.map 7 self.map = map 8 # 判断属性,如果Rule对象中没有,则使用Map类中的 9 if self.strict_slashes is None: 10 self.strict_slashes = map.strict_slashes 11 if self.subdomain is None: 12 self.subdomain = map.default_subdomain 13 # 调用Rule类的compile方法 14 self.compile()