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


WSGI

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

Web應用程序的本質就是:

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

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

Werkzeug

Werkzeug是Python的WSGI規范的實用函數庫。Flask使用的底層WSGI庫就是Werkzeug。

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

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

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

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

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執行獲取響應結果。

要調用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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM