flask/app.py-add_url_rule源码分析
之前分析route方法的时候,可以看到中间会调用add_url_rule方法,add_url_rule方法和route方法一样属于Flask这个类的。
add_url_rule方法主要用来连接url规则。具体工作方法和route类似。如果提供了视图函数,它将会和endpoint名字一起被注册
#装饰器使用方法: @app.route('/') def index(): pass # 等同与下面这种方法: def index(): pass app.add_url_rule('/', 'index', index) If the view_func is not provided you will need to connect the endpoint to a view function like so:: # 如果视图函数没有被提供,需要用下面语法把视图函数和endpoint对应起来 app.view_functions['index'] = index
def add_url_rule(self, rule, endpoint=None, view_func=None, **options): if endpoint is None: endpoint = _endpoint_from_view_func(view_func) options['endpoint'] = endpoint methods = options.pop('methods', None) # if the methods are not given and the view_func object knows its # methods we can use that instead. If neither exists, we go with # a tuple of only ``GET`` as default. if methods is None: methods = getattr(view_func, 'methods', None) or ('GET',) if isinstance(methods, string_types): raise TypeError('Allowed methods have to be iterables of strings, ' 'for example: @app.route(..., methods=["POST"])') methods = set(item.upper() for item in methods) # Methods that should always be added required_methods = set(getattr(view_func, 'required_methods', ())) # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. provide_automatic_options = getattr(view_func, 'provide_automatic_options', None) if provide_automatic_options is None: if 'OPTIONS' not in methods: provide_automatic_options = True required_methods.add('OPTIONS') else: provide_automatic_options = False # Add the required methods now. methods |= required_methods rule = self.url_rule_class(rule, methods=methods, **options) rule.provide_automatic_options = provide_automatic_options self.url_map.add(rule) if view_func is not None: old_func = self.view_functions.get(endpoint) if old_func is not None and old_func != view_func: raise AssertionError('View function mapping is overwriting an ' 'existing endpoint function: %s' % endpoint) self.view_functions[endpoint] = view_func
参数解析:
- rule: 一个字符串格式的url规则,如:"/login". 注:就是从route方法中调用add_url_rule是传递的rule
- endpoint: url规则的名字,用来反向生成url使用,默认是视图函数的名字。注:如果是route方法调用的,则这个参数是route方法传递过来的endpont
- view_func: 视图函数,当对应的endpoint名字被请求时需要调用的函数。注:如果时router方法点用的add_url_rule,则这个参数时router方法传递过来的f
- options: 类似与分析route时候的options.这个options是跟随:class:`~werkzeug.routing.Rule` object定义的,后面会分析这个对象中的具体参数,但有一个methods参数默认是只监听get方法。注:如果是router方法点用的add_url_rule,则这个参数时router方法传递过来的options函数
函数体解析:
1 def add_url_rule(self, rule, endpoint=None, view_func=None, **options): 2 if endpoint is None: 3 # 如果没有提供endpoint参数,则默认用view_func的名字 4 endpoint = _endpoint_from_view_func(view_func) 5 # 把endpoint参数添加到options里面 6 options['endpoint'] = endpoint 7 # 从options中pop出methods参数,并把值赋给methods变量,如果没有则置为None 8 methods = options.pop('methods', None) 9 # moehods的值为None的情况下 10 if methods is None: 11 # 如果view_func函数中有这个methods参数,则使用view_func中的。如果没有则赋一个列表('GET',)给methods 12 methods = getattr(view_func, 'methods', None) or ('GET',) 13 # 如果methods是字符串类型 14 if isinstance(methods, string_types): 15 # 抛出一个异常:methods需要是一个可以迭代的字符串 16 raise TypeError('Allowed methods have to be iterables of strings, ' 17 'for example: @app.route(..., methods=["POST"])') 18 # 把methods里面的item都改成大写 19 methods = set(item.upper() for item in methods) 20 21 # 在view_func里面定义了一个属性required_methods = () 22 # 作用:用来定义一些必须的方法,配合provide_automatic_options使用 23 required_methods = set(getattr(view_func, 'required_methods', ())) 24 25 # starting with Flask 0.8 the view_func object can disable and 26 # force-enable the automatic options handling. 27 # 在view_func里面定义了一个属性provide_automatic_options = None 28 # 作用:用于禁用和强制启用一些自动选项 29 provide_automatic_options = getattr(view_func, 30 'provide_automatic_options', None) 31 32 # 判断provide_automati_options是否为None 33 if provide_automatic_options is None: 34 # 如果OPTIONS字符串没有在methods里面 35 if 'OPTIONS' not in methods: 36 # 则把provude_automatic_options改为True,并把OPTIONS添加到required_methods里面 37 provide_automatic_options = True 38 required_methods.add('OPTIONS') 39 # 如果OPTIONS在methods里面,则把provide_automatic_options设置为False 40 else: 41 provide_automatic_options = False 42 43 # 合并required_methods和methods这两个集合到methods里面 44 methods |= required_methods 45 46 # 创建路由规则 47 # 调用url_rule_class方法,由于在Flask类的全局变量中定义了:url_rule_class = Rule, Rule是werkzeug/routing.py里面的一个类 48 # 也就是相当于实例化了Rule得到了rule对象,具体实例化后的结果请看Rule源码分析 49 rule = self.url_rule_class(rule, methods=methods, **options) 50 # 把provide_automatic_options属性添加到rule对象里面 51 rule.provide_automatic_options = provide_automatic_options 52 53 # 在Flask类的__init__里面定义了self.url_map = Map(),Map是werkzeug/routing.py里面的一个类 54 # self.url_map相当与实例化了Map,.add则是调用了Map类里面的add方法 55 # 具体运行结果,请参考Map源码分析,以及Map源码中的add方法分析 56 self.url_map.add(rule) 57 # 如果提供了view_func 58 if view_func is not None: 59 # 在flask类的__init__里面定义了self.view_functions = {}, 60 # 从字典里面取endpoint值并赋值为old_func,(endpoint是传递的参数,默认为视图函数名) 61 old_func = self.view_functions.get(endpoint) 62 # 如果old_func有值,并且不等于view_func 63 if old_func is not None and old_func != view_func: 64 # 则抛出异常:视图函数映射被一个已经存在的函数名重写了 65 # 也就是说已经存在了一个endpoint:old_func的映射,但是old_fun却不是view_func,也就是说endpoint重复了 66 raise AssertionError('View function mapping is overwriting an ' 67 'existing endpoint function: %s' % endpoint) 68 # 添加视图函数与endpoint映射到view_functions字典里面 69 self.view_functions[endpoint] = view_func