flask上下文管理机制
flask中的上下文分为请求上写文和应用上下文,接下来,从以下三个大的方面分别探讨flask的两大上下文管理机制。
- 方面一:请求进来时
- 方面二:视图函数
- 方面三:请求结束前
先来一个最简单的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上下文管理机制相关推荐
- Flask的上下文管理机制
前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...
- flask 上下文管理
前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...
- Flask 上下文管理-- (session,request,current_app的传递)--类似本地线程实现,以及多app应用...
Flask session,request,current_app的传递 请求上下文的作用 -- 封装请求相关得数据(request,session) 请求上下文requestsessionreque ...
- flask总结之session,websocket,上下文管理
1.关于session flask是带有session的,它加密后存储在用户浏览器的cookie中,可以通过app.seesion_interface源码查看 from flask import Fl ...
- python sqlite3事务_python使用上下文管理器实现sqlite3事务机制
如题,本文记录如何使用python上下文管理器的方式管理sqlite3的句柄创建和释放以及事务机制. 1.python上下文管理(with) python上下文管理(context),解决的是这样一类 ...
- Python 21 Flask(二)上下文管理详解
上下文管理 对于上下文管理我没有找到明确的定义,但是经过源码流程的学习后,我觉得所谓的上下文管理应该就是Flask对请求和应用相关数据的一种处理方式,它不是像Django一样通过参数的传导,而是创建了 ...
- Flask入门(一)之消息闪现、请求扩展、中间件、蓝图、上下文管理
上一篇文章>Flask安装及入门操作 一.消息闪现(flash) 1.使用 #!/usr/bin/env python # coding:utf-8 from flask import Flas ...
- flask项目源码_源码解读:Flask上下文与代理模式
在上一节中,我跟大家一起深入了解了一下Python的「上下文管理器 」.而今天呢,还是和上下文有关的话题.只不过这里的上下文和上一节的内容有点不一样,上下文管理器是管理代码块级别的上下文,而今天要讲的 ...
- Mylyn 2.0,第 2 部分: 自动上下文管理
解决信息超载并简化多任务处理 Mik Kersten, Mylyn 项目主管:总裁 & CTO, Tasktop Technologies 简介: 现在,在 2.0 版中,Mylyn(以前称为 ...
最新文章
- mysql 临时列_如何在MySQL中列出临时表列?
- osi 模型 tcpip网络模型
- 域控制器配置系统要求
- idea git 提交代码
- Dropout的前世与今生
- 并发编程——线程——CPython的GIL解释器锁
- 加速你的Hibernate引擎(上)
- java 示例_功能Java示例 第5部分–将I / O移到外部
- php 登录安全认证,介绍几种常用的web安全认证方式
- 《2018区块链整体架构及应用》(PPT全文)
- json 来实现 php 与 javascript,用 Json 来实现 PHP 与 JavaScript 间数据交换
- 解决虚拟机克隆后eth0不见的问题
- ymodem传输的终端工具_Serial for Mac(现代化的终端设计软件)
- C++ STL unordered_map按照value排序
- 删除web项目服务器,删除 Tomcat-webapps 目录自带项目【测试可行】
- 【游戏开发实战】Unity UGUI序列帧动画(蓝胖子序列帧图)
- 2022.08.09-docker容器网络配置-左冕
- 微信小程序之各类文件下载保存到本地
- 单层感知器神经网络matlab,MATLAB神经网络——单层感知器
- react--1.react环境搭建、JSX语法、注释、样式、列表渲染、定义单个组件、eact Props、react State