Flask源码关于修饰器修饰路由

来源:互联网 发布:linux 文件夹删除 编辑:程序博客网 时间:2024/06/06 05:57

装饰器route主要是为fun套了一层add_url_rule:

def decorator(f):            endpoint = options.pop('endpoint', None)            self.add_url_rule(rule, endpoint, f, **options)            return f        return decorator

add_url_rule函数主要负责工作是鉴定options,包括endpoint(默认是函数名,应该是后期机制中与url关联的函数名,在werkzeug中也相对应),methods默认处理为(‘GET’, ),然后将函数放入view_functions这个dict中,以备映射。最关键的部分还是使用了Rule这个类,它是werkzeug中关于路由管理的类,由这个类创建的路由规则将被放入werkzeug.routing中的Map

关于Rule:

A Rule represents one URL pattern. There are some options for Rule that change the way it behaves and are passed to the Rule constructor. Note that besides the rule-string all arguments must be keyword arguments in order to not break the application on Werkzeug upgrades.

关于Map:
根据werkzeug的编程来看对于路由的获取应该是Map中的bind_to_environ函数起到了作用,该函数文档解释如下:

Like :meth:bind but you can pass it an WSGI environment and it will fetch the information from that dictionary. Note that because of limitations in the protocol there is no way to get the current subdomain and real server_name from the environment. If you don’t provide it, Werkzeug will use SERVER_NAME and SERVER_PORT (or HTTP_HOST if provided) as used server_name with disabled subdomain feature.

If subdomain is None but an environment and a server name is provided it will calculate the current subdomain automatically.
Example: server_name is 'example.com' and the SERVER_NAME in the wsgi environ is 'staging.dev.example.com' the calculated subdomain will be 'staging.dev'.

If the object passed as environ has an environ attribute, the value of this attribute is used instead. This allows you to pass request objects. Additionally PATH_INFO added as a default of the :class:MapAdapter so that you don’t have to pass the path info to the match method.

这玩意又调用了bind函数,bind函数最后又返回了一个MapAdapter对象,最后werkzeug中应该调用该对象的match方法…后续不太清楚了,应该会涉及到run中的监听并调用之前view_functions里的方法。

下面基于werkzeug来实现一下它:

#!/usr/bin/env python# encoding: utf-8from werkzeug.wrappers import Request, Responsefrom werkzeug.routing import Map, Rulefrom werkzeug.exceptions import HTTPException, NotFounddef _endpoint_from_view_func(view_func):    """        返回函数名作为endpoint    """    assert view_func is not None, 'excepted view func if endpoint' \                                'is not provided.'    return view_func.__name__class TinyFlask(object):    """        造轮子!    """    request_class = Request    def __init__(self):        self.url_map = Map()        self.view_functions = {}    #函数与endpoint映射字典    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):        """            添加确定函数与url规则并相映射        """        if endpoint is None:            endpoint = _endpoint_from_view_func(view_func)        options['endpoint'] = endpoint        """            得到http方法        """        methods = options.pop('methods', None)        if methods is None:            methods = getattr(view_func, 'methods', None) or ('GET', )        if isinstance(methods, (str, unicode)):            raise TypeError('Allowed methods have to be iterables of strings, '                            'for example: @app.route(..., methods=["POST"])')        methods = set(item.upper() for item in methods)        #构造url规则        rule = Rule(rule, methods=methods, **options)        #向Map中添加该规则        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    def route(self, rule, **options):        """            装饰器来确定url规则        """        def decorator(fun):            """                装饰器            """            endpoint = options.pop('endpoint', None)            self.add_url_rule(rule, endpoint, fun, **options)            return fun        return decorator    def wsgi_app(self, environ, start_response):        """            此处创建WSGI应用        """        request = self.request_class(environ)        urls = self.url_map.bind_to_environ(environ)        try:            endpoint, args = urls.match()        except HTTPException, e:            return e(environ, start_response)        start_response('200 OK', [('Content-Type', 'text/plain')])        return self.view_functions[endpoint](**args)    def run(self, host='127.0.0.1', port=5000, debug=True, **options):        from werkzeug.serving import run_simple        self.debug = bool(debug)        options.setdefault('use_reloader', self.debug)        options.setdefault('use_debugger', self.debug)        run_simple(host, port, self, options)    def __call__(self, environ, start_response):        return self.wsgi_app(environ, start_response)app = TinyFlask()@app.route('/')def index():    return 'Hello, World'@app.route('/test/<int:year>')def test(year):    return 'Test' + str(year)if __name__ == '__main__':    app.run()

本来wsgi_app创建WSGI不会这么简单,Flask使用了RequestContext类来创建网页的上下文(里面包含session等),这里先偷懒做到这里,留WSGI这个东西下次再写。

0 0
原创粉丝点击