1. 程式人生 > >flask基礎之app初始化(四)

flask基礎之app初始化(四)

templates 定靜 縮小 path 通訊協議 handle before rom fir

前言

flask的核心對象是Flask,它定義了flask框架對於http請求的整個處理邏輯。隨著服務器被啟動,app被創建並初始化,那麽具體的過程是這樣的呢?

系列文章

  • flask基礎之安裝和使用入門(一)

  • flask基礎之jijia2模板使用基礎(二)

  • flask基礎之jijia2模板語言進階(三)

一個最基本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初始化(四)