• Flask依赖和启动流程回顾


    flask 有两个核心依赖库:werkzeug 和 jinja,而 werkzeug 又是两者中更核心的。

    werkzeug

    werkzeug 负责核心的逻辑模块,比如路由、请求和应答的封装、WSGI 相关的函数等
    werkzeug 的定位并不是一个 web 框架,而是HTTP 和 WSGI相关的工具集,可以用来编写 web 框架,也可以直接使用它提供的一些帮助函数。

    Werkzeug is an HTTP and WSGI utility library for Python.

    werkzeug 提供了 python web WSGI 开发相关的功能:

    • 路由处理:怎么根据请求中的 url 找到它的处理函数
    • request 和 response 封装:可以更好地读取 request 的数据,也容易生成响应
    • 一个自带的 WSGI server,可以用来测试环境运行自己的应用

    比如,我们可以使用 werkzeug 编写一个简单的 hello world 的 WSGI app:

    from werkzeug.wrappers import Request, Response
    
    def application(environ, start_response):
        request = Request(environ)
        text = 'Hello %s!' % request.args.get('name', 'World')
        response = Response(text, mimetype='text/plain')
        return response(environ, start_response)
    

    除了和 web WSGI 相关的功能,werkzeug 还实现了很多非常有用的数据结构和函数。比如用来处理一个 key 对应多个值的 MultiDict,不支持修改的字典ImmutableDict,可以缓存类属性的 cache_property 等等

    jinjia

    Jinja 功能比较丰富,支持 unicode 解析、自动 HTML escape 防止 XSS 攻击、继承、变量、过滤器、流程逻辑支持、python 代码逻辑集成等等。具体的功能和使用请参考官网的文档,这里就不介绍了

    flask 应用启动流程

    WSGI
    WSGI 中有一个非常重要的概念:每个 python web 应用都是一个可调用(callable)的对象。在 flask 中,这个对象就是 app = Flask(__name__) 创建出来的 app,就是下图中的绿色 Application 部分。要运行 web 应用,必须有 web server,比如我们熟悉的 apachenginx ,或者 python 中的 gunicorn ,我们下面要讲到的 werkzeug 提供的 WSGIServer,它们是下图的黄色 Server 部分。
    在这里插入图片描述
    ServerApplication 之间怎么通信,就是 WSGI 的功能。它规定了 app(environ, start_response) 的接口,server 会调用 application,并传给它两个参数:environ 包含了请求的所有信息,start_response application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息

    WSGI application 非常重要的特点是:它是可以嵌套的。换句话说,我可以写个 application,它做的事情就是调用另外一个 application,然后再返回(类似一个 proxy)。一般来说,嵌套的最后一层是业务应用,中间就是 middleware。这样的好处是,可以解耦业务逻辑和其他功能,比如限流、认证、序列化等都实现成不同的中间层,不同的中间层和业务逻辑是不相关的,可以独立维护;而且用户也可以动态地组合不同的中间层来满足不同的需求

    应用启动 app.run()

    def run(self, host=None, port=None, debug=None, **options):
        """Runs the application on a local development server."""
        from werkzeug.serving import run_simple
    
        # 如果host 和 port 没有指定,设置 host 和 port 的默认值 127.0.0.1 和 5000
        if host is None:
            host = '127.0.0.1'
        if port is None:
            server_name = self.config['SERVER_NAME']
            if server_name and ':' in server_name:
                port = int(server_name.rsplit(':', 1)[1])
            else:
                port = 5000
    
        # 调用 werkzeug.serving 模块的 run_simple 函数,传入收到的参数
        # 注意第三个参数传进去的是 self,也就是要执行的 web application
        try:
            run_simple(host, port, self, **options)
        finally:
            self._got_first_request = False
    

    这个方法的内容非常简单:处理一下参数,然后调用 werkzeug 的 run_simple。需要注意的是:run_simple 的第三个参数是 self,也就是我们创建的 Flask() applicationWSGI server 监听在指定的端口,收到 HTTP 请求的时候解析为 WSGI 格式,然后调用 app 去执行处理的逻辑。对应的执行逻辑在 werkzeug.serving:WSGIRequestHandlerrun_wsgi 中有这么一段代码:

    def execute(app):
    	#执行可调用的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
    

    要调用 app 实例,那么它就需要定义了 call 方法,我们找到 flask.app:Flask 对应的内容

    def __call__(self, environ, start_response):
        """Shortcut for :attr:`wsgi_app`."""
        return self.wsgi_app(environ, start_response)
    
    def wsgi_app(self, environ, start_response):
        """The actual WSGI application.
        """
        # 创建请求上下文,并把它压栈。
        ctx = self.request_context(environ)
        ctx.push()
        error = None
    
        try:
            try:
                # 正确的请求处理路径,会通过路由找到对应的处理函数
                response = self.full_dispatch_request()
            except Exception as e:
                # 错误处理,默认是 InternalServerError 错误处理函数,客户端会看到服务器 500 异常
                error = e
                response = self.handle_exception(e)
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            # 不管处理是否发生异常,都需要把栈中的请求 pop 出来
            ctx.auto_pop(error)
    

    继续往后看,full_dsipatch_request 的代码如下:

    def full_dispatch_request(self):
        """Dispatches the request and on top of that performs request
        pre and postprocessing as well as HTTP exception catching and
        error handling.
        """
        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)
    

    这段代码最核心的内容是 dispatch_request,加上请求的 hooks 处理和错误处理的内容。
    self.dispatch_request() 返回的是处理函数的返回结果(比如 hello world 例子中返回的字符串),finalize_request 会把它转换成 Response 对象

    在 dispatch_request 之前我们看到 reprocess_request,之后看到 finalize_request,它们里面包括了请求处理之前和处理之后的很多 hooks 。这些 hooks 包括:

    • 第一次请求处理之前的 hook 函数,通过 before_first_request定义
    • 每个请求处理之前的 hook 函数,通过 before_request 定义
    • 每个请求正常处理之后的 hook 函数,通过 after_request 定义
    • 不管请求是否异常都要执行的 teardown_request hook 函数
      dispatch_request 要做的就是找到我们的处理函数,并返回调用的结果,也就是路由的过程
        def dispatch_request(self):
            ......省略.....
            return self.view_functions[rule.endpoint](**req.view_args)
    
  • 相关阅读:
    CF786E ALT
    CF704D Captain America
    [NOI2016]循环之美
    「PKUWC2018」猎人杀
    [HNOI2019]JOJO
    博客已转移
    $20200203$的数学作业
    20200202的数学作业
    NOIp 2016 选课 (DP)
    Luogu P2574 XOR的艺术 (线段树)
  • 原文地址:https://www.cnblogs.com/donghaoblogs/p/10619819.html
Copyright © 2020-2023  润新知