flask项目源码_flask源码分析(1)路由系统

4f02c955367d9e179ad37400bb7564c8.png

框架只是为了简化开发,框架的目的很简单,也就是为了向程序员隐藏HTTP请求和相应的相关代码,让程序员专注于业务逻辑的开发。

flask主要实现了一个Web应用所需要的最小功能

Django则包括了Web应用的大部分功能

我们着重对Flask源码进行分析,通过对于其“简化开发工作”这个需求入手,分析代码为什么要这么实现,最终对Flask全部功能进行总结以及自己动手实现。

阅读本文需要有一下两点要求:

  1. 了解基本的Python语法,熟悉装饰器
  2. 有使用过Flask进行开发

首先,我们要明白,Flask是基于Werkzeug这个框架进行开发的。

Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等。。

本篇文章着重分析Flask是如何实现的,而不会过多分析Werkzeug的实现方式,Werkzeug中的实现放在后面进行。

我们先放上一个简单的Werkzeug的demo。

import os
from werkzeug.wrappers import Request, Response
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException
from werkzeug.middleware.shared_data import SharedDataMiddleware
​
​
​
class Shortly:
​
    def __init__(self):
        self.url_map = Map([
            Rule('/', endpoint='new_url'),
        ])
​
    def on_new_url(self, request):
        print(request.path)
        result = [request.path]
        return Response(''.join(result))
​
    def dispatch_request(self, request):
        adapter = self.url_map.bind_to_environ(request.environ)
        try:
            endpoint, values = adapter.match()
            return getattr(self, 'on_' + endpoint)(request, **values)
        except HTTPException as e:
            return e
​
    def wsgi_app(self, environ, start_response):
        request = Request(environ)
        response = self.dispatch_request(request)
        return response(environ, start_response)
​
    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)
​
​
def create_app(redis_host='localhost', redis_port=6379, with_static=True):
    app = Shortly({
            'redis_host': redis_host,
            'redis_port': redis_port,
        })
    if with_static:
        app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
            '/static': os.path.join(os.path.dirname(__file__), 'static')
        })
    return app
​
​
if __name__ == '__main__':
    from werkzeug.serving import run_simple
    app = create_app()
    run_simple('127.0.0.1', 5000, app, use_debugger=True, use_reloader=True)

首先我们要理解两个点

  1. werkzeug中使用Map对象和Rule对象进行路由分发
  2. 调用run_simple启动一个werkzeug项目

更多用法可以参考werkzeug官方文档

https://werkzeug-docs-cn.readthedocs.io/zh_CN/latest/quickstart.html#response

我们把目光转回Flask项目中来。

一下代码基于Flask 0.1版本源码进行分析

1- 启动一个Flask项目会发生什么?

当我们执行app.run的时候

实际会执行Flask的run方法。

class Flask:
    #  省略掉一些函数,着重关注run方法
    def run(self, host='localhost', port=5000, **options):
        """在本地开发服务器上运行程序。如果debug标志被设置,这个服务器
        会在代码更改时自动重载,并会在异常发生时显示一个调试器。
        
        :param host: 监听的主机名。设为'0.0.0.0'可以让服务器外部可见。
        :param port: 服务器的端口。
        :param options: 这些选项将被转发给底层的Werkzeug服务器。更多信息
                        参见werkzeug.run_simple。
        """
        from werkzeug import run_simple
        if 'debug' in options:
            self.debug = options.pop('debug')
        options.setdefault('use_reloader', self.debug)
        options.setdefault('use_debugger', self.debug)
        return run_simple(host, port, self, **options)

也就是说,执行的还是werkzeug的run_simple方法,用来监听一个端口。

2- Flask是怎么将路由分发到对应业务逻辑函数?

Flask在进行路由分发的时候主要运用了三个函数,以及werkzeug的Map对象

class MyFlask:
    # 省略掉一些函数,着重关注路由分发
​
    def __init__(self, package_name):
​
        self.debug = False
​
        # 包或模具哎的名称
        self.package_name = package_name
​
        # 定位程序的根目录
        self.root_path = _get_package_path(package_name)
​
        # 一个存储所有已经注册的视图函数字典,
        # 要注册一个视图函数,就使用route装饰器
        self.view_functions = {}
​
        # 存储所有应该被转发的路由
        self.url_map = Map()
​
    def add_url_rule(self, rule, endpoint, **options):
        # 将目前所有的路由加入werkzeug的Map里,用于路由分发
        options[endpoint] = endpoint
        options.setdefault('methods', ('GET', ))
        self.url_map.add(Rule(rule, **options))
​
    def route(self, rule, **options):
        # 语法糖,方便使用装饰器来注册路由,用法如下
        def decorator(f):
            self.add_url_rule(rule, f.__name__, **options)
            self.view_functions[f.__name__] = f
            return f
        return decorator

当我们在Flask项目中增加URL以及业务逻辑函数的时候

@app.route('/index', methods=['GET', 'POST'])
def team(timestamp):
    return render_template("index.html")

实际会调用Flask.route方法,注册路由 装饰器的语法不在叙述,from werkzeug.routing import Map, Rule

  1. 调用Flask.add_url_url方法,将我们装饰器参数中的rule(也就是URL)和method构成一个Rule对象,然后添加到Map对象中。
  2. 将函数名称以及函数放入self.view_functions属性中。

这样做的优势在于解决了两个问题

  1. 不用把过多函数都放在我们的对象中,方便项目管理
  2. 使用装饰器模式,能将URL和业务函数写在一起,有助于提高开发效率。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值