flask源码浅析——路由

本flask源码分析不间断更新

而且我分析的源码全是我个人觉得是很漂亮的

1 flask-login

1.1 flask.ext.login.login_required(func),下面是它的文档的官方源码

 1 def login_required(func):
 2     '''
 3     If you decorate a view with this, it will ensure that the current user is
 4     logged in and authenticated before calling the actual view. (If they are
 5     not, it calls the :attr:`LoginManager.unauthorized` callback.) For
 6     example::
 7 
 8         @app.route('/post')
 9         @login_required
10         def post():
11             pass
12 
13     If there are only certain times you need to require that your user is
14     logged in, you can do so with::
15 
16         if not current_user.is_authenticated:
17             return current_app.login_manager.unauthorized()
18 
19     ...which is essentially the code that this function adds to your views.
20 
21     It can be convenient to globally turn off authentication when unit testing.
22     To enable this, if the application configuration variable `LOGIN_DISABLED`
23     is set to `True`, this decorator will be ignored.
24 
25     :param func: The view function to decorate.
26     :type func: function
27     '''
28     @wraps(func)
29     def decorated_view(*args, **kwargs):
30         if current_app.login_manager._login_disabled:
31             return func(*args, **kwargs)
32         elif not current_user.is_authenticated:
33             return current_app.login_manager.unauthorized()
34         return func(*args, **kwargs)
35     return decorated_view
View Code

其实这个login_required方法和我最开始认为的一样,就是用一个装饰器包裹着func,里面有一个对用户是否login有个判断,最后返回func。

1.1.1 根据官方文档可以看出,这个方法有两种使用方式

  第一种是对装饰的func,也就是最常用的方式。

  第二种是仅仅使用一次,用一次判断是否已authorize,要没有进行authorize。

1.1.2 下面具体分析

  首先这个login_required的方法的基本方式就是:用装饰器包裹func,里面进行一次authorize,current_app.login_manager.unauthorized()。

  然后说一下主要逻辑:如果用户login了,但没authorize,就进行authorize,如果没有login就返回原func。

  其中current_app.login_manager._login_disabled这个函数的目的是:判断全局变量中LOGIN_DISABLE是否为真,而这个函数的目的是控制这个装饰器是否生效。

2 flask的route方法,基于flask0.1最初版本

到目前还没有看过werkzeug的源码,所以有些不足以后补充

首先是route方法的源码:

    def route(self, rule, **options):
        """A decorator that is used to register a view function for a
        given URL rule.  Example::

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

        Variables parts in the route can be specified with angular
        brackets (``/user/<username>``).  By default a variable part
        in the URL accepts any string without a slash however a different
        converter can be specified as well by using ``<converter:name>``.

        Variable parts are passed to the view function as keyword
        arguments.

        The following converters are possible:

        =========== ===========================================
        `int`       accepts integers
        `float`     like `int` but for floating point values
        `path`      like the default but also accepts slashes
        =========== ===========================================

        Here some examples::

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

            @app.route('/<username>')
            def show_user(username):
                pass

            @app.route('/post/<int:post_id>')
            def show_post(post_id):
                pass

        An important detail to keep in mind is how Flask deals with trailing
        slashes.  The idea is to keep each URL unique so the following rules
        apply:

        1. If a rule ends with a slash and is requested without a slash
           by the user, the user is automatically redirected to the same
           page with a trailing slash attached.
        2. If a rule does not end with a trailing slash and the user request
           the page with a trailing slash, a 404 not found is raised.

        This is consistent with how web servers deal with static files.  This
        also makes it possible to use relative link targets safely.

        The :meth:`route` decorator accepts a couple of other arguments
        as well:

        :param rule: the URL rule as string
        :param methods: a list of methods this rule should be limited
                        to (``GET``, ``POST`` etc.).  By default a rule
                        just listens for ``GET`` (and implicitly ``HEAD``).
        :param subdomain: specifies the rule for the subdoain in case
                          subdomain matching is in use.
        :param strict_slashes: can be used to disable the strict slashes
                               setting for this rule.  See above.
        :param options: other options to be forwarded to the underlying
                        :class:`~werkzeug.routing.Rule` object.
        """
        def decorator(f):
            self.add_url_rule(rule, f.__name__, **options)
            self.view_functions[f.__name__] = f
            return f
        return decorator
View Code

注释部分主要是使用方法不过多说明

2.1.1 分析装饰器

  它的装饰器用的很巧妙,装饰器的wraper传入的是参数,而inner传入的是func, 所以在inner函数内部会返回func

2.1.2 内部主要方法

  add_url_rule(rule, f.__name__, **options) 这个方法主要是将route的路由和option进行注册

  view_functions[f.__name__] 就是在view_function字典中添加这个route的函数名

2.2 add_url_rule 方法

说道注册路由就不得不提这个方法了,首先展示一下源码

 1     def add_url_rule(self, rule, endpoint, **options):
 2         """Connects a URL rule.  Works exactly like the :meth:`route`
 3         decorator but does not register the view function for the endpoint.
 4 
 5         Basically this example::
 6 
 7             @app.route('/')
 8             def index():
 9                 pass
10 
11         Is equivalent to the following::
12 
13             def index():
14                 pass
15             app.add_url_rule('index', '/')
16             app.view_functions['index'] = index
17 
18         :param rule: the URL rule as string
19         :param endpoint: the endpoint for the registered URL rule.  Flask
20                          itself assumes the name of the view function as
21                          endpoint
22         :param options: the options to be forwarded to the underlying
23                         :class:`~werkzeug.routing.Rule` object
24         """
25         options['endpoint'] = endpoint
26         options.setdefault('methods', ('GET',))
27         self.url_map.add(Rule(rule, **options))
View Code

注释也不过多解释

它的主要工作就是将route的rule和endpoint建立关系。通过 url_map.add(Rule(rule, **options)) 方法。

2.3 url_map

实际上是flask的和核心库 werkzeug 自己定义的一个存储url路径的‘仓库’,

werkzeug 最核心的路由功能:添加路由规则(也可以使用 m.add),把路由表绑定到特定的环境(m.bind),匹配url(urls.match)。正常情况下返回对应的 endpoint 名字和参数字典,可能报重定向或者 404 异常。

其实是 url 到 endpoint 的转换:通过 url 找到处理该 url 的 endpoint。至于 endpoint 和 view function 之间的匹配关系,werkzeug 是不管的

2.4 dispatch_request

def dispatch_request(self):
    """Does the request dispatching.  Matches the URL and returns the
    return value of the view or error handler.  This does not have to
    be a response object.  In order to convert the return value to a
    proper response object, call :func:`make_response`.
    """
 
    req = _request_ctx_stack.top.request
    if req.routing_exception is not None:
        self.raise_routing_exception(req)
    rule = req.url_rule
 
    # dispatch to the handler for that endpoint
    return self.view_functions[rule.endpoint](**req.view_args)
View Code

这个方法做的事情就是找到请求对象 request,获取它的 endpoint,然后从 view_functions 找到对应 endpoint 的 view_func ,把请求参数传递过去,进行处理并返回。

补 flask路由规则

  • 通过 @app.route 或者 app.add_url_rule 注册应用 url 对应的处理函数
  • 每次请求过来的时候,会事先调用路由匹配的逻辑,把路由结果保存起来
  • dispatch_request 根据保存的路由结果,调用对应的视图函数

最后分享来自segementfault的_Zhao的 文章, 他的路由思想和我理解的想法差不多。

----补 2017/2/22----

介绍一个搭建RESTful API 之 实现WSGI服务的URL映射,也是类似于router源代码的介绍,说的很不错。

原文地址:https://www.cnblogs.com/fuzzier/p/6229006.html