【flask】03-源码解读-路由routing

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return 'index'
    
if __name__ == "__main__":
    app.run(debug=True)

对于route路由的工作原理和工作流程,进入源码进行探究。
flask的路由管理主要是依赖于werkzeug的

  • Rule类

     用来构造不同的URL模式的对象,路由URL规则
    
  • Map类

     存储所有的URL规则和一些配置参数
    
  • BaseConverter的子类

     负责定义匹配规则
    
  • MapAdapter类

     负责协调Rule做具体的匹配的工作
    

下面看具体如何实现

第一步:
点开

@app.route('/')
def index():
    return 'index'

源码是

def route(self, rule, **options):
        """A decorator that is used to register a view function for a
        given URL rule.  This does the same thing as :meth:`add_url_rule`
        but is intended for decorator usage::
            @app.route('/')
            def index():
                return 'Hello World'
       一个用于注册视图函数的装饰器,用于装饰
       @app.route('/')
            def index():
                return 'Hello World'       
        """
        def decorator(f):
        	# f --> 视图函数
            endpoint = options.pop('endpoint', None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator

通过装饰器将url传入到add_url_rule当中
其中rule,methods以**options关键字参数传入到add_url_rule中

本质是调用add_url_rule(rule, endpoint, f, **options)函数

第二步:
点开add_url_rule(rule, endpoint, f, **options)

@setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
        """Connects a URL rule.  Works exactly like the :meth:`route`
        decorator.  If a view_func is provided it will be registered with the
        endpoint.

        Basically this example::

            @app.route('/')
            def index():
                pass

        Is equivalent to the following::

            def index():
                pass
            app.add_url_rule('/', 'index', index)
		定义连接url的规则
		1.
		@app.route('/')
			def index():
			pass
		2.
		def index():
			pass
		app.add_url_rule('/', 'index', index)
        """
        methods = options.pop('methods', None)
 
	    rule = self.url_rule_class(rule, methods=methods, **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 verwriting an '
	                                 'existing endpoint function: %s' % endpoint)
        self.view_functions[endpoint] = view_func

这个函数主要做的事情就是更新 self.url_map 和 self.view_functions 两个变量,将rule、methods、options传入Rule类构造出一个rule对象,将该rule对象添加到url_map当中.其中 options[‘endpoint’]=endpoint, rule=url . 通过routing.py的Rule类建立了(url,endpoint,methods)的映射关系

继续找,url_maprouting.py的Map类创建出来的一个对象,用来存储Rule的对象,rulewerkzeug.routing:Rule类的对象

Rule类中定义
url_map = Map([                
			Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
			Rule('/all/page/<int:page>', endpoint='all_entries')
			])

add_url_rule的view_func就是app.route构造器修饰的视图函数,app.py的Flask类定义了view_functions为一个空字典,将view_func存入view_functions中,建立了{endpoint:view_func}的映射关系

Flask的url_map存储了所有的(url,endpoint)映射关系;
Flask的view_functions存储了所有的{endpoint:view_func}映射关系;

至此,url与视图函数就通过endpoint映射起来了,请求时服务器对请求进行转发,解析得到url,通过MapAdapter类的match方法得到endpoint,再通过view_functions找到对应的视图函数,直接调用函数执行,就完成了一个完整的route.

def match(self, path_info=None, method=None, return_rule=False,
              query_args=None):
        """The usage is simple: you just pass the match method the current
        path info as well as the method (which defaults to `GET`). 
        
        Here is a small example for matching:

        >>> m = Map([
        ...     Rule('/', endpoint='index'),
        ...     Rule('/downloads/', endpoint='downloads/index'),
        ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
        ... ])  
        """
           for rule in self.map._rules:
            try:
                rv = rule.match(path, method)
            except RequestSlash:
                raise RequestRedirect(self.make_redirect_url(
                    url_quote(path_info, self.map.charset,
                              safe='/:|+') + '/', query_args))
            except RequestAliasRedirect as e:
                raise RequestRedirect(self.make_alias_redirect_url(
                    path, rule.endpoint, e.matched_values, method, query_args))
            if rv is None:
                continue
            if rule.methods is not None and method not in rule.methods:
                have_match_for.update(rule.methods)
                continue

            if self.map.redirect_defaults:
                redirect_url = self.get_default_redirect(rule, method, rv,
                                                         query_args)
                if redirect_url is not None:
                    raise RequestRedirect(redirect_url)

            if rule.redirect_to is not None:
                if isinstance(rule.redirect_to, string_types):
                    def _handle_match(match):
                        value = rv[match.group(1)]
                        return rule._converters[match.group(1)].to_url(value)
                    redirect_url = _simple_rule_re.sub(_handle_match,
                                                       rule.redirect_to)
                else:
                    redirect_url = rule.redirect_to(self, **rv)
                raise RequestRedirect(str(url_join('%s://%s%s%s' % (
                    self.url_scheme or 'http',
                    self.subdomain and self.subdomain + '.' or '',
                    self.server_name,
                    self.script_name
                ), redirect_url)))

            if return_rule:
                return rule, rv
            else:
                return rule.endpoint, rv

        if have_match_for:
            raise MethodNotAllowed(valid_methods=list(have_match_for))
        raise NotFound()

通过 url 找到处理该 url 的 endpoint

总结:

路由实现的流程:

  1. 调用函数add_url_rule() ,将rule、methods、options传入Rule类构造出一个rule对象,建立映射关系
  2. 将该rule对象添加到url_map当中,
    view_functions存储了所有的{endpoint:view_func}映射关系,
    此时url与视图函数就通过endpoint映射起来了

路由的本质是:通过 url 找到处理该 url 的 endpoint

  1. 最后,通过MapAdapter类的match方法得到endpoint,再通过view_functions找到对应的视图函数,直接调用函数执行,就完成了一个完整的route.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值