Flask源碼解析:Flask應用執行流程及原理

WSGI

WSGI:全稱是Web Server Gateway InterfaceWSGI不是服務器,python模塊,框架,API或者任何軟件,只是一種規範,描述服務器端如何與web應用程序通訊的規範。python

Web應用程序的本質就是:web

  1. 瀏覽器向服務器發送請求
  2. 服務器接受客戶端請求,並解析
  3. 服務器端把HTML做爲響應體發送給瀏覽器
  4. 瀏覽器拿取響應體渲染網頁

在客戶端和服務器端WSGI負責協議的轉化,WSGI將web組件分爲三部分:Web服務器、Web中間件、Web應用程序,當服務器接受到HTTP請求時,會按照WSGI協議解析成Request對象並調用WSGI Application,最後將響應返回給瀏覽器。flask

Werkzeug

Werkzeug是Python的WSGI規範的實用函數庫。Flask使用的底層WSGI庫就是Werkzeug。瀏覽器

WSGI 中有一個很是重要的概念:每一個 Python web 應用都是一個可調用(callable)的對象。在 Flask 中,這個對象就是 app = Flask(__name__) 建立出來的 app,就是圖中綠色部分。服務器

要運行Web應用,必須依賴於Web Server,好比咱們常見的Apache、Nginx、Lighttpd以及咱們Flask使用的Werkzug位於黃色部分。app

WSGI規定了server和app之間如何進行通訊,它規定了app(environ, start_response)接口,environ是環境設置的字典,包含了請求的全部信息,start_response是WSGI處理完畢後調用的函數。框架

源碼位置:werkzeug.serving:WSGIRequestHandlerexecute()ide

HelloWorld啓動流程

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello World"

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

應用啓動的代碼: app.run()函數

def run(self, host=None, port=None, debug=None,
        load_dotenv=True, **options):

    """
    ... 部分代碼省略
    """
    _host = '127.0.0.1'
    _port = 5000
    server_name = self.config.get('SERVER_NAME')
    sn_host, sn_port = None, None

    if server_name:
        sn_host, _, sn_port = server_name.partition(':')

    host = host or sn_host or _host
    port = int(port or sn_port or _port)

    from werkzeug.serving import run_simple

    try:
        # 導入werkzeug.serving的run_simple(),傳入接受到的參數
        # 注意:第三個參數是self: 就是咱們建立的Flask app
        run_simple(host, port, self, **options)
    finally:
        self._got_first_request = False
 
run_simple(host, port, self, **options)

監聽指定的IP和端口,當接受到請求時,WSGI會解析,而後調用app去執行請求處理的邏輯。對應的邏輯在werkzeug.serving:WSGIRequestHandlerexecute()中:
def execute(app):
    # 調用代碼獲取結果
    application_iter = app(environ, start_response)
    try:
        for data in application_iter:
            write(data)
        if not headers_sent:
            write(b'')
    finally:
        if hasattr(application_iter, 'close'):
            application_iter.close()
        application_iter = None
 

能夠看到application_iter = app(environ, start_response),調用app執行獲取響應結果。url

要調用app實例,那麼會調用其__call__()方法。flask.app:Flask

def __call__(self, environ, start_response):
    """The WSGI server calls the Flask application object as the
    WSGI application. This calls :meth:`wsgi_app` which can be
    wrapped to applying middleware."""
    return self.wsgi_app(environ, start_response)


def wsgi_app(self, environ, start_response):

    # 下篇博客講 Flask上下文會解釋,先忽略
    ctx = self.request_context(environ)
    error = None
    try:
        try:
            ctx.push()

            # 正確的請求路徑,會經過路由分發到響應的視圖處理函數
            response = self.full_dispatch_request()
        except Exception as e:
            error = e
            response = self.handle_exception(e)
        except:
            error = sys.exc_info()[1]
            raise
        return response(environ, start_response)
    finally:
        if self.should_ignore_error(error):
            error = None

        # 無論處理是否發生異常,都須要把棧中的請求 pop 出來
        ctx.auto_pop(error)

上面代碼業務邏輯就是經過路由配置,找到具體處理業務的視圖函數。full_dispatch_request()相關代碼:

def full_dispatch_request(self):
    """
    分派請求並在此之上執行請求處理
    """
    self.try_trigger_before_first_request_functions()
    try:
        request_started.send(self)
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
    return self.finalize_request(rv)

在這段中核心就是 self.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 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)

self.dispatch_request() 返回的是處理函數的返回結果(好比Hello World 例子中返回的字符串),finalize_request 會把它轉換成 Response 對象。

在 dispatch_request 以前咱們看到 preprocess_request,以後看到 finalize_request,它們裏面包括了請求處理以前和處理以後的不少 hooks:

before_first_request 、before_request、 after_request 、teardown_request 

相關文章
相關標籤/搜索