zoukankan      html  css  js  c++  java
  • 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 

  • 相关阅读:
    Maven项目打包时指定配置策略
    使Jackson和Mybatis支持JSR310标准
    Java 8的Time包常用API
    MySQL 聚集拼接
    将List<E>内对象按照某个字段排序
    判断List<E>内是否有重复对象
    eclipse中Maven项目启动报错“3 字节的 UTF-8 序列的字节 3 无效。”
    控制层@Value注解取不到值
    IntelliJ IDEA实时代码模板
    OD: Exploit Me
  • 原文地址:https://www.cnblogs.com/weihengblog/p/9490561.html
Copyright © 2011-2022 走看看