flask基础之app初始化(四)

前言

flask的核心对象是Flask,它定义了flask框架对于http请求的整个处理逻辑。随着服务器被启动,app被创建并初始化,那么具体的过程是这样的呢?

系列文章

一个最基本web应用的flask代码

from flask import Flask
import os

# 创建app对象
app = Flask(__name__,template_folder=‘static/html‘)
# 加载配置文件
app.config[‘SECRET_KEY‘] = ‘123‘
# 加载蓝图
app.register_blueprint(rest, url_prefix=‘‘)

# 加载请求钩子
@rest.before_request
def rest_test():
    print(‘this is a test‘)
    pass

# 定义api处理视图
@app.route(‘/test1‘)
def test():
    return ‘OK‘

@定义错误处理逻辑
@app.errorhandler(400)
def handle_errer(errer):
    print(errer)
    return errer

if __name__ == ‘__main__‘:
    app.run(host=‘127.0.0.1‘, port=80, debug=True)

如上所示,一个完整的web应用所拥有的最基本的东西就这些了,我们一步步分析。

第一步:创建Flask对象

创建一个Flask的app对象作为备用是一切的开端,看一下初始化干了什么?

class Flask(_PackageBoundObject):
    request_class = Request # 指定请求对象
    response_class = Response # 指定响应对象
    jinja_environment = Environment # 指定前端模板语言环境
    app_ctx_globals_class = _AppCtxGlobals # 设置app全局对象,本质是一个字典
    request_globals_class = property(_get_request_globals_class, # 设置请求上下文全局对象,其等于app_ctx_globals_class
                                        _set_request_globals_class)
    config_class = Config # 设置参数配置对象
    debug = ConfigAttribute(‘DEBUG‘) # 配置文件中如果DEBUG=True就启用debug模式
    testing = ConfigAttribute(‘TESTING‘) # 配置文件中如果TESTING=True就启用TESTING模式
    secret_key = ConfigAttribute(‘SECRET_KEY‘) # 配置文件中如果SECRET_KEY=‘xx’就启用了密匙,默认是不启用的
    # 配置文件中如果SESSION_COOKIE_NAME=‘xx’就启用了在cookie中session信息的名字,默认是session
    session_cookie_name = ConfigAttribute(‘SESSION_COOKIE_NAME‘)

    # 配置文件中如果PERMANENT_SESSION_LIFETIM=‘xx’就启用了session信息的失效时间,默认是31天,即一个月
    permanent_session_lifetime = ConfigAttribute(‘PERMANENT_SESSION_LIFETIME‘,
                                                    get_converter=_make_timedelta)

    # 设置发送文件功能最大的缓存超时时间,默认为12小时
    send_file_max_age_default = ConfigAttribute(‘SEND_FILE_MAX_AGE_DEFAULT‘,
                                                get_converter=_make_timedelta)

    # 通过LOGGER_NAME指定日志对象的名称
    logger_name = ConfigAttribute(‘LOGGER_NAME‘)

    # 启用json格式的编解码
    json_encoder = json.JSONEncoder
    json_decoder = json.JSONDecoder

    # 设置默认jinja模板环境
    jinja_options = ImmutableDict(
        extensions=[‘jinja2.ext.autoescape‘, ‘jinja2.ext.with_‘]
    )

    # 常用默认的配置集合
    default_config = ImmutableDict({
        ‘DEBUG‘: get_debug_flag(default=False), # 默认不开启DEBUG模式
        ‘TESTING‘: False, # 默认不开启TESTING模式
        ‘SECRET_KEY‘: None, # 默认没有密匙
        ‘PERMANENT_SESSION_LIFETIME‘: timedelta(days=31), # 默认session失效时间一个月
        ‘USE_X_SENDFILE‘: False, # 不启用X_SENDFILE功能
        ‘LOGGER_NAME‘: None, # 不指定logger名字,使用的是__name__
        ‘LOGGER_HANDLER_POLICY‘: ‘always‘,
        ‘SERVER_NAME‘: None, # 设置服务器的名字,默认None
        ‘SESSION_COOKIE_NAME‘: ‘session‘, # 设置cookie中的session名字
        ‘MAX_CONTENT_LENGTH‘: None, # 限制提交请求的最大字节数
        ‘SEND_FILE_MAX_AGE_DEFAULT‘: timedelta(hours=12), # 设置文件最大缓存时间
        ‘PREFERRED_URL_SCHEME‘: ‘http‘, # 默认的通讯协议
        ‘JSONIFY_MIMETYPE‘: ‘application/json‘, # 当json数据交互时,设置响应头的mimetype参数
    })

    url_rule_class = Rule # 指定路由对象管理路由
    session_interface = SecureCookieSessionInterface() # 指定session会话对象
    def __init__(self,
                    import_name, # 指定app的名字
                    static_path=None,  # 会赋值给static_url_path参数,所以一般设置static_url_path而不是这个
                    static_url_path=None, # 设置静态文件路由的前缀,默认为“/static”,即static_folder的路径
                    static_folder=‘static‘, # 指定静态文件是哪个目录
                    template_folder=‘templates‘,# 模板文件的存放目录,默认值为"templates"
                    instance_path=None, # 设置配置文件的路径,在instance_relative_config=True情况下生效
                    instance_relative_config=False, # 设置为True表示配置文件相对于实例路径而不是根路径
                    root_path=None): # app所在的根路径,默认指的是创建app这个代码的文件的目录。

        # 所有定义的视图函数存放字典,以视图函数的标识符为键,视图函数对象为字典;
        self.view_functions = {}

        # 所有自定义的错误处理方法的存放字典,以蓝图名字为键
        self.error_handler_spec = {None: self._error_handlers}

        # 所有的与url错误处理方法存放列表
        self.url_build_error_handlers = []

        # 所有的http请求处理前的请求钩子方法存放字典
        self.before_request_funcs = {}

        # 第一次请求处理前的请求钩子方法存放字典
        self.before_first_request_funcs = []

        # 所有的http请求处理后的请求钩子方法存放字典
        self.after_request_funcs = {}

        # 在处理即使存在异常的情况下的请求处理后的请求钩子方法存放字典
        self.teardown_request_funcs = {}

        # 应用上下文被弹出之前的处理函数存放点
        self.teardown_appcontext_funcs = []

        # url_value_preprocessor装饰器添加,URL路径预提取
        self.url_value_preprocessors = {} 

        # url_defaults装饰器回调添加的,在生成url时设置规则参数默认值
        self.url_default_functions = {} 

        self.blueprints = {} # 所有的蓝图的保存字典

        self.extensions = {} # 所有的flask的扩展对象的存放字典

        self.url_map = Map() # 所有的Rule对象保存的字典

        # 添加访问静态文件的Rule对象
        if self.has_static_folder:
            self.add_url_rule(self.static_url_path + ‘/<path:filename>‘,
                                endpoint=‘static‘,
                                view_func=self.send_static_file)

初始化传入相关的参数后,会执行上述代码生成一个app对象。

第二步:加载配置文件

加载配置文件有多种方式,现在只说例子中的情况:

app.config[‘SECRET_KEY‘] = ‘123‘

app的config对象其实是Config实例,是一个dict类型的子类,也就是说app的配置是通过一个字典来保存的,新加载的配置如果键和原来默认的配置的键相同将更新,否则添加。

class Config(dict):
    pass

详细的参数配置方式参考: flask之安装和使用入门

第三步:加载蓝图

app的register_blueprint方法就是用来加载蓝图的。

app.register_blueprint(rest, url_prefix=‘‘)

# 源码
def register_blueprint(self, blueprint, **options):
        ......
        self.blueprints[blueprint.name] = blueprint
        self._blueprint_order.append(blueprint)
    blueprint.register(self, options, first_registration)

如上,在app的blueprints和_blueprint_order属性中添加蓝图对象,同时蓝图调用register方法初始化。

第四步:加载请求钩子

请求钩子常用的有五种,它们通过装饰器的方式添加到app相应的存储字典中。

  • before_first_request:在处理第一个请求前运行。

  • before_request:在每次请求前运行。

  • after_request:如果没有未处理的异常抛出,在每次请求后运行。

  • teardown_request:在每次请求后运行,即使有未处理的异常抛出。

  • teardown_appcontext:在每次请求结束后应用上下文被弹出时执行,即appcontext调用pop方法;

@app.before_request
def test():
    pass

第五步:加载视图函数

app通过route装饰器加载视图函数,将标识符和视图函数作为键值对加入app的view_functions字典属性中。

@app.route(‘/test3‘, endpoint=‘mytest‘)
def test3():
    return ‘ok‘

route装饰器调用了app.add_url_rule(rule, endpoint, f, **options)方法,主要源码步骤为:

def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    ...
    rule = self.url_rule_class(rule, methods=methods, **options) # 创建一个Rule对象
    。。。
    self.url_map.add(rule) # 在app的url_map属性即Map对象中加入rule对象
    。。。
    self.view_functions[endpoint] = view_func # 在app的view_functions属性中加入标识符和视图函数的键值对

第六步:加载错误处理方法

app通过errorhandler装饰器加载对应的错误或状态码相应的处理方法。

@app.errorhandler(400)
def handle_errer(errer):
    print(errer)
    return errer

# 其主要源码
def _register_error_handler(self, key, code_or_exception, f):
    # 可以传入状态码或我们自定义的异常
    exc_class, code = self._get_exc_class_and_code(code_or_exception)
    handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
    handlers[exc_class] = f

总结

  • flask框架的核心对象有:
    Flask,AppContext,Map, RequestContext,Request,Response,Rule,Blueprint,session,_AppCtxGlobals;

  • Flask主要管理着AppContext,RequestContext,以及所有的请求的处理步骤;

  • AppContext为应用上下文对象,主要管理app对象的获取和请求的临时状态存储变量g;

  • RequestContext为请求上下文,主要管理Request对象和保存会话的session对象;

  • Request为请求对象,在发生请求是随着RequestContext被创建而创建,管理着请求数据和Rule对象;

  • Response为响应对象,在应用逻辑处理完毕后创建,管理着响应数据和响应方法;

  • Rule路由对象,管理着路由和视图处理函数的标识符的对应关系;

  • Map对象管理着所有的标识符和其视图函数对象的一一对应关系;

  • Blueprint对象,Flask对象的缩小版,主要是为了对大量的视图函数做分类管理;

  • session为保存会话信息的容器,可以看做一个字典;

  • _AppCtxGlobals即g变量,用来在每次请求中临时存储资源,它属于应用上下文的一个属性。

参考:

  • https://dormousehole.readthedocs.io/en/latest/config.html

  • http://www.pythondoc.com/flask/config.html

flask基础之app初始化(四)

上一篇:学JS的心路历程-函式(四)apply、call


下一篇:Vuex以及axios