flask中的上下文分为请求上写文和应用上下文,接下来,从以下三个大的方面分别探讨flask的两大上下文管理机制。

  1. 方面一:请求进来时
  2. 方面二:视图函数
  3. 方面三:请求结束前

先来一个最简单的flask版的Hello World

from flask import Flaskapp = Flask(__name__)@app.route('/')
def index():return "Hello World"if __name__ == '__main__':app.run()Flask版Hello World

flask版的Hello World

启动一个flask项目时,会先执行app.run()方法,这是整个项目的入口,执行run方法时,接着执行werkzeug模块中的run_simple

此时的self指的是我们实例化的Flask对象,即app,当请求到来时,会触发调用Flask的__call__方法。

一、请求进来时

触发执行__call__方法,__call__方法的逻辑很简单,直接执行wsgi_app方法,将包含所有请求相关数据和一个响应函数传进去。

此时的self指的是Flask的实例化对象app,接下来执行app的wsgi_app方法。

此时的self仍然指的是Flask的实例化对象app,首先执行app这个对象的request_context的方法,将environ传进去,最后返回一个RequestContext类的对象,对象中封装了request和session等实例变量。ctx=RequestContext()

RequestContext类在实例化过程中执行__init__方法,赋了两个非常有用的属性,一个是request,一个是session

这两个属性中request是一个Request()对象,这个对象就是我们在flask中使用的request对象,为我们提供了很多便捷的属性和方法,比如:request.method、request.form、request.args等等,另一个属性是session,初始为None。

紧接着执行ctx.push()方法,此时的self为ctx

这个方法中,在执行请求上下文对象ctx之前先执行的app的app_context()方法,返回值为AppContext()的实例化对象,即app_ctx = AppContext(),先执了AppContext()的push方法,然后才执行_request_ctx_stack对象中的top和_request_ctx_stack.push(self),最后对ctx中的session进行处理。

所以,flask中的应用上下文发生在请求上下文之前

但是我们先说请求上下文,在处理完应用上下文的push方法后,紧接着执行了_request_ctx_stack对象的两个方法。

而这个_request_ctx_stack是LocalStack这个类的对象。_request_ctx_stack = LocalStack()

LocalStack有没有很眼熟,没错,flask内部使用的机制就是类似于我们上文中自定义的LocalStack的机制,实例化过程中使用了面向对象中的组合概念,self._local = Local(),然后在自身又实现了push、pop、top方法,这三个方法中都是通过反射从Local类的实例化对象中对一个stack属性进行append、pop、[-1]的操作,所以,Local对象中的stack属性对应的值一定是一个类似于列表的东西。通过对列表的操作,实现一个类似于栈的存取。

接下来我们开始执行LocalStack()对象的push方法

在此方法中执行了self._local.stack = rv =[ ],self._local为Local类的实例化对象,Local类在实例化的过程中,会对每个对象生成__storage__和__indent_func__两个实例变量,__storage__对应的是一个空字典{},__ident_func__对应的一个获取当前线程ID的一个可执行函数get_ident。

我们翻遍整个Local类的源码,发现内部并没有实现一个叫stack的方法或者属性,但是上面我们提到了LocalStack对象会对Local对象中的一个叫stack的东西进行一系列操作。找不到不会报错吗?

这就是flask的巧妙之处,通过类的一些魔法方法巧妙的实现了相应的处理。如果对象中没有某个属性,取值时会执行类中的__getattr__方法,赋值时会执行类中的__setattr__方法。

处理完_request_ctx_stack后,就该处理session了。

  在flask中,处理session时,非常的巧妙,完美的遵循了开闭原则,会先执行session_interface对象的open_session方法,在这个方法中,会先从用户请求的cookie中获取sessionid,获取该用户之前设置的session值,然后将值赋值到ctx.session中。

  处理完session后,ctx.push方法就执行完了,返回到最开始的app.wsgi_app方法中,执行完push方法后,接着执行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)      full_dispath_request方法

full_dispatch_request

在full_dispatch_request方法中先执行preprocess_request方法,这个方法,会先执行所有被before_request装饰器装饰的函数,然后就通过路由的分发执行视图函数了(dispatch_request)

二、执行视图函数时

在执行视图函数之前,先执行了before_request,在执行我们的视图函数。

视图函数主要处理业务逻辑。在视图函数中可以调用request对象,进行取值,也可以调用session对象对session的存取。

在整个request的请求生命周期中,获取请求的数据直接调用request即可,对session进行操作直接调用session即可。request和session都是LocalProxy对象,借助偏函数的概念将对应的值传入_lookup_req_object函数。先从_request_ctx_stack(LocalStack)对象中获取ctx(请求上下文对象),再通过反射分别获取request和session属性。整个过程中LocalStack扮演了一个全局仓库的角色,请求进来将数据存取,需要时即去即用。所以,flask实现了在整个请求的生命周期中哪儿需要就直接调用的特色。

request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))

三、请求结束前

视图函数执行完后,dispatch_request执行结束,执行full_dispatch_request方法的返回值finalize_request方法。这个方法中,同样的,在返回响应之前,先执行所有被after_request装饰器装饰的函数。

---->finalize_request ------> process_response
def process_response(self, response):ctx = _request_ctx_stack.topbp = ctx.request.blueprintfuncs = ctx._after_request_functionsif bp is not None and bp in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[bp]))if None in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[None]))for handler in funcs:response = handler(response)if not self.session_interface.is_null_session(ctx.session):self.session_interface.save_session(self, ctx.session, response)return responseprocess_response

执行process_response过程中,执行完after_request后,然后,执行session的save_session方法。将内存中保存在ctx.session的值取到后,json.dumps()序列化后,写入响应的cookie中(set_cookie),最后返回响应。

def save_session(self, app, session, response):domain = self.get_cookie_domain(app)path = self.get_cookie_path(app)# If the session is modified to be empty, remove the cookie.# If the session is empty, return without setting the cookie.if not session:if session.modified:response.delete_cookie(app.session_cookie_name,domain=domain,path=path)return# Add a "Vary: Cookie" header if the session was accessed at all.if session.accessed:response.vary.add('Cookie')if not self.should_set_cookie(app, session):returnhttponly = self.get_cookie_httponly(app)secure = self.get_cookie_secure(app)samesite = self.get_cookie_samesite(app)expires = self.get_expiration_time(app, session)val = self.get_signing_serializer(app).dumps(dict(session))# set_cookie将session写入响应的cookie中
        response.set_cookie(app.session_cookie_name,val,expires=expires,httponly=httponly,domain=domain,path=path,secure=secure,samesite=samesite)save_session

返回响应后,自动的调用ctx.auto_pop(error),将Local中存储的ctx对象pop掉,整个请求结束。

四、应用上下文

与请求上下文类似,当请求进来时,先实例化一个AppContext对象app_ctx,在实例化的过程中,提供了两个有用的属性,一个是app,一个是g。self.app就是传入的全局的app对象,self.g是一个全局的存储值的对象。接着将这个app_ctx存放到LocalStack()。

class AppContext(object):def __init__(self, app):self.app = appself.url_adapter = app.create_url_adapter(None)self.g = app.app_ctx_globals_class()

视图函数中,我们就可以调用app对象和g对象,如果我们使用蓝图构建我们的项目时,在每一个直接引用app就会造成循环引用的异常,这时,应用上下文就会显得非常有用,我们可以直接调用current_app就可以在整个生命周期中使用我们的app对象了。比如使用我们的配置项:current_app.config

current_app = LocalProxy(_find_app)

最后,当视图函数执行结束后,从storage中pop掉app_ctx对象。

总结:

流程:请求到来:- 请求到来之后wsgi会触发__call__方法,由__call__方法再次调用wsgi_app方法,将request和session相关封装到ctx = RequestContext对象中。将app和g封装到app_ctx = AppContext对象中。再通过LocalStack对象将ctx、app_ctx封装到Local对象中。获取数据:通过LocalProxy对象+偏函数,调用LocalStack去Local中获取响应ctx、app_ctx中封装的值。请求结束:调用LocalStack的pop方法,将ctx和app_ctx移除。

转载于:https://www.cnblogs.com/fengchong/p/10256552.html

flask上下文管理机制相关推荐

  1. Flask的上下文管理机制

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  2. flask 上下文管理

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  3. Flask 上下文管理-- (session,request,current_app的传递)--类似本地线程实现,以及多app应用...

    Flask session,request,current_app的传递 请求上下文的作用 -- 封装请求相关得数据(request,session) 请求上下文requestsessionreque ...

  4. flask总结之session,websocket,上下文管理

    1.关于session flask是带有session的,它加密后存储在用户浏览器的cookie中,可以通过app.seesion_interface源码查看 from flask import Fl ...

  5. python sqlite3事务_python使用上下文管理器实现sqlite3事务机制

    如题,本文记录如何使用python上下文管理器的方式管理sqlite3的句柄创建和释放以及事务机制. 1.python上下文管理(with) python上下文管理(context),解决的是这样一类 ...

  6. Python 21 Flask(二)上下文管理详解

    上下文管理 对于上下文管理我没有找到明确的定义,但是经过源码流程的学习后,我觉得所谓的上下文管理应该就是Flask对请求和应用相关数据的一种处理方式,它不是像Django一样通过参数的传导,而是创建了 ...

  7. Flask入门(一)之消息闪现、请求扩展、中间件、蓝图、上下文管理

    上一篇文章>Flask安装及入门操作 一.消息闪现(flash) 1.使用 #!/usr/bin/env python # coding:utf-8 from flask import Flas ...

  8. flask项目源码_源码解读:Flask上下文与代理模式

    在上一节中,我跟大家一起深入了解了一下Python的「上下文管理器 」.而今天呢,还是和上下文有关的话题.只不过这里的上下文和上一节的内容有点不一样,上下文管理器是管理代码块级别的上下文,而今天要讲的 ...

  9. Mylyn 2.0,第 2 部分: 自动上下文管理

    解决信息超载并简化多任务处理 Mik Kersten, Mylyn 项目主管:总裁 & CTO, Tasktop Technologies 简介: 现在,在 2.0 版中,Mylyn(以前称为 ...

最新文章

  1. mysql 临时列_如何在MySQL中列出临时表列?
  2. osi 模型 tcpip网络模型
  3. 域控制器配置系统要求
  4. idea git 提交代码
  5. Dropout的前世与今生
  6. 并发编程——线程——CPython的GIL解释器锁
  7. 加速你的Hibernate引擎(上)
  8. java 示例_功能Java示例 第5部分–将I / O移到外部
  9. php 登录安全认证,介绍几种常用的web安全认证方式
  10. 《2018区块链整体架构及应用》(PPT全文)
  11. json 来实现 php 与 javascript,用 Json 来实现 PHP 与 JavaScript 间数据交换
  12. 解决虚拟机克隆后eth0不见的问题
  13. ymodem传输的终端工具_Serial for Mac(现代化的终端设计软件)
  14. C++ STL unordered_map按照value排序
  15. 删除web项目服务器,删除 Tomcat-webapps 目录自带项目【测试可行】
  16. 【游戏开发实战】Unity UGUI序列帧动画(蓝胖子序列帧图)
  17. 2022.08.09-docker容器网络配置-左冕
  18. 微信小程序之各类文件下载保存到本地
  19. 单层感知器神经网络matlab,MATLAB神经网络——单层感知器
  20. react--1.react环境搭建、JSX语法、注释、样式、列表渲染、定义单个组件、eact Props、react State

热门文章

  1. 使用Markdown
  2. Rake::TestTask 介绍
  3. Geolocation :基于浏览器的定位服务
  4. 2008R2Win7管理二十六ADRMS客户端使用及侦错
  5. 单例模式(singleton)解析例子
  6. 云计算与springCloud概念上的区别
  7. colab把数据放在content下面以及放在drive下面的训练速度比较
  8. gitkraken把github上的东西clone到本地
  9. 机器学习(三十六)——Integrating Learning and Planning(2)
  10. 深度学习(三十一)——Style Transfer