Flask源码解析:Flask应用执行流程及原理
阅读原文时间:2024年06月19日阅读:1

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是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:WSGIRequestHandler中execute()。

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:WSGIRequestHandler的execute()中:

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