7 张图搞懂 Flask 请求上下文

请求上文

1. 请求上下文的必要性

  1. 当 Flask 应用处理经过 WSGI 处理好的请求数据时,将 environ 封装成 request 对象。
  2. 为了区分不同请求的请求对象,无非就是只有两种(我只知道两种) 方法,全局变量和使用 Context locals(Werkzeug提供) 实现
  3. 很明显Flask请求上下文使用的是第二种方式,当然你可能会有疑问,使用全局变量的方式不是更简单便捷吗?
  4. 俗话说得好,一个硬币有两面。使用全局变量可以实现没毛病,那就意味着来多少的请求就需要定义多少个全局变量(细思极恐),而且我们都知道全局变量在程序中应用时很“危险的”,谁都可以访问,也就会导致数据不安全的情况出现等等等问题。
  5. 使用第二种方式就可以完美解决使用全局变量的问题,直到请求处理完毕,Flask 会 pop 掉处理完的请求上下文栈,保证了内存不会溢出。有的同学可能还会提出万一 Flask 应用接收到请求后,服务异常怎么办?Flask 也对此进行进行了相应的处理。

2. 源码剖析

特别说明

  1. 图片中标注的红色数字表示 falsk 应用执行顺序
  2. 红色标注代笔请求上文
  3. 黄色标注代表请求下文

1. 请求上文

  • 当请求到来时,Flask 会自动把封装 requst、view functions...的 RequstContext 对象 push 进 Local 对象中
  • 当请求数据封装到 environ 后,WSGI 会调用 Flask 实例的 __call__ 方法,即处理请求

  • 我们可以看到,environ 就是请求数据

  • RequestContext 类实例化时,会对请求数据 environ 进行封装成 request 对象(就是Flask 导入的request),由app.request_class(environ),主要是对请求数据的格式化,并初始化request对象
  • 我们可以很清晰的看到,request 在 RequestContext 实例化的过程中被封装进对象中了

  • 因为在 Local 类中重写了 __setattr__ 方法,所以在实例化的过程中使用父类的 __setattr__ 方法

2. 请求下文

  • 当我们使用 request.method 时,请求下文就开始发挥作用了
  • 首先需要导入 from flask import request

  • 这里用到了 functools中的 partial() 函数,主要功能有点类似于装饰器,只要参数传递可以分开传递,也就是说可以多次传递,从底层讲可以维护一块内存空间,用于存储变量值
  • 可以看到下图类初始化时,使用了_LocalProxy__local,这里使用的是私有变量的外部访问方式,私有变量在外部访问不到,本质就是在变量名前加上了 _类名__私有变量名的形式。

  • 当请求处理结束,返回 reponse 给客户端后,Flask通过 信号机制调用flask.reqeust_tearing_downflask.appcontext_tearing_down等信号,把当前的request数据销毁,整个请求结束。

3. Flask中内置信号

# 模板渲染成功的时候发送,这个信号与模板实例template上下文的字典一起调用。
flask.template_rendered
# 建立请求上下文后,在请求处理开始前发送,订阅者可以用request之类的标准代理访问请求。
flask.request_started
# 在响应发送给客户端之前发送,可以传递reponse。
flask.request_finished
# 在请求处理中抛出异常时发送,异常本身会通过execption传递到订阅函数。
flask.got_request_exception
# 在请求销毁时发送,它总是被调用,即使发生异常。
flask.request_tearing_down
# 在应用上下文销毁时发送,它总是被调用,即使发生异常。
flask.appcontext_tearing_down

References:https://flask.palletsprojects.com/en/1.1.x/reqcontext/

原文地址:https://www.cnblogs.com/henryw/p/11575311.html