官方demo示例
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
第一行import Flask 類對象,這個無需解釋。跳到第二行,使用當前模塊的名字傳入Flask類中,並實例化Flask對象,我們在這個地方打個斷點,看看Flask類別里有什么。
上圖可以看出,Flask類中定義jinia_options、request_class、response_class等屬性,這里我們不關系具體作用,先看看源碼中Flask 是不是定義了這些屬性。
class Flask(object): # 省略了注釋部分 # flask 用作請求對象的類 request_class = Request # flask 用作響應對象的類 response_class = Response # 靜態文件路徑 static_path = '/static' # 密鑰,用於加密 session 或其它涉及安全的東西 secret_key = None #存儲session對象數據的cookie名稱 session_cookie_name = 'session' # Jinja2環境的一些選項 jinja_options = dict( autoescape=True, extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] )
這部分是初始化Flask類中默認設置的一些屬性,其實通過名字也可以大概知道每個屬性的作用。看到這個地方時是不是一臉懵逼,Request、Response 是什么東西,有什么作用?Jinja2 又是什么東西? 別急,下面慢慢解釋這幾個東西的作用。
Request && Response
from werkzeug import Request as RequestBase, Response as ResponseBase class Request(RequestBase): """The request object used by default in flask. Remembers the matched endpoint and view arguments. It is what ends up as :class:`~flask.request`. If you want to replace the request object used you can subclass this and set :attr:`~flask.Flask.request_class` to your subclass. """ def __init__(self, environ): RequestBase.__init__(self, environ) self.endpoint = None # 請求對象的端點 self.view_args = None # 請求視圖函數的參數 class Response(ResponseBase): """The response object that is used by default in flask. Works like the response object from Werkzeug but is set to have a HTML mimetype by default. Quite often you don't have to create this object yourself because :meth:`~flask.Flask.make_response` will take care of that for you. If you want to replace the response object used you can subclass this and set :attr:`~flask.Flask.request_class` to your subclass. """ default_mimetype = 'text/html'
通過源碼的注釋我們可以知道,Request、Response都只是對 werkzeug 庫的Request、Response 進行了一層包裝並加入一些屬性。先說一下它們的作用:
- Request 的作用:是 flask 默認的請求對象,用來記住匹配的endpoint(端點)和view arguments(視圖參數)
- Response 的作用:是 flask 默認的響應對象,默認設置MIME類型默認設置為HTML(即是定義了內容類型 Content-Type 返回的類型為HTML), 默認情況下,你不用自己創建這個對象,因為下面的 make_response 函數會幫你處理。
看完上面源碼和解釋,是不是有新的疑問了,werkzeug又是什么?端點又是什么概念?額,werkzeug的作用真的很大,整個框架都是基於它實現的,下面會有一個部分專門說明這個庫。說明: werkzeug 庫和 jinja2 是 flask 的兩個依賴庫,會分出一篇文章專門介紹,這篇文章重點是整個 Flask 內部的機制,建議看到對應部分,先提前去讀兩個依賴庫的文章。
接下來繼續下一步的調試,初始化一個Flask類, 先看看 Flask 類的初始化函數:
def _get_package_path(name): """Returns the path to a package or cwd if that cannot be found.""" # 獲取 模塊包 路徑,被 Flask 引用 try: return os.path.abspath(os.path.dirname(sys.modules[name].__file__)) except (KeyError, AttributeError): return os.getcwd() class Flask(object): # 簡化版,已經去掉注釋,建議看源碼注釋加上這個理解 def __init__(self, package_name): # 設置是否開啟調試模式,若開啟,會監視項目代碼變化, # 開發服務器重載 Flask 應用 self.debug = False # 包或模塊的名字,模塊的名稱將會因其作為單獨應用啟動還是作為模塊導入而不同 # Flask 才知道到哪去找模板、靜態文件 self.package_name = package_name # 根據 Flask 傳入的__name__, 找到項目的根路徑 self.root_path = _get_package_path(self.package_name) # 已注冊的所有視圖函數的字典,字典的鍵是函數名稱,可以用來生成URL(url_for函數) # 字典的值是函數本身, 想要注冊視圖函數,可以使用 route 裝飾器 self.view_functions = {} # 所有已注冊錯誤處理程序的字典, 字典的鍵是一個整數類型(integer)的錯誤碼 # 字典的值是對應錯誤的函數,想要注冊錯誤handler, 可以使用 errorhandler 裝飾器 self.error_handlers = {} # 請求開始進入時,但還請求還沒調度前調用的函數列表,也就是預處理操作 # 可用於打開數據庫連接或獲取當前登錄用戶,使用 before_route 裝飾器注冊 self.before_request_funcs = [] # 請求結束時調用的函數列表,這些函數會被傳入當前響應對象並將其修改或替換它。 self.after_request_funcs = [] # 不帶參數調用的函數列表,用於填充模板上下文,每個應該返回更新模板上下文的字典 # 默認的處理器用來注入session、request和g self.template_context_processors = [_default_template_ctx_processor] # 使用 werkzeug 的 routing.Map, 用於給應用增加一些URL規則, # URL規則形成一個Map實例的過程中會生成對應的正則表達式,可以進行URL匹配 self.url_map = Map() # 添加靜態文件的URL映射規則 # SharedDataMiddleware中間件用來為程序添加處理靜態文件的能力 if self.static_path is not None: self.url_map.add(Rule(self.static_path + '/<filename>', build_only=True, endpoint='static')) if pkg_resources is not None: target = (self.package_name, 'static') else: target = os.path.join(self.root_path, 'static') self.wsgi_app = SharedDataMiddleware(self.wsgi_app, { self.static_path: target # URL路徑和實際文件目錄(static文件夾)的映射 }) # Jinja2 環境,它通過jinja_options創建,加載器(loader)通過 self.jinja_env = Environment(loader=self.create_jinja_loader(), **self.jinja_options) # 將url_for, get_flashed_message 作為全局對象填充入模板上下文中,可以在模板中調用它們 self.jinja_env.globals.update( url_for=url_for, get_flashed_messages=get_flashed_messages )
上面就是一個 Flask 實例化時所做的工作,其實就是保存了一下配置信息,設置了一下Jinja2 環境,並定義了一個URL 映射對象,用於映射URL 到函數之間的關系。
總結
開篇主要講了初始化一個Flask對象,內部做了什么工作,配置了一下信息,設置了一下Jinja2 環境,定義了一些視圖函數存放的數據結構,定義了一個Map對象用於后面保存URL 和 視圖函數的映射關系。接下來會有更多關於werkzeug, jinja2 和 WSGI 相關文章放出來,敬請期待!!!