程序员

flask源码笔记(2) 最简单的flask app分析

2017-10-08  本文已影响137人  kuulid

github地址:包括所有实例的代码 https://github.com/kurset/learn_flask_code
不定期更新。


下面我们尝试分析一下一个最简单的flask应用都发生了什么

from flask import Flask

app = Flask(__name__)

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

if __name__ == '__main__':
    app.run()

大致这段程序flask要做四件事情

  1. 初始化Flask的对象
  2. 建立url和对应的视图处理函数
  3. 启动一个flask的开发服务器
  4. 解析请求url并调用相应的视图处理函数

初始化Flask的对象

在源码中,Flask类位于app.py中,是最重要的类,大约有两千多行代码,先来看看__init__方法

 def __init__(self, import_name, static_path=None, static_url_path=None,
                 static_folder='static', template_folder='templates',
                 instance_path=None, instance_relative_config=False,
                 root_path=None):

基本上只需要传入一个模块名,其他的一些设置项都有自己的默认值。但这个方法里面初始化有些数据很重要,比如

self.view_functions = {}
self.url_map = Map()

大概也能看出来,这分别保存了Flask对象的url数据结构和视图函数。

建立url和对应的视图处理函数

Flask是如何建立url和视图处理函数呢
当然是找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

就是取出endpoint的设置项,并进行add_url_rule的操作。
那么endpoint是什么呢,我们可以把它看做是url_map和view_function对应的key值,通过这个值,把url和视图函数联系起来。但一般我们并没有设置这个值,在add_url_rule函数中,我们可以看到Flask调用了_endpoint_from_view_func方法,实际上endpoint取的是view_func.name
接下来,在add_url_rule方法中可以看到

rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options

self.url_map.add(rule)

url_rule_class是Flask封装的一个Rule的Class

url_rule_class = Rule

新初始化一个rule的class并加入到url_map中。

启动一个flask的开发服务器

这个说法并不准确,因为当我们看Flask类中run()方法时候,可以看到Flask的开发服务器实际上是werkzeug提供的。

# run方法中 简化代码
from werkzeug.serving import run_simple
run_simple(host, port, self, **options)

解析请求url并调用相应的视图处理函数

这是一个比较麻烦的过程,先从Flask的wsgi_app看起,从wsgi中接到environ基本的请求信息,Flask首先保存请求信息并压入请求栈中,这个细节先不去探究

ctx = self.request_context(environ)
ctx.push()

之后调用full_dispatch_request方法,处理请求。
在full_dispatch_request里调用full_dispatch_request方法

    def dispatch_request(self):
        req = _request_ctx_stack.top.request
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        rule = req.url_rule
        # if we provide automatic options for this URL and the
        # request came with the OPTIONS method, reply automatically
        if getattr(rule, 'provide_automatic_options', False) \
           and req.method == 'OPTIONS':
            return self.make_default_options_response()
        # otherwise dispatch to the handler for that endpoint
        return self.view_functions[rule.endpoint](**req.view_args)

可以看到先从当前的请求上下文中找到request信息,之后在request信息中保留url的endpoint,再从view_functions里面根据endpoint找到视图函数,并返回执行结果。
full_dispatch_request最后会调用一个finalize_request方法,将结果包装成response_class,最终返回给用户。

    def finalize_request(self, rv, from_error_handler=False):
        response = self.make_response(rv)
        try:
            response = self.process_response(response)
            request_finished.send(self, response=response)
        except Exception:
            if not from_error_handler:
                raise
            self.logger.exception('Request finalizing failed with an '
                                  'error while handling an error')
        return response
上一篇 下一篇

猜你喜欢

热点阅读