Flask源码解析

两个核心依赖

falsk主要依赖两个库 —— Werkzeug 和 Jinja。

Jinja2

由于大多数Web程序都需要渲染模板,与Jinja2集成可以减少大量的工作。此处不展开讨论。

Werkzeug

Flask的核心扩展就是Werkzeug。

python Web框架都需要处理WSGI交互,它是为了让Web服务器与python程序能够进行数据交流而定义的一套接口标准/规范。而Werkzeug是一个优秀的WSGI工具库。

HTTP请求 -》 WSGI规定的数据格式 -》 Web程序

从路由处理,到请求解析,再到响应封装,以及上下文和各种数据结构都离不开Werkzeug。

WSGI程序

根据WSGI的规定,Web程序(WSGI程序)必须是一个可调用对象。这个可调用对象接收两个参数:

  • environ:包含了请求的所有信息的字典。
  • start_response:需要在可调用对象中调用的函数,用来发起响应,参数是状态码,响应头部等

WSGI服务器会在调用这个可调用对象时传入这两个参数。另外这个可调用对象还要返回一个可迭代对象。

这个可调用对象可以是函数、方法、类或是实现了__call__方法的类实例。

以下借助简单的实例来了解最主要的两种实现:函数和类

# 函数实现

# 可调用对象    接收两个参数
def hello(environ, start_response):
    # 响应信息
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    # 需要在可调用函数中调用的函数
    start_response(status, response_headers)
    # 返回可迭代对象
    return [b'<h1>Hello</h1>']

注:WSGI规定请求和响应主体应该为字符串(bytestrings),即py2中的str。在py3中字符串默认为unicode类型,因此需要在字符串前添加b声明为bytes类型,兼容两者

# 类实现

class AppClass:
    
    def __init__(self, environ, start_response):
        self.environ = environ
        self.statr = start_response
    
    # iter方法,这个类被迭代时,调用这个方法
    # 实现该方法的类就是迭代器
    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/html')]
        self.start(status, response_headers)
        yield b'<h1>Hello</h1>'
werkzeug中如何实现Web程序

由于flask是基于werkzeug实现的,所以先了解以下werkzeug是如何实现一个简单的web程序

from werkzeug.wrappers import Request, Response

@Request.application
def hello(request):
    return Response('hello')

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 5000, hello)

通过以上代码,使用run_simple规定了ip、端口号、调用对象

路由是怎么设定的?

Werkzeug怎么实现路由系统

# 路由表
m = Map()
rule1 = Rule('/', endpoint='index')
rule2 = Rule('/downloads/', endpoint='downloads/index')
m.add(rule1)
m.add(rule2)

Flask的路由系统

Flask使用中的路由系统,是通过route() 装饰器来将视图函数注册为路由。进入route函数

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

    return decorator

可见内部调用了add_url_rule,并将函数作为参数传入。看到add_url_rule存在关键的语句

# url_map实际上就是Map类的实例
# rule就是通过route相关更正成的Rule实例
self.url_map.add(rule)

# view_functions是一个字典,存储了端点和视图函数的映射关系。可用于查询
self.view_functions[endpoint] = view_func

再进入底层就会发现,实际上就同上例的werkzeug实现

导入config配置参数

最初,我们修改配置文件会使用以下方法

app.config['DEGUB'] = True

导入参数

import config
app.config.from_object(config)

# 在config.py 文件中 存放配置参数
DEBUG = True
SECRET_KEY = os.urandom(24)
DIALECT = 'mysql'
DRIVER = 'mysqlconnector'
USERNAME = 'root'
PASSWORD = 'root'
HOST = '127.0.0.1'
PORT = '3306'
DATABASE = 'test'

如果自定义了配置文件类也可传入字符串

app.config.from_object('config.Foo')
# 以上代表 config.py文件中的 Foo类

进入from_object() 函数 [位于config.py]

    def from_object(self, obj):
        if isinstance(obj, string_types):
            obj = import_string(obj)
        for key in dir(obj):
            if key.isupper():
                self[key] = getattr(obj, key)

首先判断如果是字符串类型的,做相应处理获得对象。在import_string函数中

module_name, obj_name = import_name.rsplit(".", 1)
module = __import__(module_name, globals(), locals(), [obj_name])

dir()函数的作用:

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;
带参数时,返回参数的属性、方法列表。
如果参数包含方法__dir__(),该方法将被调用。
如果参数不包含__dir__(),该方法将最大限度地收集参数信息。

获取属性后判断是否为大写,是则添加为配置参数

用类导入配置的作用

在开发和线上,往往采用的不是相同的配置文件。我们可以通过类封装几套配置文件以供使用。

可以编写一个基础类,在开发测试、线上运行都相同、都需要的配置参数。再通过继承,扩展不同环境下的不同配置参数。

则在不同的环境下,只需要改变from_object() 中的参数即可。

Flask如何处理请求

app程序对象

在一些Python web框架中,视图函数类似

@route('/')
def index():
    return 'hello'

但在flask中

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

flask 中存在一个显式的程序对象,我们需要在全局空间中创建它。设计原因主要包括:

  • 相较于隐式程序对象,同一时间只能有一个实例存在,显式的程序对象允许多个程序实例存在。
  • 允许通过子类化Flask类来改变程序行为。
  • 允许通过工厂函数来创建程序实例,可以在不同的地方传入不同的配置来创建不同的程序实例。
  • 允许通过蓝本来模块化程序。

启动app.run()

在Flask类中

当调用app.run(),程序启动。我们查看run()函数的源码

from werkzeug.serving import run_simple

try:
    run_simple(host, port, self, **options)
finally:
    # reset the first request information if the development server
    # reset normally.  This makes it possible to restart the server
    # without reloader and that stuff from an interactive shell.
    self._got_first_request = False

可见run_simple函数,而第三个参数是self,即flask对象。

当调用对象时,python会执行__call__方法。

进入Flask() 类可以看到

    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exce
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值